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