Skip to contentSkip to conversation

Allow multiple TAP devices without --dev-node on Windows

PATCH In review Mathias Sundman 2 participants 3 messages Oct 5, 2004 – Oct 7, 2004

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Allow multiple TAP devices without --dev-node on Windows

I was going to ask for a feature yesterday, but though I'd write it myself instead of just asking for others todo things for me :-)

I have about 15 OpenVPN configs on my Windows machine, but I only use 2-3 simulatainously. But, as I need be able to connect to more than one site, I've had to create one TAP device for EVERY config file, as I had to specify the the dev-node in every config if I had installed more than one TAP device.

With the attached patch this behavior is modified so that if dev-node is not specified, OpenVPN tries to open the first TAP device is finds. If CreateFile() fails on this device, it moves on and tries to open next device.

If all CreateFile() fails on all avaiable TAP devices, first then openvpn terminates.

This works great as long as all TAP configuration is done dynamically from OpenVPN which is always the case for me. If you have static configuration done on the TAP devices, you need to use dev-node to specify which device to use anyway, so I don't think this patch should brake anything.

--
_____________________________________________________________

Mathias Sundman                  (^)   ASCII Ribbon Campaign
OpenVPN GUI for Windows           X    NO HTML/RTF in e-mail
http://www.nilings.se/openvpn    / \   NO Word docs in e-mail
tun.c +111 -39
diff -r -u openvpn-2.0_beta11/tun.c openvpn-2.0_beta11-new/tun.c
@@ -122,7 +122,10 @@
   if (dt == DEV_TYPE_TUN || dt == DEV_TYPE_TAP)
     {
       struct buffer out = alloc_buf_gc (256, gc);
-      get_device_guid (dev_node, BPTR (&out), buf_forward_capacity (&out), gc);
+      if (dev_node)
+        get_device_guid (dev_node, BPTR (&out), buf_forward_capacity (&out), gc);
+      else
+        get_unspecified_device_guid (0, BPTR (&out), buf_forward_capacity (&out), gc);
       return BSTR (&out);
     }
 #endif
@@ -2109,6 +2112,52 @@
 }
 
 /*
+ * Get a adapted GUID and optional actual_name from the 
+ * registry for the TAP device # = device_number.
+ */
+const char *
+get_unspecified_device_guid (const int device_number,
+		             char *actual_name,
+		             int actual_name_size,
+		             struct gc_arena *gc)
+{
+  const struct tap_reg *tr;
+  const struct panel_reg *pr;
+
+  const struct tap_reg *tap_reg = get_tap_reg (gc);
+  const struct panel_reg *panel_reg = get_panel_reg (gc);
+
+  struct buffer ret = alloc_buf_gc (256, gc);
+  struct buffer actual;
+  int i;
+
+  buf_set_write (&actual, actual_name, actual_name_size);
+
+  /* Make sure we have at least one TAP adapter */
+  if (!tap_reg)
+    {
+      msg (M_FATAL, "There are no TAP-Win32 adapters on this system.  You should be able to create a TAP-Win32 adapter by going to Start -> All Programs -> " PACKAGE_NAME " -> Add a new TAP-Win32 virtual ethernet adapter.");
+    }
+
+  /* Move on to specified device number */
+  for (i=0; i < device_number; i++)
+    {
+      if (!tap_reg->next)
+        msg (M_FATAL, "There is no available TAP device.");
+      tap_reg = tap_reg->next;
+    }
+  
+  const char *act;
+  act = guid_to_name (tap_reg->guid, panel_reg);
+  buf_printf (&ret, "%s", tap_reg->guid);
+  if (act)
+    buf_printf (&actual, "%s", act);
+  else
+    buf_printf (&actual, "NULL");
+  return BSTR (&ret);
+}
+
+/*
  * Lookup a --dev-node adapter name in the registry
  * returning the GUID and optional actual_name.
  */
@@ -2135,21 +2184,6 @@
       msg (M_FATAL, "There are no TAP-Win32 adapters on this system.  You should be able to create a TAP-Win32 adapter by going to Start -> All Programs -> " PACKAGE_NAME " -> Add a new TAP-Win32 virtual ethernet adapter.");
     }
 
-  /* If --dev-node not specified, look for a default TAP adapter */
-  if (!name)
-    {
-      const char *act;
-      if (tap_reg->next)
-	msg (M_FATAL, "You have more than one TAP-Win32 adapter on this system.  You must use the --dev-node option to tell me which one to use.  Use openvpn --show-adapters to see a list.");
-      act = guid_to_name (tap_reg->guid, panel_reg);
-      buf_printf (&ret, "%s", tap_reg->guid);
-      if (act)
-	buf_printf (&actual, "%s", act);
-      else
-	buf_printf (&actual, "NULL");
-      return BSTR (&ret);
-    }
-
   /* Check if GUID was explicitly specified as --dev-node parameter */
   if (is_tap_win32 (name, tap_reg))
     {
@@ -2773,33 +2807,71 @@
   {
     char guid_buffer[256];
 
+    if (dev_node)
+      {
+        /* Get the device GUID for the device specified with --dev-node. */
+        device_guid = get_device_guid (dev_node, guid_buffer, sizeof (guid_buffer), &gc);
+
+        /* Open Windows TAP-Win32 adapter */
+        openvpn_snprintf (device_path, sizeof(device_path), "%s%s%s",
+   		          USERMODEDEVICEDIR,
+		          device_guid,
+		          TAPSUFFIX);
+
+        tt->hand = CreateFile (
+			       device_path,
+			       GENERIC_READ | GENERIC_WRITE,
+			       0, /* was: FILE_SHARE_READ */
+			       0,
+			       OPEN_EXISTING,
+			       FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
+			       0
+			       );
+
+        if (tt->hand == INVALID_HANDLE_VALUE)
+          msg (M_ERR, "CreateFile failed on TAP device: %s", device_path);
+      }
+    else 
+      {
+        int device_number = 0;
+
+        /* Try opening all TAP devices until we find one available */
+        while(1)
+          {
+            device_guid = get_unspecified_device_guid (device_number, 
+						       guid_buffer, 
+						       sizeof (guid_buffer),
+						       &gc);
+
+            /* Open Windows TAP-Win32 adapter */
+            openvpn_snprintf (device_path, sizeof(device_path), "%s%s%s",
+       		  	      USERMODEDEVICEDIR,
+			      device_guid,
+			      TAPSUFFIX);
+
+            tt->hand = CreateFile (
+			 	   device_path,
+				   GENERIC_READ | GENERIC_WRITE,
+				   0, /* was: FILE_SHARE_READ */
+				   0,
+				   OPEN_EXISTING,
+				   FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
+				   0
+				   );
+
+            if (tt->hand == INVALID_HANDLE_VALUE)
+              msg (D_TUNTAP_INFO, "CreateFile failed on TAP device: %s", device_path);
+            else
+              break;
+        
+            device_number++;
+          }
+      }
+
     /* translate high-level device name into a device instance
        GUID using the registry */
-    device_guid = get_device_guid (dev_node, guid_buffer, sizeof (guid_buffer), &gc);
     tt->actual_name = string_alloc (guid_buffer, NULL);
   }
-
-  /*
-   * Open Windows TAP-Win32 adapter
-   */
-
-  openvpn_snprintf (device_path, sizeof(device_path), "%s%s%s",
-		    USERMODEDEVICEDIR,
-		    device_guid,
-		    TAPSUFFIX);
-
-  tt->hand = CreateFile (
-			 device_path,
-			 GENERIC_READ | GENERIC_WRITE,
-			 0, /* was: FILE_SHARE_READ */
-			 0,
-			 OPEN_EXISTING,
-			 FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
-			 0
-			 );
-
-  if (tt->hand == INVALID_HANDLE_VALUE)
-    msg (M_ERR, "CreateFile failed on TAP device: %s", device_path);
 
   msg (M_INFO, "TAP-WIN32 device [%s] opened: %s", tt->actual_name, device_path);
 
tun.h +5 -0
diff -r -u openvpn-2.0_beta11/tun.h openvpn-2.0_beta11-new/tun.h
@@ -286,6 +286,11 @@
 			     int actual_name_size,
 			     struct gc_arena *gc);
 
+const char *get_unspecified_device_guid (const int device_number,
+			                 char *actual_name,
+  			                 int actual_name_size,
+			                 struct gc_arena *gc);
+
 void verify_255_255_255_252 (in_addr_t local, in_addr_t remote);
 
 const IP_ADAPTER_INFO *get_adapter_info_list (struct gc_arena *gc);
#1 lore raw
Quoted 9 lines · Mathias Sundman

I was going to ask for a feature yesterday, but though I'd write it myself instead of just asking for others todo things for me :-)

I have about 15 OpenVPN configs on my Windows machine, but I only use 2-3 simulatainously. But, as I need be able to connect to more than one site, I've had to create one TAP device for EVERY config file, as I had to specify the the dev-node in every config if I had installed more than one TAP device.

With the attached patch this behavior is modified so that if dev-node is not specified, OpenVPN tries to open the first TAP device is finds. If CreateFile() fails on this device, it moves on and tries to open next device.

If all CreateFile() fails on all avaiable TAP devices, first then openvpn terminates.

This works great as long as all TAP configuration is done dynamically from OpenVPN which is always the case for me. If you have static configuration done on the TAP devices, you need to use dev-node to specify which device to use anyway, so I don't think this patch should brake anything.

Now that Eric Tanner has showed us how we can install the TAP device as hidden this patch became even more useful, as it's a little troublesome to change the TAP device name, when it's not visible in "Network Connections" :-)

James, could you consider applying this patch for the beta12 release?

If there's something with the patch you dislike or would like to have implemented in a diffrent way, let me know, and I'll try fixing it...

Signature · 4 lines
_________________________________________________________
Mathias Sundman              (^)   ASCII Ribbon Campaign
NILINGS AB                    X    NO HTML/RTF in e-mail
Tel: +46-(0)8-666 32 28      / \   NO Word docs in e-mail
#2 lore raw
Quoted 22 lines · Mathias Sundman
Quoted 12 lines · Mathias Sundman

I was going to ask for a feature yesterday, but though I'd write it myself instead of just asking for others todo things for me :-)

I have about 15 OpenVPN configs on my Windows machine, but I only use 2-3 simulatainously. But, as I need be able to connect to more than one site, I've had to create one TAP device for EVERY config file, as I had to specify the the dev-node in every config if I had installed more than one TAP device.

With the attached patch this behavior is modified so that if dev-node is not specified, OpenVPN tries to open the first TAP device is finds. If CreateFile() fails on this device, it moves on and tries to open next device.

Quoted 7 lines

If all CreateFile() fails on all avaiable TAP devices, first then openvpn terminates.

This works great as long as all TAP configuration is done dynamically from OpenVPN which is always the case for me. If you have static configuration done on the TAP devices, you need to use dev-node to specify which device to use anyway, so I don't think this patch should brake anything.

Quoted 4 lines

Now that Eric Tanner has showed us how we can install the TAP device as hidden this patch became even more useful, as it's a little troublesome to change the TAP device name, when it's not visible in "Network Connections" :-)

Yes, the NCF_HIDDEN flag in the INF file is rather cool, if not a bit subversive. I modified the TAP-Win32 code a while back to not depend on an adapter being listed in the Network Connections panel so that NCF_HIDDEN would work. If it's hidden, you can still open it by name, but you must use the GUID name. The Network Connections panel is basically a link from a friendly name to the GUID name.

Quoted 4 lines

James, could you consider applying this patch for the beta12 release?

If there's something with the patch you dislike or would like to have implemented in a diffrent way, let me know, and I'll try fixing it...

Yes, I do plan to merge it.

James