Browse code

Enable dhcp on tap adapter using interactive service

Currently, if dhcp on the TAP interface is disabled, OpenVPN
on Windows tries to enable it using netsh but that succeeds only when
run with admin privileges.

When interactive service is available, delegate this task to the
service.

Trac: #1111
Tested on Windows 7

Signed-off-by: Selva Nair <selva.nair@gmail.com>
Acked-by: Lev Stipakov <lstipakov@gmail.com>
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <1538510474-27602-2-git-send-email-selva.nair@gmail.com>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg17517.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>

Selva Nair authored on 2018/10/03 05:01:13
Showing 3 changed files
... ...
@@ -37,7 +37,8 @@ typedef enum {
37 37
     msg_flush_neighbors,
38 38
     msg_add_block_dns,
39 39
     msg_del_block_dns,
40
-    msg_register_dns
40
+    msg_register_dns,
41
+    msg_enable_dhcp,
41 42
 } message_type_t;
42 43
 
43 44
 typedef struct {
... ...
@@ -111,4 +112,9 @@ typedef struct {
111 111
     interface_t iface;
112 112
 } block_dns_message_t;
113 113
 
114
+typedef struct {
115
+    message_header_t header;
116
+    interface_t iface;
117
+} enable_dhcp_message_t;
118
+
114 119
 #endif /* ifndef OPENVPN_MSG_H_ */
... ...
@@ -5203,6 +5203,49 @@ netsh_enable_dhcp(const struct tuntap_options *to,
5203 5203
     argv_reset(&argv);
5204 5204
 }
5205 5205
 
5206
+/* Enable dhcp on tap adapter using iservice */
5207
+static bool
5208
+service_enable_dhcp(const struct tuntap *tt)
5209
+{
5210
+    DWORD len;
5211
+    bool ret = false;
5212
+    ack_message_t ack;
5213
+    struct gc_arena gc = gc_new();
5214
+    HANDLE pipe = tt->options.msg_channel;
5215
+
5216
+    enable_dhcp_message_t dhcp = {
5217
+        .header = {
5218
+            msg_enable_dhcp,
5219
+            sizeof(enable_dhcp_message_t),
5220
+            0
5221
+        },
5222
+        .iface = { .index = tt->adapter_index, .name = "" }
5223
+    };
5224
+
5225
+    if (!WriteFile(pipe, &dhcp, sizeof(dhcp), &len, NULL)
5226
+        || !ReadFile(pipe, &ack, sizeof(ack), &len, NULL))
5227
+    {
5228
+        msg(M_WARN, "Enable_dhcp: could not talk to service: %s [%lu]",
5229
+            strerror_win32(GetLastError(), &gc), GetLastError());
5230
+        goto out;
5231
+    }
5232
+
5233
+    if (ack.error_number != NO_ERROR)
5234
+    {
5235
+        msg(M_NONFATAL, "TUN: enabling dhcp using service failed: %s [status=%u if_index=%d]",
5236
+            strerror_win32(ack.error_number, &gc), ack.error_number, dhcp.iface.index);
5237
+    }
5238
+    else
5239
+    {
5240
+        msg(M_INFO, "DHCP enabled on interface %d using service", dhcp.iface.index);
5241
+        ret = true;
5242
+    }
5243
+
5244
+out:
5245
+    gc_free(&gc);
5246
+    return ret;
5247
+}
5248
+
5206 5249
 /*
5207 5250
  * Return a TAP name for netsh commands.
5208 5251
  */
... ...
@@ -5683,7 +5726,15 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
5683 5683
              */
5684 5684
             if (dhcp_status(tt->adapter_index) == DHCP_STATUS_DISABLED)
5685 5685
             {
5686
-                netsh_enable_dhcp(&tt->options, tt->actual_name);
5686
+                /* try using the service if available, else directly execute netsh */
5687
+                if (tt->options.msg_channel)
5688
+                {
5689
+                    service_enable_dhcp(tt);
5690
+                }
5691
+                else
5692
+                {
5693
+                    netsh_enable_dhcp(&tt->options, tt->actual_name);
5694
+                }
5687 5695
             }
5688 5696
             dhcp_masq = true;
5689 5697
             dhcp_masq_post = true;
... ...
@@ -1164,6 +1164,45 @@ out:
1164 1164
     return err;
1165 1165
 }
1166 1166
 
1167
+static DWORD
1168
+HandleEnableDHCPMessage(const enable_dhcp_message_t *dhcp)
1169
+{
1170
+    DWORD err = 0;
1171
+    DWORD timeout = 5000; /* in milli seconds */
1172
+    wchar_t argv0[MAX_PATH];
1173
+
1174
+    /* Path of netsh */
1175
+    swprintf(argv0, _countof(argv0), L"%s\\%s", get_win_sys_path(), L"netsh.exe");
1176
+    argv0[_countof(argv0) - 1] = L'\0';
1177
+
1178
+    /* cmd template:
1179
+     * netsh interface ipv4 set address name=$if_index source=dhcp
1180
+     */
1181
+    const wchar_t *fmt = L"netsh interface ipv4 set address name=\"%d\" source=dhcp";
1182
+
1183
+    /* max cmdline length in wchars -- include room for if index:
1184
+     * 10 chars for 32 bit int in decimal and +1 for NUL
1185
+     */
1186
+    size_t ncmdline = wcslen(fmt) + 10 + 1;
1187
+    wchar_t *cmdline = malloc(ncmdline*sizeof(wchar_t));
1188
+    if (!cmdline)
1189
+    {
1190
+        err = ERROR_OUTOFMEMORY;
1191
+        return err;
1192
+    }
1193
+
1194
+    openvpn_sntprintf(cmdline, ncmdline, fmt, dhcp->iface.index);
1195
+
1196
+    err = ExecCommand(argv0, cmdline, timeout);
1197
+
1198
+    /* Note: This could fail if dhcp is already enabled, so the caller
1199
+     * may not want to treat errors as FATAL.
1200
+     */
1201
+
1202
+    free(cmdline);
1203
+    return err;
1204
+}
1205
+
1167 1206
 static VOID
1168 1207
 HandleMessage(HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_lists_t *lists)
1169 1208
 {
... ...
@@ -1175,6 +1214,7 @@ HandleMessage(HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_lists
1175 1175
         flush_neighbors_message_t flush_neighbors;
1176 1176
         block_dns_message_t block_dns;
1177 1177
         dns_cfg_message_t dns;
1178
+        enable_dhcp_message_t dhcp;
1178 1179
     } msg;
1179 1180
     ack_message_t ack = {
1180 1181
         .header = {
... ...
@@ -1235,6 +1275,13 @@ HandleMessage(HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_lists
1235 1235
             ack.error_number = HandleDNSConfigMessage(&msg.dns, lists);
1236 1236
             break;
1237 1237
 
1238
+        case msg_enable_dhcp:
1239
+            if (msg.header.size == sizeof(msg.dhcp))
1240
+            {
1241
+                ack.error_number = HandleEnableDHCPMessage(&msg.dhcp);
1242
+            }
1243
+            break;
1244
+
1238 1245
         default:
1239 1246
             ack.error_number = ERROR_MESSAGE_TYPE;
1240 1247
             MsgToEventLog(MSG_FLAGS_ERROR, TEXT("Unknown message type %d"), msg.header.type);