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.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  */
67 static 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  */
77 uint32_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 
101 uint32_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 
130 int GetIfaceMTUWin32(const char *pcap_dev) { return 0; }
131 int GetGlobalMTUWin32(void) { return 0; }
132 
133 int 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 }
140 int 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 }
147 int 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 
157 static HMODULE wmiutils_dll = NULL;
158 
159 /**
160  * \brief obtain the WMI utilities DLL
161  */
162 static 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  */
175 static 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  */
188 const 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  */
216 static 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 
236 static 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 
269 release:
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  */
278 int 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 
298 release:
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  */
317 int 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 
347 fail:
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 
369 typedef 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 
393 typedef struct ComInstance_ {
394  IWbemLocator *locator;
395  IWbemServices *services;
396 } ComInstance;
397 
398 /**
399  * \brief Creates a COM instance connected to the specified resource
400  */
401 static 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 
449 release:
450  SysFreeString(resource_bstr);
451 
452  return hr;
453 }
454 
455 /**
456  * \brief Releases resources for a COM instance.
457  */
458 static 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  */
470 static 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 
501 release:
502  SysFreeString(name_bstr);
503 
504  return hr;
505 }
506 
507 /**
508  * \brief spawns an empty class instance of the specified type
509  */
510 static 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 
529 release:
530  return hr;
531 }
532 
533 typedef 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  */
544 static 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 
580 release:
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  */
591 static 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 
602 typedef 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  */
613 static 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 
635 release:
636  return hr;
637 }
638 
639 /**
640  * \brief releases the WMI method call resources
641  */
642 static 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  */
655 static 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 
670 release:
671  return hr;
672 }
673 
674 /**
675  * Obtains an IWbemClassObject named property of a parent IWbemClassObject
676  */
677 static 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 
697 release:
698  VariantClear(&out_var);
699  return hr;
700 }
701 
702 /**
703  * Obtains an Encapsulation value from an MSNdis_WmiOffload property
704  */
705 static 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 
734 release:
735  VariantClear(&out_var);
736  ReleaseObject(subcategory_object);
737  ReleaseObject(category_object);
738  return hr;
739 }
740 
741 static 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 
758 release:
759  return hr;
760 }
761 
762 static 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 
820 release:
821  return hr;
822 }
823 
824 static 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 
926 release:
927  ReleaseObject(ndis_object_header);
928 
929  return hr;
930 }
931 
932 /**
933  * \brief polls the NDIS TCP offloading status, namely LSOv1/v2
934  */
935 static 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 
1108 release:
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 
1120 int 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 
1183 release:
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 
1197 static HRESULT
1198 BuildNdisTcpOffloadParameters(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 
1418 release:
1419  VariantClear(&param_variant);
1420 
1421  return hr;
1422 }
1423 
1424 static 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 
1534 release:
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 
1545 int 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 
1590 release:
1591  SCFree(if_info_list);
1592 
1593  return ret;
1594 }
1595 
1596 int 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 
1627 release:
1628  SCFree(if_info_list);
1629 
1630  return ret;
1631 }
1632 
1633 #endif /* NTDDI_VERSION >= NTDDI_VISTA */
1634 
1635 #ifdef UNITTESTS
1636 static 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 
1656 void Win32SyscallRegisterTests(void)
1657 {
1658 #ifdef UNITTESTS
1659  UtRegisterTest("Win32TestStripPcapPrefix", Win32TestStripPcapPrefix);
1660 #endif
1661 }
1662 
1663 #endif /* OS_WIN32 */
win32-syscall.h
SCLog
void SCLog(int x, const char *file, const char *func, const int line, const char *module, const char *fmt,...)
Definition: util-debug.c:727
SC_LOG_DEBUG
@ SC_LOG_DEBUG
Definition: util-debug.h:57
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:269
LiveDevice_
Definition: util-device.h:50
util-unittest.h
util-device.h
util-debug.h
LiveDevice_::dev
char * dev
Definition: util-device.h:51
SCLogWarning
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition: util-debug.h:249
type
uint16_t type
Definition: decode-vlan.c:107
LiveDevice_::offload_orig
uint32_t offload_orig
Definition: util-device.h:65
SCLogInfo
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:224
SCLogLevel
SCLogLevel
The various log levels NOTE: when adding new level, don't forget to update SCLogMapLogLevelToSyslogLe...
Definition: util-debug.h:48
util-mem.h
flags
uint8_t flags
Definition: decode-gre.h:0
suricata-common.h
SCLogPerf
#define SCLogPerf(...)
Definition: util-debug.h:230
SCMalloc
#define SCMalloc(sz)
Definition: util-mem.h:47
SCFree
#define SCFree(p)
Definition: util-mem.h:61
suricata.h