suricata
win32-syscall.c
Go to the documentation of this file.
1/* Copyright (C) 2018 Open Information Security Foundation
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18/**
19 * \file
20 *
21 * \author Jacob Masen-Smith <jacob@evengx.com>
22 *
23 * Isolation for WMI/COM functionality
24 *
25 * References:
26 * https://msdn.microsoft.com/en-us/library/aa390421(v=vs.85).aspx
27 * https://blogs.msdn.microsoft.com/ndis/2015/03/21/mapping-from-ndis-oids-to-wmi-classes/
28 * https://stackoverflow.com/questions/1431103/how-to-obtain-data-from-wmi-using-a-c-application
29 * https://docs.microsoft.com/en-us/windows-hardware/drivers/network/oid-tcp-offload-parameters
30 * https://wutils.com/wmi/root/wmi/ms_409/msndis_tcpoffloadcurrentconfig/
31 * https://docs.microsoft.com/en-us/windows-hardware/drivers/network/oid-tcp-offload-current-config
32 * https://wutils.com/wmi/root/wmi/msndis_tcpoffloadparameters/
33 */
34
35#ifdef OS_WIN32
36
37#include <inttypes.h>
38#include <stdbool.h>
39
40// clang-format off
41#include <winsock2.h>
42#include <windows.h>
43#include <wbemidl.h>
44#include <strsafe.h>
45#include <ntddndis.h>
46#include <ws2ipdef.h>
47#include <iphlpapi.h>
48// clang-format on
49
50/* Windows strsafe.h defines _snprintf as an undefined warning type */
51#undef _snprintf
52#define _snprintf StringCbPrintfA
53
54#include "suricata-common.h"
55#include "util-debug.h"
56#include "util-device-private.h"
57#include "util-mem.h"
58#include "util-unittest.h"
59
60#include "suricata.h"
61
62#include "win32-syscall.h"
63
64/**
65 * \brief return only the GUID portion of the name
66 */
67static const char *StripPcapPrefix(const char *pcap_dev)
68{
69 return strchr(pcap_dev, '{');
70}
71
72/**
73 * \brief get the adapter address list, which includes IP status/details
74 *
75 * Clients MUST FREE the returned list to avoid memory leaks.
76 */
77uint32_t Win32GetAdaptersAddresses(IP_ADAPTER_ADDRESSES **pif_info_list)
78{
79 DWORD err = NO_ERROR;
80 IP_ADAPTER_ADDRESSES *if_info_list;
81
82 ULONG size = 0;
83 err = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &size);
84 if (err != ERROR_BUFFER_OVERFLOW) {
85 return err;
86 }
87 if_info_list = SCMalloc((size_t)size);
88 if (if_info_list == NULL) {
89 return ERROR_NOT_ENOUGH_MEMORY;
90 }
91 err = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, if_info_list, &size);
92 if (err != NO_ERROR) {
93 SCFree(if_info_list);
94 return err;
95 }
96
97 *pif_info_list = if_info_list;
98 return NO_ERROR;
99}
100
101uint32_t Win32FindAdapterAddresses(IP_ADAPTER_ADDRESSES *if_info_list,
102 const char *adapter_name,
103 IP_ADAPTER_ADDRESSES **pif_info)
104{
105 DWORD ret = NO_ERROR;
106 adapter_name = StripPcapPrefix(adapter_name);
107 *pif_info = NULL;
108
109 for (IP_ADAPTER_ADDRESSES *current = if_info_list; current != NULL;
110 current = current->Next) {
111
112 /* if we find the adapter, return that data */
113 if (strncmp(adapter_name, current->AdapterName, strlen(adapter_name)) ==
114 0) {
115
116 *pif_info = current;
117 break;
118 }
119 }
120
121 if (*pif_info == NULL) {
122 ret = ERROR_NOT_FOUND;
123 }
124
125 return ret;
126}
127
128#if NTDDI_VERSION < NTDDI_VISTA
129
130int GetIfaceMTUWin32(const char *pcap_dev) { return 0; }
131int GetGlobalMTUWin32(void) { return 0; }
132
133int GetIfaceOffloadingWin32(const char *ifname, int csum, int other)
134{
135 SCLogWarning("Suricata not targeted for Windows Vista or "
136 "higher. Network offload interrogation not "
137 "available.");
138 return -1;
139}
140int DisableIfaceOffloadingWin32(LiveDevice *ldev, int csum, int other)
141{
142 SCLogWarning("Suricata not targeted for Windows Vista or "
143 "higher. Network offload interrogation not "
144 "available.");
145 return -1;
146}
147int RestoreIfaceOffloadingWin32(LiveDevice *ldev)
148{
149 SCLogWarning("Suricata not targeted for Windows Vista or "
150 "higher. Network offload interrogation not "
151 "available.");
152 return -1;
153}
154
155#else /* NTDDI_VERSION >= NTDDI_VISTA */
156
157static HMODULE wmiutils_dll = NULL;
158
159/**
160 * \brief obtain the WMI utilities DLL
161 */
162static HMODULE WmiUtils(void)
163{
164 if (wmiutils_dll == NULL) {
165 wmiutils_dll =
166 LoadLibraryA("C:\\Windows\\System32\\wbem\\wmiutils.dll");
167 }
168
169 return wmiutils_dll;
170}
171
172/**
173 * \brief allocate a BSTR from a converted unsigned integer
174 */
175static BSTR utob(uint64_t ui)
176{
177 wchar_t buf[20];
178 _ui64tow(ui, buf, 10);
179 return SysAllocString(buf);
180}
181
182/**
183 * \brief Get the win32/wmi error string
184 *
185 * The caller should use the LocalFree function on the returned pointer to free
186 * the buffer when it is no longer needed.
187 */
188const char *Win32GetErrorString(DWORD error_code, HMODULE ext_module)
189{
190 char *error_string = NULL;
191
192 DWORD flags =
193 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS;
194 if (ext_module != NULL) {
195 flags |= FORMAT_MESSAGE_FROM_HMODULE;
196 } else {
197 flags |= FORMAT_MESSAGE_FROM_SYSTEM;
198 }
199
200 FormatMessageA(flags, ext_module, error_code, 0, (LPTSTR)&error_string, 0,
201 NULL);
202
203 if (error_string == NULL) {
204 return "";
205 }
206
207 error_string[strlen(error_string) - 2] = 0; // remove line breaks
208
209 return error_string;
210}
211
212#ifdef DEBUG
213/**
214 * \brief log an HRESULT
215 */
216static void _Win32HResultLog(SCLogLevel level, HRESULT hr, const char *file,
217 const char *function, const int line)
218{
219 const char *err_str = Win32GetErrorString(hr, WmiUtils());
220 SCLog(level, file, function, line, "HRESULT: %s (0x%08" PRIx32 ")", err_str,
221 (uint32_t)(hr));
222 LocalFree((LPVOID)err_str);
223}
224
225#define Win32HResultLogDebug(hr) \
226 _Win32HResultLog(SC_LOG_DEBUG, (hr), __FILE__, __FUNCTION__, __LINE__)
227#else
228#define Win32HResultLogDebug(hr)
229#endif /* DEBUG */
230
231/**
232 * \brief log a WBEM error
233 */
234#define WbemLogDebug(hr) (_WbemLogDebug)((hr), __FILE__, __FUNCTION__, __LINE__)
235
236static void _WbemLogDebug(HRESULT hr, const char *file, const char *function,
237 const int line)
238{
239#ifdef DEBUG
240 IErrorInfo *err_info;
241 BSTR err_description;
242 char *err_description_mb = NULL;
243
244 _Win32HResultLog(SC_LOG_DEBUG, hr, file, function, line);
245
246 GetErrorInfo(0, &err_info);
247 if (!SUCCEEDED(
248 err_info->lpVtbl->GetDescription(err_info, &err_description))) {
249 // not much to do when your error log errors out...
250 goto release;
251 }
252
253 err_description_mb = SCMalloc(SysStringLen(err_description) + 1);
254
255 if (err_description_mb == NULL) {
256 // not much to do when your error log errors out...
257 goto release;
258 }
259
260 // do the actual multibyte conversion
261 err_description_mb[SysStringLen(err_description)] = 0;
262 wcstombs(err_description_mb, err_description,
263 SysStringLen(err_description));
264
265 // log the description
266 SCLog(SC_LOG_DEBUG, file, function, line, "WBEM error: %s",
267 err_description_mb);
268
269release:
270 SCFree(err_description_mb);
271 SysFreeString(err_description);
272#endif /* DEBUG */
273}
274
275/**
276 * \brief get the maximum transmissible unit for the specified pcap device name
277 */
278int GetIfaceMTUWin32(const char *pcap_dev)
279{
280 DWORD err = NO_ERROR;
281
282 int mtu = 0;
283
284 IP_ADAPTER_ADDRESSES *if_info_list = NULL, *if_info = NULL;
285 err = Win32GetAdaptersAddresses(&if_info_list);
286 if (err != NO_ERROR) {
287 mtu = -1;
288 goto release;
289 }
290 err = Win32FindAdapterAddresses(if_info_list, pcap_dev, &if_info);
291 if (err != NO_ERROR) {
292 mtu = -1;
293 goto release;
294 }
295
296 mtu = if_info->Mtu;
297
298release:
299 SCFree(if_info_list);
300
301 if (err != S_OK) {
302 const char *errbuf = Win32GetErrorString(err, WmiUtils());
303 SCLogWarning("Failure when trying to get MTU via syscall for '%s': %s "
304 "(0x%08" PRIx32 ")",
305 pcap_dev, errbuf, (uint32_t)err);
306 LocalFree((LPVOID)errbuf);
307 } else {
308 SCLogInfo("Found an MTU of %d for '%s'", mtu, pcap_dev);
309 }
310
311 return mtu;
312}
313
314/**
315 * \brief get the maximum transmissible unit for all devices on the system
316 */
317int GetGlobalMTUWin32(void)
318{
319 uint32_t mtu = 0;
320
321 DWORD err = NO_ERROR;
322 IP_ADAPTER_ADDRESSES *if_info_list = NULL;
323
324 /* get a list of all adapters' data */
325 err = Win32GetAdaptersAddresses(&if_info_list);
326 if (err != NO_ERROR) {
327 goto fail;
328 }
329
330 /* now search for the right adapter in the list */
331 IP_ADAPTER_ADDRESSES *if_info = NULL;
332 for (if_info = if_info_list; if_info != NULL; if_info = if_info->Next) {
333 /* -1 (uint) is an invalid value */
334 if (if_info->Mtu == (uint32_t)-1) {
335 continue;
336 }
337
338 /* we want to return the largest MTU value so we allocate enough */
339 mtu = max(mtu, if_info->Mtu);
340 }
341
342 SCFree(if_info_list);
343
344 SCLogInfo("Found a global MTU of %" PRIu32, mtu);
345 return (int)mtu;
346
347fail:
348 SCFree(if_info_list);
349
350 const char *errbuf = NULL;
351 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
352 FORMAT_MESSAGE_IGNORE_INSERTS,
353 NULL, err, 0, (LPTSTR)&errbuf, 0, NULL);
354
355 SCLogWarning("Failure when trying to get global MTU via syscall: %s (%" PRId32 ")", errbuf,
356 (uint32_t)err);
357
358 return -1;
359}
360
361#define ReleaseObject(objptr) \
362 do { \
363 if ((objptr) != NULL) { \
364 (objptr)->lpVtbl->Release(objptr); \
365 (objptr) = NULL; \
366 } \
367 } while (0);
368
369typedef enum Win32TcpOffloadFlags_ {
370 WIN32_TCP_OFFLOAD_FLAG_NONE = 0,
371 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX = 1,
372 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX = 1 << 1,
373 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX = 1 << 2,
374 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX = 1 << 3,
375 WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4 = 1 << 4,
376 WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4 = 1 << 5,
377 WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6 = 1 << 6,
378
379 /* aggregates */
380 WIN32_TCP_OFFLOAD_FLAG_CSUM = WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX |
381 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX |
382 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX |
383 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX,
384 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4 = WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX |
385 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX,
386 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6 = WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX |
387 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX,
388 WIN32_TCP_OFFLOAD_FLAG_LSO = WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4 |
389 WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4 |
390 WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6,
391} Win32TcpOffloadFlags;
392
393typedef struct ComInstance_ {
394 IWbemLocator *locator;
395 IWbemServices *services;
396} ComInstance;
397
398/**
399 * \brief Creates a COM instance connected to the specified resource
400 */
401static HRESULT ComInstanceInit(ComInstance *instance, LPCWSTR resource)
402{
403 HRESULT hr = S_OK;
404
405 instance->locator = NULL;
406 instance->services = NULL;
407
408 BSTR resource_bstr = SysAllocString(resource);
409 if (resource_bstr == NULL) {
410 hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY);
411 SCLogWarning("Failed to allocate BSTR");
412 goto release;
413 }
414
415 /* connect to COM */
416 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
417 if (hr == S_FALSE) {
418 /* already initialized */
419 hr = S_OK;
420 } else {
421 if (hr != S_OK) {
422 SCLogWarning("COM CoInitializeEx failed: 0x%" PRIx32, (uint32_t)hr);
423 goto release;
424 }
425 hr = CoInitializeSecurity(
426 NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT,
427 RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
428 if (hr != S_OK) {
429 SCLogWarning("COM CoInitializeSecurity failed: 0x%" PRIx32, (uint32_t)hr);
430 goto release;
431 }
432 }
433
434 /* connect to WMI */
435 hr = CoCreateInstance(&CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER,
436 &IID_IWbemLocator, (LPVOID *)&instance->locator);
437 if (hr != S_OK) {
438 SCLogWarning("COM CoCreateInstance failed: 0x%" PRIx32, (uint32_t)hr);
439 goto release;
440 }
441 hr = instance->locator->lpVtbl->ConnectServer(
442 instance->locator, resource_bstr, NULL, NULL, NULL, 0, NULL, NULL,
443 &instance->services);
444 if (hr != S_OK) {
445 SCLogWarning("COM ConnectServer failed: 0x%" PRIx32, (uint32_t)hr);
446 goto release;
447 }
448
449release:
450 SysFreeString(resource_bstr);
451
452 return hr;
453}
454
455/**
456 * \brief Releases resources for a COM instance.
457 */
458static void ComInstanceRelease(ComInstance *instance)
459{
460 if (instance == NULL) {
461 return;
462 }
463 ReleaseObject(instance->services);
464 ReleaseObject(instance->locator);
465}
466
467/**
468 * \brief obtains a class definition from COM services
469 */
470static HRESULT GetWbemClass(ComInstance *instance, LPCWSTR name,
471 IWbemClassObject **p_class)
472{
473 HRESULT hr = WBEM_S_NO_ERROR;
474 BSTR name_bstr = NULL;
475
476 if (instance == NULL || name == NULL || p_class == NULL ||
477 *p_class != NULL) {
478 hr = HRESULT_FROM_WIN32(E_INVALIDARG);
479 Win32HResultLogDebug(hr);
480 goto release;
481 }
482
483 /* allocate name string */
484 name_bstr = SysAllocString(name);
485 if (name_bstr == NULL) {
486 hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY);
487 SCLogWarning("Failed to allocate BSTR");
488 goto release;
489 }
490
491 /* obtain object */
492 hr = instance->services->lpVtbl->GetObject(instance->services, name_bstr,
493 WBEM_FLAG_RETURN_WBEM_COMPLETE,
494 NULL, p_class, NULL);
495 if (hr != S_OK) {
496 WbemLogDebug(hr);
497 SCLogWarning("WMI GetObject failed: 0x%" PRIx32, (uint32_t)hr);
498 goto release;
499 }
500
501release:
502 SysFreeString(name_bstr);
503
504 return hr;
505}
506
507/**
508 * \brief spawns an empty class instance of the specified type
509 */
510static HRESULT GetWbemClassInstance(ComInstance *instance, LPCWSTR name,
511 IWbemClassObject **p_instance)
512{
513 HRESULT hr = WBEM_S_NO_ERROR;
514
515 IWbemClassObject *class = NULL;
516
517 hr = GetWbemClass(instance, name, &class);
518 if (hr != WBEM_S_NO_ERROR) {
519 goto release;
520 }
521
522 hr = class->lpVtbl->SpawnInstance(class, 0, p_instance);
523 if (hr != WBEM_S_NO_ERROR) {
524 WbemLogDebug(hr);
525 SCLogWarning("WMI SpawnInstance failed: 0x%" PRIx32, (uint32_t)hr);
526 goto release;
527 }
528
529release:
530 return hr;
531}
532
533typedef struct WbemMethod_ {
534 ComInstance *com_instance;
535
536 BSTR method_name;
537
538 IWbemClassObject *in_params, *out_params;
539} WbemMethod;
540
541/**
542 * \brief initializes resources for a WMI method handle
543 */
544static HRESULT GetWbemMethod(ComInstance *com_instance, LPCWSTR class_name,
545 LPCWSTR method_name, WbemMethod *method)
546{
547 HRESULT hr = S_OK;
548 IWbemClassObject *class = NULL;
549
550 method->com_instance = com_instance;
551
552 BSTR class_name_bstr = SysAllocString(class_name);
553 if (class_name_bstr == NULL) {
554 hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY);
555 SCLogWarning("Failed to allocate BSTR");
556 goto release;
557 }
558 method->method_name = SysAllocString(method_name);
559 if (method->method_name == NULL) {
560 hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY);
561 SCLogWarning("Failed to allocate BSTR");
562 goto release;
563 }
564
565 /* find our class definition to retrieve parameters */
566 hr = GetWbemClass(com_instance, class_name, &class);
567 if (hr != WBEM_S_NO_ERROR) {
568 goto release;
569 }
570
571 /* find the method on the retrieved class */
572 hr = class->lpVtbl->GetMethod(class, method_name, 0, &method->in_params,
573 &method->out_params);
574 if (hr != WBEM_S_NO_ERROR) {
575 WbemLogDebug(hr);
576 SCLogWarning("WMI GetMethod failed: 0x%" PRIx32, (uint32_t)hr);
577 goto release;
578 }
579
580release:
581 ReleaseObject(class);
582
583 SysFreeString(class_name_bstr);
584
585 return hr;
586}
587
588/**
589 * \brief Releases resources for a WMI method handle
590 */
591static void WbemMethodRelease(WbemMethod *method)
592{
593 if (method == NULL) {
594 return;
595 }
596 ReleaseObject(method->in_params);
597 ReleaseObject(method->out_params);
598
599 SysFreeString(method->method_name);
600}
601
602typedef struct WbemMethodCall_ {
603 WbemMethod *method;
604
605 BSTR instance_path;
606
607 IWbemClassObject *in_params;
608} WbemMethodCall;
609
610/**
611 * \brief generates a single-use WMI method call
612 */
613static HRESULT GetWbemMethodCall(WbemMethod *method, LPCWSTR instance_path,
614 WbemMethodCall *call)
615{
616 HRESULT hr = S_OK;
617
618 call->method = method;
619 call->instance_path = SysAllocString(instance_path);
620 if (call->instance_path == NULL) {
621 hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY);
622 SCLogWarning("Failed to allocate BSTR: 0x%" PRIx32, (uint32_t)hr);
623 goto release;
624 }
625
626 /* make an instance of the in/out params */
627 hr = method->in_params->lpVtbl->SpawnInstance(method->in_params, 0,
628 &call->in_params);
629 if (hr != S_OK) {
630 WbemLogDebug(hr);
631 SCLogWarning("WMI SpawnInstance failed on in_params: 0x%" PRIx32, (uint32_t)hr);
632 goto release;
633 }
634
635release:
636 return hr;
637}
638
639/**
640 * \brief releases the WMI method call resources
641 */
642static void WbemMethodCallRelease(WbemMethodCall *call)
643{
644 if (call == NULL) {
645 return;
646 }
647 ReleaseObject(call->in_params);
648
649 SysFreeString(call->instance_path);
650}
651
652/**
653 * \brief executes the method after the client has set applicable parameters.
654 */
655static HRESULT WbemMethodCallExec(WbemMethodCall *call,
656 IWbemClassObject **p_out_params)
657{
658 HRESULT hr = S_OK;
659
660 hr = call->method->com_instance->services->lpVtbl->ExecMethod(
661 call->method->com_instance->services, call->instance_path,
662 call->method->method_name, 0, NULL, call->in_params, p_out_params,
663 NULL);
664 if (hr != WBEM_S_NO_ERROR) {
665 WbemLogDebug(hr);
666 SCLogDebug("WMI ExecMethod failed: 0x%" PRIx32, (uint32_t)hr);
667 goto release;
668 }
669
670release:
671 return hr;
672}
673
674/**
675 * Obtains an IWbemClassObject named property of a parent IWbemClassObject
676 */
677static HRESULT WbemGetSubObject(IWbemClassObject *object, LPCWSTR property_name,
678 IWbemClassObject **sub_object)
679{
680 HRESULT hr = S_OK;
681
682 VARIANT out_var;
683 VariantInit(&out_var);
684 hr = object->lpVtbl->Get(object, property_name, 0, &out_var, NULL, NULL);
685 if (hr != WBEM_S_NO_ERROR) {
686 goto release;
687 }
688
689 IUnknown *unknown = V_UNKNOWN(&out_var);
690 hr = unknown->lpVtbl->QueryInterface(unknown, &IID_IWbemClassObject,
691 (void **)sub_object);
692 if (hr != S_OK) {
693 SCLogWarning("WMI QueryInterface (IWbemClassObject) failed: 0x%" PRIx32, (uint32_t)hr);
694 goto release;
695 }
696
697release:
698 VariantClear(&out_var);
699 return hr;
700}
701
702/**
703 * Obtains an Encapsulation value from an MSNdis_WmiOffload property
704 */
705static HRESULT GetEncapsulation(IWbemClassObject *object, LPCWSTR category,
706 LPCWSTR subcategory, ULONG *encapsulation)
707{
708 HRESULT hr = WBEM_S_NO_ERROR;
709
710 IWbemClassObject *category_object = NULL;
711 IWbemClassObject *subcategory_object = NULL;
712
713 VARIANT out_var;
714 VariantInit(&out_var);
715
716 /* get category object */
717 hr = WbemGetSubObject(object, category, &category_object);
718 if (hr != WBEM_S_NO_ERROR) {
719 goto release;
720 }
721
722 /* get sub-category object */
723 hr = WbemGetSubObject(category_object, subcategory, &subcategory_object);
724 if (hr != WBEM_S_NO_ERROR) {
725 goto release;
726 }
727 hr = subcategory_object->lpVtbl->Get(subcategory_object, L"Encapsulation",
728 0, &out_var, NULL, NULL);
729 if (hr != WBEM_S_NO_ERROR) {
730 goto release;
731 }
732 *encapsulation = V_UI4(&out_var);
733
734release:
735 VariantClear(&out_var);
736 ReleaseObject(subcategory_object);
737 ReleaseObject(category_object);
738 return hr;
739}
740
741static HRESULT GetIUnknown(IWbemClassObject *object, IUnknown **p_unknown)
742{
743 HRESULT hr = WBEM_S_NO_ERROR;
744
745 if (object == NULL || p_unknown == NULL || *p_unknown != NULL) {
746 hr = HRESULT_FROM_WIN32(E_INVALIDARG);
747 Win32HResultLogDebug(hr);
748 goto release;
749 }
750
751 hr = object->lpVtbl->QueryInterface(object, &IID_IUnknown,
752 (void **)p_unknown);
753 if (hr != S_OK) {
754 SCLogWarning("WMI QueryInterface (IUnknown) failed: 0x%" PRIx32, (uint32_t)hr);
755 goto release;
756 }
757
758release:
759 return hr;
760}
761
762static HRESULT BuildNdisObjectHeader(ComInstance *instance, uint8_t type,
763 uint8_t revision, uint16_t size,
764 IWbemClassObject **p_ndis_object_header)
765{
766 HRESULT hr = WBEM_S_NO_ERROR;
767
768 if (instance == NULL || p_ndis_object_header == NULL ||
769 *p_ndis_object_header != NULL) {
770
771 hr = HRESULT_FROM_WIN32(E_INVALIDARG);
772 Win32HResultLogDebug(hr);
773 goto release;
774 }
775
776 /* obtain object */
777 hr = GetWbemClassInstance(instance, L"MSNdis_ObjectHeader",
778 p_ndis_object_header);
779 if (hr != WBEM_S_NO_ERROR) {
780 goto release;
781 }
782
783 VARIANT param_variant;
784 VariantInit(&param_variant);
785 IWbemClassObject *ndis_object_header = *p_ndis_object_header;
786
787 /* set parameters */
788 V_VT(&param_variant) = VT_UI1;
789 V_UI1(&param_variant) = type;
790 hr = ndis_object_header->lpVtbl->Put(ndis_object_header, L"Type", 0,
791 &param_variant, 0);
792 VariantClear(&param_variant);
793 if (hr != WBEM_S_NO_ERROR) {
794 WbemLogDebug(hr);
795 goto release;
796 }
797
798 V_VT(&param_variant) = VT_UI1;
799 V_UI1(&param_variant) = revision;
800 hr = ndis_object_header->lpVtbl->Put(ndis_object_header, L"Revision", 0,
801 &param_variant, 0);
802 VariantClear(&param_variant);
803 if (hr != WBEM_S_NO_ERROR) {
804 WbemLogDebug(hr);
805 goto release;
806 }
807
808 /* https://docs.microsoft.com/en-us/windows-hardware/drivers/network/ndis-object-version-issues-for-wmi
809 */
810 V_VT(&param_variant) = VT_I4;
811 V_I4(&param_variant) = size;
812 hr = ndis_object_header->lpVtbl->Put(ndis_object_header, L"Size", 0,
813 &param_variant, 0);
814 VariantClear(&param_variant);
815 if (hr != WBEM_S_NO_ERROR) {
816 WbemLogDebug(hr);
817 goto release;
818 }
819
820release:
821 return hr;
822}
823
824static HRESULT BuildNdisWmiMethodHeader(ComInstance *instance,
825 uint64_t net_luid, uint32_t port_number,
826 uint64_t request_id, uint32_t timeout,
827 IWbemClassObject **p_ndis_method_header)
828{
829 HRESULT hr = WBEM_S_NO_ERROR;
830
831 IWbemClassObject *ndis_object_header = NULL;
832
833 if (instance == NULL || p_ndis_method_header == NULL ||
834 *p_ndis_method_header != NULL) {
835
836 hr = HRESULT_FROM_WIN32(E_INVALIDARG);
837 Win32HResultLogDebug(hr);
838 goto release;
839 }
840
841 /* obtain object */
842 hr = GetWbemClassInstance(instance, L"MSNdis_WmiMethodHeader",
843 p_ndis_method_header);
844 if (hr != WBEM_S_NO_ERROR) {
845 goto release;
846 }
847
848 VARIANT param_variant;
849 VariantInit(&param_variant);
850
851 /* get embedded MSNdis_ObjectHeader */
852 hr = BuildNdisObjectHeader(instance, NDIS_WMI_OBJECT_TYPE_METHOD,
853 NDIS_WMI_METHOD_HEADER_REVISION_1, 0xFFFF,
854 &ndis_object_header);
855 if (hr != WBEM_S_NO_ERROR) {
856 goto release;
857 }
858 V_VT(&param_variant) = VT_UNKNOWN;
859 V_UNKNOWN(&param_variant) = NULL;
860 hr = GetIUnknown(ndis_object_header, &V_UNKNOWN(&param_variant));
861 if (hr != WBEM_S_NO_ERROR) {
862 goto release;
863 }
864
865 IWbemClassObject *ndis_method_header = *p_ndis_method_header;
866
867 /* set parameters */
868 hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"Header", 0,
869 &param_variant, 0);
870 VariantClear(&param_variant);
871 if (hr != WBEM_S_NO_ERROR) {
872 WbemLogDebug(hr);
873 goto release;
874 }
875
876 V_VT(&param_variant) = VT_BSTR;
877 V_BSTR(&param_variant) = utob(net_luid);
878 hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"NetLuid", 0,
879 &param_variant, 0);
880 VariantClear(&param_variant);
881 if (hr != WBEM_S_NO_ERROR) {
882 WbemLogDebug(hr);
883 goto release;
884 }
885
886 V_VT(&param_variant) = VT_BSTR;
887 V_BSTR(&param_variant) = utob((uint64_t)port_number);
888 hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"PortNumber", 0,
889 &param_variant, 0);
890 VariantClear(&param_variant);
891 if (hr != WBEM_S_NO_ERROR) {
892 WbemLogDebug(hr);
893 goto release;
894 }
895
896 V_VT(&param_variant) = VT_BSTR;
897 V_BSTR(&param_variant) = utob(request_id);
898 hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"RequestId", 0,
899 &param_variant, 0);
900 VariantClear(&param_variant);
901 if (hr != WBEM_S_NO_ERROR) {
902 WbemLogDebug(hr);
903 goto release;
904 }
905
906 V_VT(&param_variant) = VT_BSTR;
907 V_BSTR(&param_variant) = utob((uint64_t)timeout);
908 hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"Timeout", 0,
909 &param_variant, 0);
910 VariantClear(&param_variant);
911 if (hr != WBEM_S_NO_ERROR) {
912 WbemLogDebug(hr);
913 goto release;
914 }
915
916 V_VT(&param_variant) = VT_BSTR;
917 V_BSTR(&param_variant) = utob((uint64_t)0);
918 hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"Padding", 0,
919 &param_variant, 0);
920 VariantClear(&param_variant);
921 if (hr != WBEM_S_NO_ERROR) {
922 WbemLogDebug(hr);
923 goto release;
924 }
925
926release:
927 ReleaseObject(ndis_object_header);
928
929 return hr;
930}
931
932/**
933 * \brief polls the NDIS TCP offloading status, namely LSOv1/v2
934 */
935static HRESULT GetNdisOffload(LPCWSTR if_description, uint32_t *offload_flags)
936{
937 HRESULT hr = S_OK;
938
939 ComInstance instance = {};
940 WbemMethod method = {};
941 WbemMethodCall call = {};
942
943 IWbemClassObject *ndis_method_header = NULL;
944 IWbemClassObject *out_params = NULL;
945 IWbemClassObject *ndis_offload = NULL;
946
947 if (if_description == NULL) {
948 SCLogWarning("No description specified for device");
949 hr = HRESULT_FROM_WIN32(E_INVALIDARG);
950 goto release;
951 }
952
953 LPCWSTR class_name = L"MSNdis_TcpOffloadCurrentConfig";
954 LPCWSTR instance_name_fmt = L"%s=\"%s\"";
955 size_t n_chars = wcslen(class_name) + wcslen(if_description) +
956 wcslen(instance_name_fmt);
957 LPWSTR instance_name = SCMalloc((n_chars + 1) * sizeof(wchar_t));
958 if (instance_name == NULL) {
959 SCLogWarning("Failed to allocate buffer for instance path");
960 goto release;
961 }
962 instance_name[n_chars] = 0; /* defensively null-terminate */
963 hr = StringCchPrintfW(instance_name, n_chars, instance_name_fmt, class_name,
964 if_description);
965 if (hr != S_OK) {
966 SCLogWarning("Failed to format WMI class instance name: 0x%" PRIx32, (uint32_t)hr);
967 goto release;
968 }
969 /* method name */
970 LPCWSTR method_name = L"WmiQueryCurrentOffloadConfig";
971
972 /* connect to COM/WMI */
973 hr = ComInstanceInit(&instance, L"ROOT\\WMI");
974 if (hr != S_OK) {
975 goto release;
976 }
977
978 /* obtain method */
979 hr = GetWbemMethod(&instance, class_name, method_name, &method);
980 if (hr != S_OK) {
981 goto release;
982 }
983
984 /* make parameter instances */
985 hr = GetWbemMethodCall(&method, instance_name, &call);
986 if (hr != S_OK) {
987 goto release;
988 }
989
990 /* build parameters */
991
992 VARIANT param_variant;
993 VariantInit(&param_variant);
994
995 /* Make MSNdis_WmiMethodHeader */
996 hr = BuildNdisWmiMethodHeader(&instance, 0, 0, 0, 5, &ndis_method_header);
997 if (hr != WBEM_S_NO_ERROR) {
998 goto release;
999 }
1000 V_VT(&param_variant) = VT_UNKNOWN;
1001 V_UNKNOWN(&param_variant) = NULL;
1002 hr = GetIUnknown(ndis_method_header, &V_UNKNOWN(&param_variant));
1003 if (hr != WBEM_S_NO_ERROR) {
1004 goto release;
1005 }
1006
1007 /* Set in_params */
1008 hr = call.in_params->lpVtbl->Put(call.in_params, L"Header", 0,
1009 &param_variant, 0);
1010 VariantClear(&param_variant);
1011 if (hr != WBEM_S_NO_ERROR) {
1012 WbemLogDebug(hr);
1013 goto release;
1014 }
1015
1016 /* execute the method */
1017 hr = WbemMethodCallExec(&call, &out_params);
1018 if (hr != S_OK) {
1019 size_t if_description_len = wcslen(if_description);
1020 char *if_description_ansi = SCMalloc(if_description_len + 1);
1021 if (if_description_ansi == NULL) {
1022 SCLogWarning("Failed to allocate buffer for interface description");
1023 goto release;
1024 }
1025 if_description_ansi[if_description_len] = 0;
1026 wcstombs(if_description_ansi, if_description, if_description_len);
1027 SCLogInfo("Obtaining offload state failed, device \"%s\" may not "
1028 "support offload. Error: 0x%" PRIx32,
1029 if_description_ansi, (uint32_t)hr);
1030 SCFree(if_description_ansi);
1031 Win32HResultLogDebug(hr);
1032 goto release;
1033 }
1034
1035 /* inspect the result */
1036 hr = WbemGetSubObject(out_params, L"Offload", &ndis_offload);
1037 if (hr != WBEM_S_NO_ERROR) {
1038 goto release;
1039 }
1040 ULONG encapsulation = 0;
1041
1042 /* Checksum */
1043 hr = GetEncapsulation(ndis_offload, L"Checksum", L"IPv4Receive",
1044 &encapsulation);
1045 if (hr != WBEM_S_NO_ERROR) {
1046 WbemLogDebug(hr);
1047 goto release;
1048 }
1049 if (encapsulation != 0) {
1050 *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX;
1051 }
1052 hr = GetEncapsulation(ndis_offload, L"Checksum", L"IPv4Transmit",
1053 &encapsulation);
1054 if (hr != WBEM_S_NO_ERROR) {
1055 WbemLogDebug(hr);
1056 goto release;
1057 }
1058 if (encapsulation != 0) {
1059 *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX;
1060 }
1061 hr = GetEncapsulation(ndis_offload, L"Checksum", L"IPv6Receive",
1062 &encapsulation);
1063 if (hr != WBEM_S_NO_ERROR) {
1064 WbemLogDebug(hr);
1065 goto release;
1066 }
1067 if (encapsulation != 0) {
1068 *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX;
1069 }
1070 hr = GetEncapsulation(ndis_offload, L"Checksum", L"IPv6Transmit",
1071 &encapsulation);
1072 if (hr != WBEM_S_NO_ERROR) {
1073 WbemLogDebug(hr);
1074 goto release;
1075 }
1076 if (encapsulation != 0) {
1077 *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX;
1078 }
1079
1080 /* LsoV1 */
1081 hr = GetEncapsulation(ndis_offload, L"LsoV1", L"WmiIPv4", &encapsulation);
1082 if (hr != WBEM_S_NO_ERROR) {
1083 WbemLogDebug(hr);
1084 goto release;
1085 }
1086 if (encapsulation != 0) {
1087 *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4;
1088 }
1089
1090 /* LsoV2 */
1091 hr = GetEncapsulation(ndis_offload, L"LsoV2", L"WmiIPv4", &encapsulation);
1092 if (hr != WBEM_S_NO_ERROR) {
1093 WbemLogDebug(hr);
1094 goto release;
1095 }
1096 if (encapsulation != 0) {
1097 *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4;
1098 }
1099 hr = GetEncapsulation(ndis_offload, L"LsoV2", L"WmiIPv6", &encapsulation);
1100 if (hr != WBEM_S_NO_ERROR) {
1101 WbemLogDebug(hr);
1102 goto release;
1103 }
1104 if (encapsulation != 0) {
1105 *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6;
1106 }
1107
1108release:
1109 ReleaseObject(ndis_method_header);
1110 ReleaseObject(ndis_offload);
1111 ReleaseObject(out_params);
1112
1113 WbemMethodCallRelease(&call);
1114 WbemMethodRelease(&method);
1115 ComInstanceRelease(&instance);
1116
1117 return hr;
1118}
1119
1120int GetIfaceOffloadingWin32(const char *pcap_dev, int csum, int other)
1121{
1122 SCLogDebug("Querying offloading for device %s", pcap_dev);
1123
1124 DWORD err = NO_ERROR;
1125 int ret = 0;
1126 uint32_t offload_flags = 0;
1127
1128 /* WMI uses the description as an identifier... */
1129 IP_ADAPTER_ADDRESSES *if_info_list = NULL, *if_info = NULL;
1130 err = Win32GetAdaptersAddresses(&if_info_list);
1131 if (err != NO_ERROR) {
1132 ret = -1;
1133 goto release;
1134 }
1135 err = Win32FindAdapterAddresses(if_info_list, pcap_dev, &if_info);
1136 if (err != NO_ERROR) {
1137 ret = -1;
1138 goto release;
1139 }
1140 LPWSTR if_description = if_info->Description;
1141
1142 /* now query WMI for the offload info */
1143 err = GetNdisOffload(if_description, &offload_flags);
1144 if (err != S_OK) {
1145 ret = -1;
1146 goto release;
1147 } else if (offload_flags != 0) {
1148 if (csum == 1) {
1149 if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM) != 0) {
1150 ret = 1;
1151 }
1152 }
1153 if (other == 1) {
1154 if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSO) != 0) {
1155 ret = 1;
1156 }
1157 }
1158 }
1159
1160 if (ret == 0) {
1161 SCLogPerf("NIC offloading on %s: Checksum IPv4 Rx: %d Tx: %d IPv6 "
1162 "Rx: %d Tx: %d LSOv1 IPv4: %d LSOv2 IPv4: %d IPv6: %d",
1163 pcap_dev,
1164 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX) != 0,
1165 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX) != 0,
1166 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX) != 0,
1167 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX) != 0,
1168 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4) != 0,
1169 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4) != 0,
1170 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6) != 0);
1171 } else {
1172 SCLogWarning("NIC offloading on %s: Checksum IPv4 Rx: %d Tx: %d IPv6 "
1173 "Rx: %d Tx: %d LSOv1 IPv4: %d LSOv2 IPv4: %d IPv6: %d",
1174 pcap_dev, (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX) != 0,
1175 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX) != 0,
1176 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX) != 0,
1177 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX) != 0,
1178 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4) != 0,
1179 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4) != 0,
1180 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6) != 0);
1181 }
1182
1183release:
1184 if (ret == -1) {
1185 const char *err_str = Win32GetErrorString(err, WmiUtils());
1186 SCLogWarning("Failure when trying to get feature via syscall for '%s': "
1187 "%s (0x%08" PRIx32 ")",
1188 pcap_dev, err_str, (uint32_t)err);
1189 LocalFree((LPVOID)err_str);
1190 }
1191
1192 SCFree(if_info_list);
1193
1194 return ret;
1195}
1196
1197static HRESULT
1198BuildNdisTcpOffloadParameters(ComInstance *instance, uint32_t offload_flags,
1199 bool enable,
1200 IWbemClassObject **p_ndis_tcp_offload_parameters)
1201{
1202 HRESULT hr = WBEM_S_NO_ERROR;
1203
1204 IWbemClassObject *ndis_object_header = NULL;
1205
1206 if (instance == NULL || p_ndis_tcp_offload_parameters == NULL ||
1207 *p_ndis_tcp_offload_parameters != NULL) {
1208
1209 hr = HRESULT_FROM_WIN32(E_INVALIDARG);
1210 Win32HResultLogDebug(hr);
1211 goto release;
1212 }
1213
1214 /* obtain object */
1215 hr = GetWbemClassInstance(instance, L"MSNdis_TcpOffloadParameters",
1216 p_ndis_tcp_offload_parameters);
1217 if (hr != WBEM_S_NO_ERROR) {
1218 goto release;
1219 }
1220
1221 VARIANT param_variant;
1222 VariantInit(&param_variant);
1223
1224 /* get embedded MSNdis_ObjectHeader */
1225 hr = BuildNdisObjectHeader(instance, NDIS_OBJECT_TYPE_DEFAULT,
1226 NDIS_OFFLOAD_PARAMETERS_REVISION_1,
1227 NDIS_SIZEOF_OFFLOAD_PARAMETERS_REVISION_1,
1228 &ndis_object_header);
1229 if (hr != WBEM_S_NO_ERROR) {
1230 goto release;
1231 }
1232 V_VT(&param_variant) = VT_UNKNOWN;
1233 V_UNKNOWN(&param_variant) = NULL;
1234 hr = GetIUnknown(ndis_object_header, &V_UNKNOWN(&param_variant));
1235 if (hr != WBEM_S_NO_ERROR) {
1236 goto release;
1237 }
1238
1239 IWbemClassObject *ndis_tcp_offload_parameters =
1240 *p_ndis_tcp_offload_parameters;
1241
1242 /* set parameters */
1243 hr = ndis_tcp_offload_parameters->lpVtbl->Put(
1244 ndis_tcp_offload_parameters, L"Header", 0, &param_variant, 0);
1245 VariantClear(&param_variant);
1246 if (hr != WBEM_S_NO_ERROR) {
1247 Win32HResultLogDebug(hr);
1248 goto release;
1249 }
1250
1251 /* IPv4 csum */
1252 V_VT(&param_variant) = VT_BSTR;
1253 V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);
1254 if (!enable && (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4) != 0) {
1255 /* this is basically all disabled cases */
1256 V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED);
1257 } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4) ==
1258 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4) {
1259 /* implied enable */
1260 V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED);
1261 } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX) != 0) {
1262 /* implied enable */
1263 V_BSTR(&param_variant) =
1264 utob(NDIS_OFFLOAD_PARAMETERS_RX_ENABLED_TX_DISABLED);
1265 } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX) != 0) {
1266 /* implied enable */
1267 V_BSTR(&param_variant) =
1268 utob(NDIS_OFFLOAD_PARAMETERS_TX_ENABLED_RX_DISABLED);
1269 }
1270 hr = ndis_tcp_offload_parameters->lpVtbl->Put(
1271 ndis_tcp_offload_parameters, L"IPv4Checksum", 0, &param_variant, 0);
1272 if (hr != WBEM_S_NO_ERROR) {
1273 WbemLogDebug(hr);
1274 goto release;
1275 }
1276 hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters,
1277 L"TCPIPv4Checksum", 0,
1278 &param_variant, 0);
1279 if (hr != WBEM_S_NO_ERROR) {
1280 WbemLogDebug(hr);
1281 goto release;
1282 }
1283 hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters,
1284 L"UDPIPv4Checksum", 0,
1285 &param_variant, 0);
1286 if (hr != WBEM_S_NO_ERROR) {
1287 WbemLogDebug(hr);
1288 goto release;
1289 }
1290 VariantClear(&param_variant);
1291
1292 /* IPv6 csum */
1293 V_VT(&param_variant) = VT_BSTR;
1294 V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);
1295 if (!enable && (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6) != 0) {
1296 /* this is basically all disabled cases */
1297 V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED);
1298 } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6) ==
1299 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6) {
1300 /* implied enable */
1301 V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED);
1302 } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX) != 0) {
1303 /* implied enable */
1304 V_BSTR(&param_variant) =
1305 utob(NDIS_OFFLOAD_PARAMETERS_RX_ENABLED_TX_DISABLED);
1306 } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX) != 0) {
1307 /* implied enable */
1308 V_BSTR(&param_variant) =
1309 utob(NDIS_OFFLOAD_PARAMETERS_TX_ENABLED_RX_DISABLED);
1310 }
1311 hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters,
1312 L"TCPIPv6Checksum", 0,
1313 &param_variant, 0);
1314 if (hr != WBEM_S_NO_ERROR) {
1315 WbemLogDebug(hr);
1316 goto release;
1317 }
1318 hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters,
1319 L"UDPIPv6Checksum", 0,
1320 &param_variant, 0);
1321 if (hr != WBEM_S_NO_ERROR) {
1322 WbemLogDebug(hr);
1323 goto release;
1324 }
1325 VariantClear(&param_variant);
1326
1327 /* LSOv1 */
1328 V_VT(&param_variant) = VT_BSTR;
1329 V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);
1330 if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4) != 0) {
1331 if (enable) {
1332 V_BSTR(&param_variant) =
1333 utob(NDIS_OFFLOAD_PARAMETERS_LSOV1_ENABLED);
1334 } else {
1335 V_BSTR(&param_variant) =
1336 utob(NDIS_OFFLOAD_PARAMETERS_LSOV1_DISABLED);
1337 }
1338 }
1339 hr = ndis_tcp_offload_parameters->lpVtbl->Put(
1340 ndis_tcp_offload_parameters, L"LsoV1", 0, &param_variant, 0);
1341 if (hr != WBEM_S_NO_ERROR) {
1342 WbemLogDebug(hr);
1343 goto release;
1344 }
1345 VariantClear(&param_variant);
1346
1347 /* LSOv2 IPv4 */
1348 V_VT(&param_variant) = VT_BSTR;
1349 V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);
1350 if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4) != 0) {
1351 if (enable) {
1352 V_BSTR(&param_variant) =
1353 utob(NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED);
1354 } else {
1355 V_BSTR(&param_variant) =
1356 utob(NDIS_OFFLOAD_PARAMETERS_LSOV2_DISABLED);
1357 }
1358 }
1359 hr = ndis_tcp_offload_parameters->lpVtbl->Put(
1360 ndis_tcp_offload_parameters, L"LsoV2IPv4", 0, &param_variant, 0);
1361 if (hr != WBEM_S_NO_ERROR) {
1362 WbemLogDebug(hr);
1363 goto release;
1364 }
1365 VariantClear(&param_variant);
1366
1367 /* LSOv2 IPv4 */
1368 V_VT(&param_variant) = VT_BSTR;
1369 V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);
1370 if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6) != 0) {
1371 if (enable) {
1372 V_BSTR(&param_variant) =
1373 utob(NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED);
1374 } else {
1375 V_BSTR(&param_variant) =
1376 utob(NDIS_OFFLOAD_PARAMETERS_LSOV2_DISABLED);
1377 }
1378 }
1379 hr = ndis_tcp_offload_parameters->lpVtbl->Put(
1380 ndis_tcp_offload_parameters, L"LsoV2IPv6", 0, &param_variant, 0);
1381 if (hr != WBEM_S_NO_ERROR) {
1382 WbemLogDebug(hr);
1383 goto release;
1384 }
1385 VariantClear(&param_variant);
1386
1387 /* currently unused fields */
1388 V_VT(&param_variant) = VT_BSTR;
1389 V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);
1390 hr = ndis_tcp_offload_parameters->lpVtbl->Put(
1391 ndis_tcp_offload_parameters, L"IPSec", 0, &param_variant, 0);
1392 if (hr != WBEM_S_NO_ERROR) {
1393 WbemLogDebug(hr);
1394 goto release;
1395 }
1396 hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters,
1397 L"TcpConnectionIPv4", 0,
1398 &param_variant, 0);
1399 if (hr != WBEM_S_NO_ERROR) {
1400 WbemLogDebug(hr);
1401 goto release;
1402 }
1403 hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters,
1404 L"TcpConnectionIPv6", 0,
1405 &param_variant, 0);
1406 if (hr != WBEM_S_NO_ERROR) {
1407 WbemLogDebug(hr);
1408 goto release;
1409 }
1410 hr = ndis_tcp_offload_parameters->lpVtbl->Put(
1411 ndis_tcp_offload_parameters, L"Flags", 0, &param_variant, 0);
1412 if (hr != WBEM_S_NO_ERROR) {
1413 WbemLogDebug(hr);
1414 goto release;
1415 }
1416 /* further fields are for NDIS 6.1+ */
1417
1418release:
1419 VariantClear(&param_variant);
1420
1421 return hr;
1422}
1423
1424static HRESULT SetNdisOffload(LPCWSTR if_description, uint32_t offload_flags,
1425 bool enable)
1426{
1427 HRESULT hr = S_OK;
1428
1429 ComInstance instance = {};
1430 WbemMethod method = {};
1431 WbemMethodCall call = {};
1432
1433 /* param 0 */
1434 IWbemClassObject *ndis_method_header = NULL;
1435 /* param 1 */
1436 IWbemClassObject *ndis_tcp_offload_parameters = NULL;
1437
1438 if (if_description == NULL) {
1439 SCLogWarning("No description specified for device");
1440 return E_INVALIDARG;
1441 }
1442
1443 LPCWSTR class_name = L"MSNdis_SetTcpOffloadParameters";
1444 LPCWSTR instance_name_fmt = L"%s=\"%s\"";
1445 size_t n_chars = wcslen(class_name) + wcslen(if_description) +
1446 wcslen(instance_name_fmt);
1447 LPWSTR instance_name = SCMalloc((n_chars + 1) * sizeof(wchar_t));
1448 if (instance_name == NULL) {
1449 SCLogWarning("Failed to allocate buffer for instance path");
1450 goto release;
1451 }
1452 instance_name[n_chars] = 0; /* defensively null-terminate */
1453 hr = StringCchPrintfW(instance_name, n_chars, instance_name_fmt, class_name,
1454 if_description);
1455 if (hr != S_OK) {
1456 SCLogWarning("Failed to format WMI class instance name: 0x%" PRIx32, (uint32_t)hr);
1457 goto release;
1458 }
1459
1460 /* method name */
1461 LPCWSTR method_name = L"WmiSetTcpOffloadParameters";
1462
1463 /* connect to COM/WMI */
1464 hr = ComInstanceInit(&instance, L"ROOT\\WMI");
1465 if (hr != S_OK) {
1466 goto release;
1467 }
1468
1469 /* obtain method */
1470 hr = GetWbemMethod(&instance, class_name, method_name, &method);
1471 if (hr != S_OK) {
1472 goto release;
1473 }
1474
1475 /* make parameter instances */
1476 hr = GetWbemMethodCall(&method, instance_name, &call);
1477 if (hr != S_OK) {
1478 goto release;
1479 }
1480
1481 /* build parameters */
1482
1483 VARIANT param_variant;
1484 VariantInit(&param_variant);
1485
1486 /* Make MSNdis_WmiMethodHeader */
1487 hr = BuildNdisWmiMethodHeader(&instance, 0, 0, 0, 5, &ndis_method_header);
1488 if (hr != WBEM_S_NO_ERROR) {
1489 goto release;
1490 }
1491
1492 V_VT(&param_variant) = VT_UNKNOWN;
1493 V_UNKNOWN(&param_variant) = NULL;
1494 hr = GetIUnknown(ndis_method_header, &V_UNKNOWN(&param_variant));
1495 if (hr != WBEM_S_NO_ERROR) {
1496 goto release;
1497 }
1498 hr = call.in_params->lpVtbl->Put(call.in_params, L"MethodHeader", 0,
1499 &param_variant, 0);
1500 VariantClear(&param_variant);
1501 if (hr != WBEM_S_NO_ERROR) {
1502 Win32HResultLogDebug(hr);
1503 goto release;
1504 }
1505
1506 /* Make MSNdis_TcpOffloadParameters */
1507 hr = BuildNdisTcpOffloadParameters(&instance, offload_flags, enable,
1508 &ndis_tcp_offload_parameters);
1509 if (hr != WBEM_S_NO_ERROR) {
1510 goto release;
1511 }
1512
1513 V_VT(&param_variant) = VT_UNKNOWN;
1514 V_UNKNOWN(&param_variant) = NULL;
1515 hr = GetIUnknown(ndis_tcp_offload_parameters, &V_UNKNOWN(&param_variant));
1516 if (hr != WBEM_S_NO_ERROR) {
1517 goto release;
1518 }
1519 hr = call.in_params->lpVtbl->Put(call.in_params, L"TcpOffloadParameters", 0,
1520 &param_variant, 0);
1521 VariantClear(&param_variant);
1522 if (hr != WBEM_S_NO_ERROR) {
1523 Win32HResultLogDebug(hr);
1524 goto release;
1525 }
1526
1527 /* execute the method */
1528 hr = WbemMethodCallExec(&call, NULL);
1529 if (hr != S_OK) {
1530 Win32HResultLogDebug(hr);
1531 goto release;
1532 }
1533
1534release:
1535 ReleaseObject(ndis_tcp_offload_parameters);
1536 ReleaseObject(ndis_method_header);
1537
1538 WbemMethodCallRelease(&call);
1539 WbemMethodRelease(&method);
1540 ComInstanceRelease(&instance);
1541
1542 return hr;
1543}
1544
1545int DisableIfaceOffloadingWin32(LiveDevice *ldev, int csum, int other)
1546{
1547 SCLogDebug("Disabling offloading for device %s", ldev->dev);
1548
1549 int ret = 0;
1550 DWORD err = NO_ERROR;
1551 uint32_t offload_flags = 0;
1552
1553 if (ldev == NULL) {
1554 return -1;
1555 }
1556
1557 /* WMI uses the description as an identifier... */
1558 IP_ADAPTER_ADDRESSES *if_info_list = NULL, *if_info = NULL;
1559 err = Win32GetAdaptersAddresses(&if_info_list);
1560 if (err != NO_ERROR) {
1561 ret = -1;
1562 goto release;
1563 }
1564 err = Win32FindAdapterAddresses(if_info_list, ldev->dev, &if_info);
1565 if (err != NO_ERROR) {
1566 ret = -1;
1567 goto release;
1568 }
1569 LPWSTR if_description = if_info->Description;
1570
1571 err = GetNdisOffload(if_description, &offload_flags);
1572 if (err != S_OK) {
1573 ret = -1;
1574 goto release;
1575 }
1576
1577 if (!csum) {
1578 offload_flags &= ~WIN32_TCP_OFFLOAD_FLAG_CSUM;
1579 }
1580 if (!other) {
1581 offload_flags &= ~WIN32_TCP_OFFLOAD_FLAG_LSO;
1582 }
1583
1584 err = SetNdisOffload(if_description, offload_flags, 0);
1585 if (err != S_OK) {
1586 ret = -1;
1587 goto release;
1588 }
1589
1590release:
1591 SCFree(if_info_list);
1592
1593 return ret;
1594}
1595
1596int RestoreIfaceOffloadingWin32(LiveDevice *ldev)
1597{
1598 SCLogDebug("Enabling offloading for device %s", ldev->dev);
1599
1600 int ret = 0;
1601 DWORD err = NO_ERROR;
1602
1603 if (ldev == NULL) {
1604 return -1;
1605 }
1606
1607 /* WMI uses the description as an identifier... */
1608 IP_ADAPTER_ADDRESSES *if_info_list = NULL, *if_info = NULL;
1609 err = Win32GetAdaptersAddresses(&if_info_list);
1610 if (err != NO_ERROR) {
1611 ret = -1;
1612 goto release;
1613 }
1614 err = Win32FindAdapterAddresses(if_info_list, ldev->dev, &if_info);
1615 if (err != NO_ERROR) {
1616 ret = -1;
1617 goto release;
1618 }
1619 LPWSTR if_description = if_info->Description;
1620
1621 err = SetNdisOffload(if_description, ldev->offload_orig, 1);
1622 if (err != S_OK) {
1623 ret = -1;
1624 goto release;
1625 }
1626
1627release:
1628 SCFree(if_info_list);
1629
1630 return ret;
1631}
1632
1633#endif /* NTDDI_VERSION >= NTDDI_VISTA */
1634
1635#ifdef UNITTESTS
1636static int Win32TestStripPcapPrefix(void)
1637{
1638 int result = 1;
1639
1640 const char *name1 = "\\Device\\NPF_{D4A32435-1BA7-4008-93A6-1518AA4BBD9B}";
1641 const char *expect_name1 = "{D4A32435-1BA7-4008-93A6-1518AA4BBD9B}";
1642
1643 const char *name2 = "{D4A32435-1BA7-4008-93A6-1518AA4BBD9B}";
1644 const char *expect_name2 = "{D4A32435-1BA7-4008-93A6-1518AA4BBD9B}";
1645
1646 result &= (strncmp(expect_name1, StripPcapPrefix(name1),
1647 strlen(expect_name1)) == 0);
1648
1649 result &= (strncmp(expect_name2, StripPcapPrefix(name2),
1650 strlen(expect_name2)) == 0);
1651
1652 return result;
1653}
1654#endif /* UNITTESTS */
1655
1656void Win32SyscallRegisterTests(void)
1657{
1658#ifdef UNITTESTS
1659 UtRegisterTest("Win32TestStripPcapPrefix", Win32TestStripPcapPrefix);
1660#endif
1661}
1662
1663#endif /* OS_WIN32 */
uint8_t flags
Definition decode-gre.h:0
uint16_t type
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
const char * name
void SCLog(int x, const char *file, const char *func, const int line, const char *module, const char *fmt,...)
Definition util-debug.c:723
SCLogLevel
The various log levels NOTE: when adding new level, don't forget to update SCLogMapLogLevelToSyslogLe...
Definition util-debug.h:32
@ SC_LOG_DEBUG
Definition util-debug.h:41
#define SCLogPerf(...)
Definition util-debug.h:234
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition util-debug.h:255
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition util-debug.h:225
#define SCMalloc(sz)
Definition util-mem.h:47
#define SCFree(p)
Definition util-mem.h:61