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