Pārlūkot izejas kodu

BTap: support TUN on Windows

ambrop7 15 gadi atpakaļ
vecāks
revīzija
9df1fc62d1
4 mainītis faili ar 302 papildinājumiem un 130 dzēšanām
  1. 47 129
      tuntap/BTap.c
  2. 9 1
      tuntap/CMakeLists.txt
  3. 211 0
      tuntap/tapwin32-funcs.c
  4. 35 0
      tuntap/tapwin32-funcs.h

+ 47 - 129
tuntap/BTap.c

@@ -29,6 +29,7 @@
 #include <objbase.h>
 #include <wtypes.h>
 #include "wintap-common.h"
+#include <tuntap/tapwin32-funcs.h>
 #else
 #include <linux/if_tun.h>
 #include <net/if.h>
@@ -44,8 +45,6 @@
 
 #include <tuntap/BTap.h>
 
-#define REGNAME_SIZE 256
-
 static void report_error (BTap *o);
 static void input_handler_send (BTap *o, uint8_t *data, int data_len);
 static void input_handler_cancel (BTap *o);
@@ -439,158 +438,77 @@ int BTap_Init (BTap *o, BReactor *reactor, char *devname, BTap_handler_error han
     
     #ifdef BADVPN_USE_WINAPI
     
-    if (tun) {
-        DEBUG("TUN not supported on Windows");
-        goto fail0;
-    }
-    
-    char *device_component_id;
-    char *device_name;
-    
-    char strdata[(devname ? strlen(devname) + 1 : 0)];
+    // parse device specification
     
     if (!devname) {
-        device_component_id = TAP_COMPONENT_ID;
-        device_name = NULL;
-    } else {
-        strcpy(strdata, devname);
-        char *colon = strstr(strdata, ":");
-        if (!colon) {
-            DEBUG("No colon in device string");
-            goto fail0;
-        }
-        *colon = '\0';
-        device_component_id = strdata;
-        device_name = colon + 1;
-        if (strlen(device_component_id) == 0) {
-            device_component_id = TAP_COMPONENT_ID;
-        }
-        if (strlen(device_name) == 0) {
-            device_name = NULL;
-        }
+        DEBUG("no device specification provided");
+        return 0;
     }
     
-    DEBUG("Opening component ID %s, name %s", device_component_id, device_name);
+    int devname_len = strlen(devname);
     
-    // open adapter key
-    // used to find all devices with the given ComponentId
-    HKEY adapter_key;
-    if (RegOpenKeyEx(
-            HKEY_LOCAL_MACHINE,
-            ADAPTER_KEY,
-            0,
-            KEY_READ,
-            &adapter_key
-    ) != ERROR_SUCCESS) {
-        DEBUG("Error opening adapter key");
-        goto fail0;
-    }
-    
-    char net_cfg_instance_id[REGNAME_SIZE];
-    int found = 0;
+    char device_component_id[devname_len + 1];
+    char device_name[devname_len + 1];
+    uint32_t tun_addrs[3];
     
-    DWORD i;
-    for (i = 0;; i++) {
-        DWORD len;
-        DWORD type;
-        
-        char key_name[REGNAME_SIZE];
-        len = sizeof(key_name);
-        if (RegEnumKeyEx(adapter_key, i, key_name, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
-            break;
-        }
-        
-        char unit_string[REGNAME_SIZE];
-        snprintf(unit_string, sizeof(unit_string), "%s\\%s", ADAPTER_KEY, key_name);
-        HKEY unit_key;
-        if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, unit_string, 0, KEY_READ, &unit_key) != ERROR_SUCCESS) {
-            continue;
-        }
-        
-        char component_id[REGNAME_SIZE];
-        len = sizeof(component_id);
-        if (RegQueryValueEx(unit_key, "ComponentId", NULL, &type, component_id, &len) != ERROR_SUCCESS || type != REG_SZ) {
-            ASSERT_FORCE(RegCloseKey(unit_key) == ERROR_SUCCESS)
-            continue;
-        }
-        
-        len = sizeof(net_cfg_instance_id);
-        if (RegQueryValueEx(unit_key, "NetCfgInstanceId", NULL, &type, net_cfg_instance_id, &len) != ERROR_SUCCESS || type != REG_SZ) {
-            ASSERT_FORCE(RegCloseKey(unit_key) == ERROR_SUCCESS)
-            continue;
+    if (tun) {
+        if (!tapwin32_parse_tun_spec(devname, device_component_id, device_name, tun_addrs)) {
+            DEBUG("failed to parse TUN device specification");
+            return 0;
         }
-        
-        RegCloseKey(unit_key);
-        
-        // check if ComponentId matches
-        if (!strcmp(component_id, device_component_id)) {
-            // if no name was given, use the first device with the given ComponentId
-            if (!device_name) {
-                found = 1;
-                break;
-            }
-            
-            // open connection key
-            char conn_string[REGNAME_SIZE];
-            snprintf(conn_string, sizeof(conn_string), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, net_cfg_instance_id);
-            HKEY conn_key;
-            if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, conn_string, 0, KEY_READ, &conn_key) != ERROR_SUCCESS) {
-                continue;
-            }
-            
-            // read name
-            char name[REGNAME_SIZE];
-            len = sizeof(name);
-            if (RegQueryValueEx(conn_key, "Name", NULL, &type, name, &len) != ERROR_SUCCESS || type != REG_SZ) {
-                ASSERT_FORCE(RegCloseKey(conn_key) == ERROR_SUCCESS)
-                continue;
-            }
-            
-            ASSERT_FORCE(RegCloseKey(conn_key) == ERROR_SUCCESS)
-            
-            // check name
-            if (!strcmp(name, device_name)) {
-                found = 1;
-                break;
-            }
+    } else {
+        if (!tapwin32_parse_tap_spec(devname, device_component_id, device_name)) {
+            DEBUG("failed to parse TAP device specification");
+            return 0;
         }
     }
     
-    ASSERT_FORCE(RegCloseKey(adapter_key) == ERROR_SUCCESS)
+    // locate device path
+    
+    char device_path[TAPWIN32_MAX_REG_SIZE];
     
-    if (!found) {
-        DEBUG("Could not find TAP device");
+    DEBUG("Looking for TAP-Win32 with component ID %s, name %s", device_component_id, device_name);
+    
+    if (!tapwin32_find_device(device_component_id, device_name, &device_path)) {
+        DEBUG("Could not find device");
         goto fail0;
     }
     
-    char device_path[REGNAME_SIZE];
-    snprintf(device_path, sizeof(device_path), "%s%s%s", USERMODEDEVICEDIR, net_cfg_instance_id, TAPSUFFIX);
+    // open device
     
     DEBUG("Opening device %s", device_path);
     
-    if ((o->device = CreateFile(
-        device_path,
-        GENERIC_READ | GENERIC_WRITE,
-        0,
-        0,
-        OPEN_EXISTING,
-        FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
-        0
-    )) == INVALID_HANDLE_VALUE) {
+    o->device = CreateFile(device_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM|FILE_FLAG_OVERLAPPED, 0);
+    if (o->device == INVALID_HANDLE_VALUE) {
         DEBUG("CreateFile failed");
         goto fail0;
     }
     
-    // get MTU
+    // set TUN if needed
     
-    ULONG umtu;
     DWORD len;
-    if (!DeviceIoControl(o->device, TAP_IOCTL_GET_MTU, NULL, 0, &umtu, sizeof(umtu), &len, NULL)) {
-        DEBUG("DeviceIoControl(TAP_IOCTL_GET_MTU) failed");
-        goto fail1;
+    
+    if (tun) {
+        if (!DeviceIoControl(o->device, TAP_IOCTL_CONFIG_TUN, tun_addrs, sizeof(tun_addrs), tun_addrs, sizeof(tun_addrs), &len, NULL)) {
+            DEBUG("DeviceIoControl(TAP_IOCTL_CONFIG_TUN) failed");
+            goto fail1;
+        }
     }
     
-    o->frame_mtu = umtu + BTAP_ETHERNET_HEADER_LENGTH;
+    // get MTU
+    
+    if (tun) {
+        o->frame_mtu = 65535;
+    } else {
+        ULONG umtu;
+        
+        if (!DeviceIoControl(o->device, TAP_IOCTL_GET_MTU, NULL, 0, &umtu, sizeof(umtu), &len, NULL)) {
+            DEBUG("DeviceIoControl(TAP_IOCTL_GET_MTU) failed");
+            goto fail1;
+        }
+        
+        o->frame_mtu = umtu + BTAP_ETHERNET_HEADER_LENGTH;
+    }
     
     // set connected
     

+ 9 - 1
tuntap/CMakeLists.txt

@@ -1,2 +1,10 @@
-add_library(tuntap BTap.c)
+set(TUNTAP_ADDITIONAL_SOURCES)
+if (WIN32)
+    list(APPEND TUNTAP_ADDITIONAL_SOURCES tapwin32-funcs.c)
+endif ()
+
+add_library(tuntap
+    BTap.c
+    ${TUNTAP_ADDITIONAL_SOURCES}
+)
 target_link_libraries(tuntap system flow)

+ 211 - 0
tuntap/tapwin32-funcs.c

@@ -0,0 +1,211 @@
+/**
+ * @file tapwin32-funcs.c
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * This file is part of BadVPN.
+ * 
+ * BadVPN is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ * 
+ * BadVPN is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <misc/debug.h>
+
+#include "wintap-common.h"
+
+#include <tuntap/tapwin32-funcs.h>
+
+static int split_spec (char *name, char *sep, char *out_fields[], int num_fields)
+{
+    ASSERT(num_fields > 0)
+    ASSERT(strlen(sep) > 0)
+    
+    size_t seplen = strlen(sep);
+    
+    int i;
+    for (i = 0; i < num_fields - 1; i++) {
+        char *s = strstr(name, sep);
+        if (!s) {
+            DEBUG("missing separator number %d", (i + 1));
+            return 0;
+        }
+        
+        int flen = s - name;
+        memcpy(out_fields[i], name, flen);
+        out_fields[i][flen] = '\0';
+        
+        name = s + seplen;
+    }
+    
+    int flen = strlen(name);
+    memcpy(out_fields[i], name, flen);
+    out_fields[i][flen] = '\0';
+    
+    return 1;
+}
+
+static int parse_ipv4_addr (char *name, uint8_t out_addr[4])
+{
+    if (strlen(name) > 15) {
+        return 0;
+    }
+    
+    char (nums[4])[16];
+    
+    char *out_fields[] = { nums[0], nums[1], nums[2], nums[3] };
+    
+    if (!split_spec(name, ".", out_fields, 4)) {
+        return 0;
+    }
+    
+    for (int i = 0; i < 4; i++) {
+        if (strlen(nums[i]) > 3) {
+            return 0;
+        }
+        
+        int num = atoi(nums[i]);
+        
+        if (!(num >= 0 && num < 256)) {
+            return 0;
+        }
+        
+        out_addr[i] = num;
+    }
+    
+    return 1;
+}
+
+int tapwin32_parse_tap_spec (char *name, char *out_component_id, char *out_human_name)
+{
+    char *out_fields[] = { out_component_id, out_human_name };
+    
+    return split_spec(name, ":", out_fields, 2);
+}
+
+int tapwin32_parse_tun_spec (char *name, char *out_component_id, char *out_human_name, uint32_t out_addrs[3])
+{
+    int namelen = strlen(name);
+    
+    char (addr_strs[3])[namelen + 1];
+    
+    char *out_fields[] = { out_component_id, out_human_name, addr_strs[0], addr_strs[1], addr_strs[2] };
+    
+    if (!split_spec(name, ":", out_fields, 5)) {
+        return 0;
+    }
+    
+    for (int i = 0; i < 3; i++) {
+        if (!parse_ipv4_addr(addr_strs[i], (uint8_t *)(out_addrs + i))) {
+            return 0;
+        }
+    }
+    
+    return 1;
+}
+
+int tapwin32_find_device (char *device_component_id, char *device_name, char (*device_path)[TAPWIN32_MAX_REG_SIZE])
+{
+    // open adapter key
+    // used to find all devices with the given ComponentId
+    HKEY adapter_key;
+    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, ADAPTER_KEY, 0, KEY_READ, &adapter_key) != ERROR_SUCCESS) {
+        DEBUG("Error opening adapter key");
+        return 0;
+    }
+    
+    char net_cfg_instance_id[TAPWIN32_MAX_REG_SIZE];
+    int found = 0;
+    
+    DWORD i;
+    for (i = 0;; i++) {
+        DWORD len;
+        DWORD type;
+        
+        char key_name[TAPWIN32_MAX_REG_SIZE];
+        len = sizeof(key_name);
+        if (RegEnumKeyEx(adapter_key, i, key_name, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
+            break;
+        }
+        
+        char unit_string[TAPWIN32_MAX_REG_SIZE];
+        snprintf(unit_string, sizeof(unit_string), "%s\\%s", ADAPTER_KEY, key_name);
+        HKEY unit_key;
+        if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, unit_string, 0, KEY_READ, &unit_key) != ERROR_SUCCESS) {
+            continue;
+        }
+        
+        char component_id[TAPWIN32_MAX_REG_SIZE];
+        len = sizeof(component_id);
+        if (RegQueryValueEx(unit_key, "ComponentId", NULL, &type, component_id, &len) != ERROR_SUCCESS || type != REG_SZ) {
+            ASSERT_FORCE(RegCloseKey(unit_key) == ERROR_SUCCESS)
+            continue;
+        }
+        
+        len = sizeof(net_cfg_instance_id);
+        if (RegQueryValueEx(unit_key, "NetCfgInstanceId", NULL, &type, net_cfg_instance_id, &len) != ERROR_SUCCESS || type != REG_SZ) {
+            ASSERT_FORCE(RegCloseKey(unit_key) == ERROR_SUCCESS)
+            continue;
+        }
+        
+        RegCloseKey(unit_key);
+        
+        // check if ComponentId matches
+        if (!strcmp(component_id, device_component_id)) {
+            // if no name was given, use the first device with the given ComponentId
+            if (!device_name) {
+                found = 1;
+                break;
+            }
+            
+            // open connection key
+            char conn_string[TAPWIN32_MAX_REG_SIZE];
+            snprintf(conn_string, sizeof(conn_string), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, net_cfg_instance_id);
+            HKEY conn_key;
+            if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, conn_string, 0, KEY_READ, &conn_key) != ERROR_SUCCESS) {
+                continue;
+            }
+            
+            // read name
+            char name[TAPWIN32_MAX_REG_SIZE];
+            len = sizeof(name);
+            if (RegQueryValueEx(conn_key, "Name", NULL, &type, name, &len) != ERROR_SUCCESS || type != REG_SZ) {
+                ASSERT_FORCE(RegCloseKey(conn_key) == ERROR_SUCCESS)
+                continue;
+            }
+            
+            ASSERT_FORCE(RegCloseKey(conn_key) == ERROR_SUCCESS)
+            
+            // check name
+            if (!strcmp(name, device_name)) {
+                found = 1;
+                break;
+            }
+        }
+    }
+    
+    ASSERT_FORCE(RegCloseKey(adapter_key) == ERROR_SUCCESS)
+    
+    if (!found) {
+        return 0;
+    }
+    
+    snprintf(*device_path, sizeof(*device_path), "%s%s%s", USERMODEDEVICEDIR, net_cfg_instance_id, TAPSUFFIX);
+    
+    return 1;
+}

+ 35 - 0
tuntap/tapwin32-funcs.h

@@ -0,0 +1,35 @@
+/**
+ * @file tapwin32-funcs.h
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * This file is part of BadVPN.
+ * 
+ * BadVPN is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ * 
+ * BadVPN is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef BADVPN_TUNTAP_TAPWIN32_FUNCS_H
+#define BADVPN_TUNTAP_TAPWIN32_FUNCS_H
+
+#include <stdint.h>
+#include <windows.h>
+
+#define TAPWIN32_MAX_REG_SIZE 256
+
+int tapwin32_parse_tap_spec (char *name, char *out_component_id, char *out_human_name);
+int tapwin32_parse_tun_spec (char *name, char *out_component_id, char *out_human_name, uint32_t out_addrs[3]);
+int tapwin32_find_device (char *device_component_id, char *device_name, char (*device_path)[TAPWIN32_MAX_REG_SIZE]);
+
+#endif