Skip to content

Commit 89b1d0a

Browse files
committed
ipc: cache windows lookups to avoid O(n^2) with nested lookups
Signed-off-by: Jason A. Donenfeld <[email protected]>
1 parent c52a006 commit 89b1d0a

File tree

3 files changed

+136
-4
lines changed

3 files changed

+136
-4
lines changed

src/ipc-uapi-windows.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <stdio.h>
1111
#include <stdbool.h>
1212
#include <fcntl.h>
13+
#include <hashtable.h>
1314

1415
static FILE *userspace_interface_file(const char *iface)
1516
{
@@ -113,17 +114,23 @@ static FILE *userspace_interface_file(const char *iface)
113114
return NULL;
114115
}
115116

117+
static bool have_cached_interfaces;
118+
static struct hashtable cached_interfaces;
119+
116120
static bool userspace_has_wireguard_interface(const char *iface)
117121
{
118122
char fname[MAX_PATH];
119123
WIN32_FIND_DATA find_data;
120124
HANDLE find_handle;
121125
bool ret = false;
122126

127+
if (have_cached_interfaces)
128+
return hashtable_find_entry(&cached_interfaces, iface) != NULL;
129+
123130
snprintf(fname, sizeof(fname), "ProtectedPrefix\\Administrators\\WireGuard\\%s", iface);
124131
find_handle = FindFirstFile("\\\\.\\pipe\\*", &find_data);
125132
if (find_handle == INVALID_HANDLE_VALUE)
126-
return -GetLastError();
133+
return -EIO;
127134
do {
128135
if (!strcmp(fname, find_data.cFileName)) {
129136
ret = true;
@@ -139,18 +146,25 @@ static int userspace_get_wireguard_interfaces(struct string_list *list)
139146
static const char prefix[] = "ProtectedPrefix\\Administrators\\WireGuard\\";
140147
WIN32_FIND_DATA find_data;
141148
HANDLE find_handle;
149+
char *iface;
142150
int ret = 0;
143151

144152
find_handle = FindFirstFile("\\\\.\\pipe\\*", &find_data);
145153
if (find_handle == INVALID_HANDLE_VALUE)
146-
return -GetLastError();
154+
return -EIO;
147155
do {
148156
if (strncmp(prefix, find_data.cFileName, strlen(prefix)))
149157
continue;
150-
ret = string_list_add(list, find_data.cFileName + strlen(prefix));
158+
iface = find_data.cFileName + strlen(prefix);
159+
ret = string_list_add(list, iface);
151160
if (ret < 0)
152161
goto out;
162+
if (!hashtable_find_or_insert_entry(&cached_interfaces, iface)) {
163+
ret = -errno;
164+
goto out;
165+
}
153166
} while (FindNextFile(find_handle, &find_data));
167+
have_cached_interfaces = true;
154168

155169
out:
156170
FindClose(find_handle);

src/ipc-windows.h

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,17 @@
1313
#include <ddk/ndisguid.h>
1414
#include <nci.h>
1515
#include <wireguard.h>
16+
#include <hashtable.h>
1617

1718
#define IPC_SUPPORTS_KERNEL_INTERFACE
1819

20+
static bool have_cached_kernel_interfaces;
21+
static struct hashtable cached_kernel_interfaces;
22+
1923
static int kernel_get_wireguard_interfaces(struct string_list *list)
2024
{
2125
HDEVINFO dev_info = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL);
26+
bool will_have_cached_kernel_interfaces = true;
2227

2328
if (dev_info == INVALID_HANDLE_VALUE) {
2429
errno = EACCES;
@@ -33,6 +38,7 @@ static int kernel_get_wireguard_interfaces(struct string_list *list)
3338
HKEY key;
3439
GUID instance_id;
3540
char *interface_name;
41+
struct hashtable_entry *entry;
3642

3743
if (!SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data)) {
3844
if (GetLastError() == ERROR_NO_MORE_ITEMS)
@@ -105,23 +111,74 @@ static int kernel_get_wireguard_interfaces(struct string_list *list)
105111
}
106112

107113
string_list_add(list, interface_name);
114+
115+
entry = hashtable_find_or_insert_entry(&cached_kernel_interfaces, interface_name);
108116
free(interface_name);
117+
if (!entry)
118+
goto cleanup_entry;
119+
120+
if (SetupDiGetDeviceInstanceIdW(dev_info, &dev_info_data, NULL, 0, &buf_len) || GetLastError() != ERROR_INSUFFICIENT_BUFFER)
121+
goto cleanup_entry;
122+
entry->value = calloc(sizeof(WCHAR), buf_len);
123+
if (!entry->value)
124+
goto cleanup_entry;
125+
if (!SetupDiGetDeviceInstanceIdW(dev_info, &dev_info_data, entry->value, buf_len, &buf_len)) {
126+
free(entry->value);
127+
entry->value = NULL;
128+
goto cleanup_entry;
129+
}
130+
131+
cleanup_entry:
132+
will_have_cached_kernel_interfaces |= entry != NULL && entry->value != NULL;
109133
cleanup_buf:
110134
free(buf);
111135
cleanup_key:
112136
RegCloseKey(key);
113137
skip:;
114138
}
115139
SetupDiDestroyDeviceInfoList(dev_info);
140+
have_cached_kernel_interfaces = will_have_cached_kernel_interfaces;
116141
return 0;
117142
}
118143

119144
static HANDLE kernel_interface_handle(const char *iface)
120145
{
121-
HDEVINFO dev_info = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL);
146+
HDEVINFO dev_info;
122147
WCHAR *interfaces = NULL;
123148
HANDLE handle;
124149

150+
if (have_cached_kernel_interfaces) {
151+
struct hashtable_entry *entry = hashtable_find_entry(&cached_kernel_interfaces, iface);
152+
if (entry) {
153+
DWORD buf_len;
154+
if (CM_Get_Device_Interface_List_SizeW(
155+
&buf_len, (GUID *)&GUID_DEVINTERFACE_NET, (DEVINSTID_W)entry->value,
156+
CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS)
157+
goto err_hash;
158+
interfaces = calloc(buf_len, sizeof(*interfaces));
159+
if (!interfaces)
160+
goto err_hash;
161+
if (CM_Get_Device_Interface_ListW(
162+
(GUID *)&GUID_DEVINTERFACE_NET, (DEVINSTID_W)entry->value, interfaces, buf_len,
163+
CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS || !interfaces[0]) {
164+
free(interfaces);
165+
interfaces = NULL;
166+
goto err_hash;
167+
}
168+
handle = CreateFileW(interfaces, GENERIC_READ | GENERIC_WRITE,
169+
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
170+
OPEN_EXISTING, 0, NULL);
171+
free(interfaces);
172+
if (handle == INVALID_HANDLE_VALUE)
173+
goto err_hash;
174+
return handle;
175+
err_hash:
176+
errno = EACCES;
177+
return NULL;
178+
}
179+
}
180+
181+
dev_info = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL);
125182
if (dev_info == INVALID_HANDLE_VALUE)
126183
return NULL;
127184

src/wincompat/include/hashtable.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/* SPDX-License-Identifier: GPL-2.0
2+
*
3+
* Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
4+
*/
5+
6+
#ifndef _HASHTABLE_H
7+
#define _HASHTABLE_H
8+
9+
#include <string.h>
10+
11+
enum { HASHTABLE_ENTRY_BUCKETS_POW2 = 1 << 10 };
12+
13+
struct hashtable_entry {
14+
char *key;
15+
void *value;
16+
struct hashtable_entry *next;
17+
};
18+
19+
struct hashtable {
20+
struct hashtable_entry *entry_buckets[HASHTABLE_ENTRY_BUCKETS_POW2];
21+
};
22+
23+
static unsigned int hashtable_bucket(const char *str)
24+
{
25+
unsigned long hash = 5381;
26+
char c;
27+
while ((c = *str++))
28+
hash = ((hash << 5) + hash) ^ c;
29+
return hash & (HASHTABLE_ENTRY_BUCKETS_POW2 - 1);
30+
}
31+
32+
static struct hashtable_entry *hashtable_find_entry(struct hashtable *hashtable, const char *key)
33+
{
34+
struct hashtable_entry *entry;
35+
for (entry = hashtable->entry_buckets[hashtable_bucket(key)]; entry; entry = entry->next) {
36+
if (!strcmp(entry->key, key))
37+
return entry;
38+
}
39+
return NULL;
40+
}
41+
42+
static struct hashtable_entry *hashtable_find_or_insert_entry(struct hashtable *hashtable, const char *key)
43+
{
44+
struct hashtable_entry **entry;
45+
for (entry = &hashtable->entry_buckets[hashtable_bucket(key)]; *entry; entry = &(*entry)->next) {
46+
if (!strcmp((*entry)->key, key))
47+
return *entry;
48+
}
49+
*entry = calloc(1, sizeof(**entry));
50+
if (!*entry)
51+
return NULL;
52+
(*entry)->key = strdup(key);
53+
if (!(*entry)->key) {
54+
free(*entry);
55+
*entry = NULL;
56+
return NULL;
57+
}
58+
return *entry;
59+
}
60+
61+
#endif

0 commit comments

Comments
 (0)