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