diff options
Diffstat (limited to 'src/win32-syscall.c')
-rw-r--r-- | src/win32-syscall.c | 1663 |
1 files changed, 1663 insertions, 0 deletions
diff --git a/src/win32-syscall.c b/src/win32-syscall.c new file mode 100644 index 0000000..4e9145e --- /dev/null +++ b/src/win32-syscall.c @@ -0,0 +1,1663 @@ +/* Copyright (C) 2018 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Jacob Masen-Smith <jacob@evengx.com> + * + * Isolation for WMI/COM functionality + * + * References: + * https://msdn.microsoft.com/en-us/library/aa390421(v=vs.85).aspx + * https://blogs.msdn.microsoft.com/ndis/2015/03/21/mapping-from-ndis-oids-to-wmi-classes/ + * https://stackoverflow.com/questions/1431103/how-to-obtain-data-from-wmi-using-a-c-application + * https://docs.microsoft.com/en-us/windows-hardware/drivers/network/oid-tcp-offload-parameters + * https://wutils.com/wmi/root/wmi/ms_409/msndis_tcpoffloadcurrentconfig/ + * https://docs.microsoft.com/en-us/windows-hardware/drivers/network/oid-tcp-offload-current-config + * https://wutils.com/wmi/root/wmi/msndis_tcpoffloadparameters/ + */ + +#ifdef OS_WIN32 + +#include <inttypes.h> +#include <stdbool.h> + +// clang-format off +#include <winsock2.h> +#include <windows.h> +#include <wbemidl.h> +#include <strsafe.h> +#include <ntddndis.h> +#include <ws2ipdef.h> +#include <iphlpapi.h> +// clang-format on + +/* Windows strsafe.h defines _snprintf as an undefined warning type */ +#undef _snprintf +#define _snprintf StringCbPrintfA + +#include "suricata-common.h" +#include "util-debug.h" +#include "util-device.h" +#include "util-mem.h" +#include "util-unittest.h" + +#include "suricata.h" + +#include "win32-syscall.h" + +/** + * \brief return only the GUID portion of the name + */ +static const char *StripPcapPrefix(const char *pcap_dev) +{ + return strchr(pcap_dev, '{'); +} + +/** + * \brief get the adapter address list, which includes IP status/details + * + * Clients MUST FREE the returned list to avoid memory leaks. + */ +uint32_t Win32GetAdaptersAddresses(IP_ADAPTER_ADDRESSES **pif_info_list) +{ + DWORD err = NO_ERROR; + IP_ADAPTER_ADDRESSES *if_info_list; + + ULONG size = 0; + err = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &size); + if (err != ERROR_BUFFER_OVERFLOW) { + return err; + } + if_info_list = SCMalloc((size_t)size); + if (if_info_list == NULL) { + return ERROR_NOT_ENOUGH_MEMORY; + } + err = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, if_info_list, &size); + if (err != NO_ERROR) { + SCFree(if_info_list); + return err; + } + + *pif_info_list = if_info_list; + return NO_ERROR; +} + +uint32_t Win32FindAdapterAddresses(IP_ADAPTER_ADDRESSES *if_info_list, + const char *adapter_name, + IP_ADAPTER_ADDRESSES **pif_info) +{ + DWORD ret = NO_ERROR; + adapter_name = StripPcapPrefix(adapter_name); + *pif_info = NULL; + + for (IP_ADAPTER_ADDRESSES *current = if_info_list; current != NULL; + current = current->Next) { + + /* if we find the adapter, return that data */ + if (strncmp(adapter_name, current->AdapterName, strlen(adapter_name)) == + 0) { + + *pif_info = current; + break; + } + } + + if (*pif_info == NULL) { + ret = ERROR_NOT_FOUND; + } + + return ret; +} + +#if NTDDI_VERSION < NTDDI_VISTA + +int GetIfaceMTUWin32(const char *pcap_dev) { return 0; } +int GetGlobalMTUWin32(void) { return 0; } + +int GetIfaceOffloadingWin32(const char *ifname, int csum, int other) +{ + SCLogWarning("Suricata not targeted for Windows Vista or " + "higher. Network offload interrogation not " + "available."); + return -1; +} +int DisableIfaceOffloadingWin32(LiveDevice *ldev, int csum, int other) +{ + SCLogWarning("Suricata not targeted for Windows Vista or " + "higher. Network offload interrogation not " + "available."); + return -1; +} +int RestoreIfaceOffloadingWin32(LiveDevice *ldev) +{ + SCLogWarning("Suricata not targeted for Windows Vista or " + "higher. Network offload interrogation not " + "available."); + return -1; +} + +#else /* NTDDI_VERSION >= NTDDI_VISTA */ + +static HMODULE wmiutils_dll = NULL; + +/** + * \brief obtain the WMI utilities DLL + */ +static HMODULE WmiUtils(void) +{ + if (wmiutils_dll == NULL) { + wmiutils_dll = + LoadLibraryA("C:\\Windows\\System32\\wbem\\wmiutils.dll"); + } + + return wmiutils_dll; +} + +/** + * \brief allocate a BSTR from a converted unsigned integer + */ +static BSTR utob(uint64_t ui) +{ + wchar_t buf[20]; + _ui64tow(ui, buf, 10); + return SysAllocString(buf); +} + +/** + * \brief Get the win32/wmi error string + * + * The caller should use the LocalFree function on the returned pointer to free + * the buffer when it is no longer needed. + */ +const char *Win32GetErrorString(DWORD error_code, HMODULE ext_module) +{ + char *error_string = NULL; + + DWORD flags = + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS; + if (ext_module != NULL) { + flags |= FORMAT_MESSAGE_FROM_HMODULE; + } else { + flags |= FORMAT_MESSAGE_FROM_SYSTEM; + } + + FormatMessageA(flags, ext_module, error_code, 0, (LPTSTR)&error_string, 0, + NULL); + + if (error_string == NULL) { + return ""; + } + + error_string[strlen(error_string) - 2] = 0; // remove line breaks + + return error_string; +} + +#ifdef DEBUG +/** + * \brief log an HRESULT + */ +static void _Win32HResultLog(SCLogLevel level, HRESULT hr, const char *file, + const char *function, const int line) +{ + const char *err_str = Win32GetErrorString(hr, WmiUtils()); + SCLog(level, file, function, line, "HRESULT: %s (0x%08" PRIx32 ")", err_str, + (uint32_t)(hr)); + LocalFree((LPVOID)err_str); +} + +#define Win32HResultLogDebug(hr) \ + _Win32HResultLog(SC_LOG_DEBUG, (hr), __FILE__, __FUNCTION__, __LINE__) +#else +#define Win32HResultLogDebug(hr) +#endif /* DEBUG */ + +/** + * \brief log a WBEM error + */ +#define WbemLogDebug(hr) (_WbemLogDebug)((hr), __FILE__, __FUNCTION__, __LINE__) + +static void _WbemLogDebug(HRESULT hr, const char *file, const char *function, + const int line) +{ +#ifdef DEBUG + IErrorInfo *err_info; + BSTR err_description; + char *err_description_mb = NULL; + + _Win32HResultLog(SC_LOG_DEBUG, hr, file, function, line); + + GetErrorInfo(0, &err_info); + if (!SUCCEEDED( + err_info->lpVtbl->GetDescription(err_info, &err_description))) { + // not much to do when your error log errors out... + goto release; + } + + err_description_mb = SCMalloc(SysStringLen(err_description) + 1); + + if (err_description_mb == NULL) { + // not much to do when your error log errors out... + goto release; + } + + // do the actual multibyte conversion + err_description_mb[SysStringLen(err_description)] = 0; + wcstombs(err_description_mb, err_description, + SysStringLen(err_description)); + + // log the description + SCLog(SC_LOG_DEBUG, file, function, line, "WBEM error: %s", + err_description_mb); + +release: + SCFree(err_description_mb); + SysFreeString(err_description); +#endif /* DEBUG */ +} + +/** + * \brief get the maximum transmissible unit for the specified pcap device name + */ +int GetIfaceMTUWin32(const char *pcap_dev) +{ + DWORD err = NO_ERROR; + + int mtu = 0; + + IP_ADAPTER_ADDRESSES *if_info_list = NULL, *if_info = NULL; + err = Win32GetAdaptersAddresses(&if_info_list); + if (err != NO_ERROR) { + mtu = -1; + goto release; + } + err = Win32FindAdapterAddresses(if_info_list, pcap_dev, &if_info); + if (err != NO_ERROR) { + mtu = -1; + goto release; + } + + mtu = if_info->Mtu; + +release: + SCFree(if_info_list); + + if (err != S_OK) { + const char *errbuf = Win32GetErrorString(err, WmiUtils()); + SCLogWarning("Failure when trying to get MTU via syscall for '%s': %s " + "(0x%08" PRIx32 ")", + pcap_dev, errbuf, (uint32_t)err); + LocalFree((LPVOID)errbuf); + } else { + SCLogInfo("Found an MTU of %d for '%s'", mtu, pcap_dev); + } + + return mtu; +} + +/** + * \brief get the maximum transmissible unit for all devices on the system + */ +int GetGlobalMTUWin32(void) +{ + uint32_t mtu = 0; + + DWORD err = NO_ERROR; + IP_ADAPTER_ADDRESSES *if_info_list = NULL; + + /* get a list of all adapters' data */ + err = Win32GetAdaptersAddresses(&if_info_list); + if (err != NO_ERROR) { + goto fail; + } + + /* now search for the right adapter in the list */ + IP_ADAPTER_ADDRESSES *if_info = NULL; + for (if_info = if_info_list; if_info != NULL; if_info = if_info->Next) { + /* -1 (uint) is an invalid value */ + if (if_info->Mtu == (uint32_t)-1) { + continue; + } + + /* we want to return the largest MTU value so we allocate enough */ + mtu = max(mtu, if_info->Mtu); + } + + SCFree(if_info_list); + + SCLogInfo("Found a global MTU of %" PRIu32, mtu); + return (int)mtu; + +fail: + SCFree(if_info_list); + + const char *errbuf = NULL; + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, 0, (LPTSTR)&errbuf, 0, NULL); + + SCLogWarning("Failure when trying to get global MTU via syscall: %s (%" PRId32 ")", errbuf, + (uint32_t)err); + + return -1; +} + +#define ReleaseObject(objptr) \ + do { \ + if ((objptr) != NULL) { \ + (objptr)->lpVtbl->Release(objptr); \ + (objptr) = NULL; \ + } \ + } while (0); + +typedef enum Win32TcpOffloadFlags_ { + WIN32_TCP_OFFLOAD_FLAG_NONE = 0, + WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX = 1, + WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX = 1 << 1, + WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX = 1 << 2, + WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX = 1 << 3, + WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4 = 1 << 4, + WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4 = 1 << 5, + WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6 = 1 << 6, + + /* aggregates */ + WIN32_TCP_OFFLOAD_FLAG_CSUM = WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX | + WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX | + WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX | + WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX, + WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4 = WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX | + WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX, + WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6 = WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX | + WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX, + WIN32_TCP_OFFLOAD_FLAG_LSO = WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4 | + WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4 | + WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6, +} Win32TcpOffloadFlags; + +typedef struct ComInstance_ { + IWbemLocator *locator; + IWbemServices *services; +} ComInstance; + +/** + * \brief Creates a COM instance connected to the specified resource + */ +static HRESULT ComInstanceInit(ComInstance *instance, LPCWSTR resource) +{ + HRESULT hr = S_OK; + + instance->locator = NULL; + instance->services = NULL; + + BSTR resource_bstr = SysAllocString(resource); + if (resource_bstr == NULL) { + hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY); + SCLogWarning("Failed to allocate BSTR"); + goto release; + } + + /* connect to COM */ + hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if (hr == S_FALSE) { + /* already initialized */ + hr = S_OK; + } else { + if (hr != S_OK) { + SCLogWarning("COM CoInitializeEx failed: 0x%" PRIx32, (uint32_t)hr); + goto release; + } + hr = CoInitializeSecurity( + NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, + RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL); + if (hr != S_OK) { + SCLogWarning("COM CoInitializeSecurity failed: 0x%" PRIx32, (uint32_t)hr); + goto release; + } + } + + /* connect to WMI */ + hr = CoCreateInstance(&CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, + &IID_IWbemLocator, (LPVOID *)&instance->locator); + if (hr != S_OK) { + SCLogWarning("COM CoCreateInstance failed: 0x%" PRIx32, (uint32_t)hr); + goto release; + } + hr = instance->locator->lpVtbl->ConnectServer( + instance->locator, resource_bstr, NULL, NULL, NULL, 0, NULL, NULL, + &instance->services); + if (hr != S_OK) { + SCLogWarning("COM ConnectServer failed: 0x%" PRIx32, (uint32_t)hr); + goto release; + } + +release: + SysFreeString(resource_bstr); + + return hr; +} + +/** + * \brief Releases resources for a COM instance. + */ +static void ComInstanceRelease(ComInstance *instance) +{ + if (instance == NULL) { + return; + } + ReleaseObject(instance->services); + ReleaseObject(instance->locator); +} + +/** + * \brief obtains a class definition from COM services + */ +static HRESULT GetWbemClass(ComInstance *instance, LPCWSTR name, + IWbemClassObject **p_class) +{ + HRESULT hr = WBEM_S_NO_ERROR; + BSTR name_bstr = NULL; + + if (instance == NULL || name == NULL || p_class == NULL || + *p_class != NULL) { + hr = HRESULT_FROM_WIN32(E_INVALIDARG); + Win32HResultLogDebug(hr); + goto release; + } + + /* allocate name string */ + name_bstr = SysAllocString(name); + if (name_bstr == NULL) { + hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY); + SCLogWarning("Failed to allocate BSTR"); + goto release; + } + + /* obtain object */ + hr = instance->services->lpVtbl->GetObject(instance->services, name_bstr, + WBEM_FLAG_RETURN_WBEM_COMPLETE, + NULL, p_class, NULL); + if (hr != S_OK) { + WbemLogDebug(hr); + SCLogWarning("WMI GetObject failed: 0x%" PRIx32, (uint32_t)hr); + goto release; + } + +release: + SysFreeString(name_bstr); + + return hr; +} + +/** + * \brief spawns an empty class instance of the specified type + */ +static HRESULT GetWbemClassInstance(ComInstance *instance, LPCWSTR name, + IWbemClassObject **p_instance) +{ + HRESULT hr = WBEM_S_NO_ERROR; + + IWbemClassObject *class = NULL; + + hr = GetWbemClass(instance, name, &class); + if (hr != WBEM_S_NO_ERROR) { + goto release; + } + + hr = class->lpVtbl->SpawnInstance(class, 0, p_instance); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + SCLogWarning("WMI SpawnInstance failed: 0x%" PRIx32, (uint32_t)hr); + goto release; + } + +release: + return hr; +} + +typedef struct WbemMethod_ { + ComInstance *com_instance; + + BSTR method_name; + + IWbemClassObject *in_params, *out_params; +} WbemMethod; + +/** + * \brief initializes resources for a WMI method handle + */ +static HRESULT GetWbemMethod(ComInstance *com_instance, LPCWSTR class_name, + LPCWSTR method_name, WbemMethod *method) +{ + HRESULT hr = S_OK; + IWbemClassObject *class = NULL; + + method->com_instance = com_instance; + + BSTR class_name_bstr = SysAllocString(class_name); + if (class_name_bstr == NULL) { + hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY); + SCLogWarning("Failed to allocate BSTR"); + goto release; + } + method->method_name = SysAllocString(method_name); + if (method->method_name == NULL) { + hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY); + SCLogWarning("Failed to allocate BSTR"); + goto release; + } + + /* find our class definition to retrieve parameters */ + hr = GetWbemClass(com_instance, class_name, &class); + if (hr != WBEM_S_NO_ERROR) { + goto release; + } + + /* find the method on the retrieved class */ + hr = class->lpVtbl->GetMethod(class, method_name, 0, &method->in_params, + &method->out_params); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + SCLogWarning("WMI GetMethod failed: 0x%" PRIx32, (uint32_t)hr); + goto release; + } + +release: + ReleaseObject(class); + + SysFreeString(class_name_bstr); + + return hr; +} + +/** + * \brief Releases resources for a WMI method handle + */ +static void WbemMethodRelease(WbemMethod *method) +{ + if (method == NULL) { + return; + } + ReleaseObject(method->in_params); + ReleaseObject(method->out_params); + + SysFreeString(method->method_name); +} + +typedef struct WbemMethodCall_ { + WbemMethod *method; + + BSTR instance_path; + + IWbemClassObject *in_params; +} WbemMethodCall; + +/** + * \brief generates a single-use WMI method call + */ +static HRESULT GetWbemMethodCall(WbemMethod *method, LPCWSTR instance_path, + WbemMethodCall *call) +{ + HRESULT hr = S_OK; + + call->method = method; + call->instance_path = SysAllocString(instance_path); + if (call->instance_path == NULL) { + hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY); + SCLogWarning("Failed to allocate BSTR: 0x%" PRIx32, (uint32_t)hr); + goto release; + } + + /* make an instance of the in/out params */ + hr = method->in_params->lpVtbl->SpawnInstance(method->in_params, 0, + &call->in_params); + if (hr != S_OK) { + WbemLogDebug(hr); + SCLogWarning("WMI SpawnInstance failed on in_params: 0x%" PRIx32, (uint32_t)hr); + goto release; + } + +release: + return hr; +} + +/** + * \brief releases the WMI method call resources + */ +static void WbemMethodCallRelease(WbemMethodCall *call) +{ + if (call == NULL) { + return; + } + ReleaseObject(call->in_params); + + SysFreeString(call->instance_path); +} + +/** + * \brief executes the method after the client has set applicable parameters. + */ +static HRESULT WbemMethodCallExec(WbemMethodCall *call, + IWbemClassObject **p_out_params) +{ + HRESULT hr = S_OK; + + hr = call->method->com_instance->services->lpVtbl->ExecMethod( + call->method->com_instance->services, call->instance_path, + call->method->method_name, 0, NULL, call->in_params, p_out_params, + NULL); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + SCLogDebug("WMI ExecMethod failed: 0x%" PRIx32, (uint32_t)hr); + goto release; + } + +release: + return hr; +} + +/** + * Obtains an IWbemClassObject named property of a parent IWbemClassObject + */ +static HRESULT WbemGetSubObject(IWbemClassObject *object, LPCWSTR property_name, + IWbemClassObject **sub_object) +{ + HRESULT hr = S_OK; + + VARIANT out_var; + VariantInit(&out_var); + hr = object->lpVtbl->Get(object, property_name, 0, &out_var, NULL, NULL); + if (hr != WBEM_S_NO_ERROR) { + goto release; + } + + IUnknown *unknown = V_UNKNOWN(&out_var); + hr = unknown->lpVtbl->QueryInterface(unknown, &IID_IWbemClassObject, + (void **)sub_object); + if (hr != S_OK) { + SCLogWarning("WMI QueryInterface (IWbemClassObject) failed: 0x%" PRIx32, (uint32_t)hr); + goto release; + } + +release: + VariantClear(&out_var); + return hr; +} + +/** + * Obtains an Encapsulation value from an MSNdis_WmiOffload property + */ +static HRESULT GetEncapsulation(IWbemClassObject *object, LPCWSTR category, + LPCWSTR subcategory, ULONG *encapsulation) +{ + HRESULT hr = WBEM_S_NO_ERROR; + + IWbemClassObject *category_object = NULL; + IWbemClassObject *subcategory_object = NULL; + + VARIANT out_var; + VariantInit(&out_var); + + /* get category object */ + hr = WbemGetSubObject(object, category, &category_object); + if (hr != WBEM_S_NO_ERROR) { + goto release; + } + + /* get sub-category object */ + hr = WbemGetSubObject(category_object, subcategory, &subcategory_object); + if (hr != WBEM_S_NO_ERROR) { + goto release; + } + hr = subcategory_object->lpVtbl->Get(subcategory_object, L"Encapsulation", + 0, &out_var, NULL, NULL); + if (hr != WBEM_S_NO_ERROR) { + goto release; + } + *encapsulation = V_UI4(&out_var); + +release: + VariantClear(&out_var); + ReleaseObject(subcategory_object); + ReleaseObject(category_object); + return hr; +} + +static HRESULT GetIUnknown(IWbemClassObject *object, IUnknown **p_unknown) +{ + HRESULT hr = WBEM_S_NO_ERROR; + + if (object == NULL || p_unknown == NULL || *p_unknown != NULL) { + hr = HRESULT_FROM_WIN32(E_INVALIDARG); + Win32HResultLogDebug(hr); + goto release; + } + + hr = object->lpVtbl->QueryInterface(object, &IID_IUnknown, + (void **)p_unknown); + if (hr != S_OK) { + SCLogWarning("WMI QueryInterface (IUnknown) failed: 0x%" PRIx32, (uint32_t)hr); + goto release; + } + +release: + return hr; +} + +static HRESULT BuildNdisObjectHeader(ComInstance *instance, uint8_t type, + uint8_t revision, uint16_t size, + IWbemClassObject **p_ndis_object_header) +{ + HRESULT hr = WBEM_S_NO_ERROR; + + if (instance == NULL || p_ndis_object_header == NULL || + *p_ndis_object_header != NULL) { + + hr = HRESULT_FROM_WIN32(E_INVALIDARG); + Win32HResultLogDebug(hr); + goto release; + } + + /* obtain object */ + hr = GetWbemClassInstance(instance, L"MSNdis_ObjectHeader", + p_ndis_object_header); + if (hr != WBEM_S_NO_ERROR) { + goto release; + } + + VARIANT param_variant; + VariantInit(¶m_variant); + IWbemClassObject *ndis_object_header = *p_ndis_object_header; + + /* set parameters */ + V_VT(¶m_variant) = VT_UI1; + V_UI1(¶m_variant) = type; + hr = ndis_object_header->lpVtbl->Put(ndis_object_header, L"Type", 0, + ¶m_variant, 0); + VariantClear(¶m_variant); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + + V_VT(¶m_variant) = VT_UI1; + V_UI1(¶m_variant) = revision; + hr = ndis_object_header->lpVtbl->Put(ndis_object_header, L"Revision", 0, + ¶m_variant, 0); + VariantClear(¶m_variant); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + + /* https://docs.microsoft.com/en-us/windows-hardware/drivers/network/ndis-object-version-issues-for-wmi + */ + V_VT(¶m_variant) = VT_I4; + V_I4(¶m_variant) = size; + hr = ndis_object_header->lpVtbl->Put(ndis_object_header, L"Size", 0, + ¶m_variant, 0); + VariantClear(¶m_variant); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + +release: + return hr; +} + +static HRESULT BuildNdisWmiMethodHeader(ComInstance *instance, + uint64_t net_luid, uint32_t port_number, + uint64_t request_id, uint32_t timeout, + IWbemClassObject **p_ndis_method_header) +{ + HRESULT hr = WBEM_S_NO_ERROR; + + IWbemClassObject *ndis_object_header = NULL; + + if (instance == NULL || p_ndis_method_header == NULL || + *p_ndis_method_header != NULL) { + + hr = HRESULT_FROM_WIN32(E_INVALIDARG); + Win32HResultLogDebug(hr); + goto release; + } + + /* obtain object */ + hr = GetWbemClassInstance(instance, L"MSNdis_WmiMethodHeader", + p_ndis_method_header); + if (hr != WBEM_S_NO_ERROR) { + goto release; + } + + VARIANT param_variant; + VariantInit(¶m_variant); + + /* get embedded MSNdis_ObjectHeader */ + hr = BuildNdisObjectHeader(instance, NDIS_WMI_OBJECT_TYPE_METHOD, + NDIS_WMI_METHOD_HEADER_REVISION_1, 0xFFFF, + &ndis_object_header); + if (hr != WBEM_S_NO_ERROR) { + goto release; + } + V_VT(¶m_variant) = VT_UNKNOWN; + V_UNKNOWN(¶m_variant) = NULL; + hr = GetIUnknown(ndis_object_header, &V_UNKNOWN(¶m_variant)); + if (hr != WBEM_S_NO_ERROR) { + goto release; + } + + IWbemClassObject *ndis_method_header = *p_ndis_method_header; + + /* set parameters */ + hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"Header", 0, + ¶m_variant, 0); + VariantClear(¶m_variant); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + + V_VT(¶m_variant) = VT_BSTR; + V_BSTR(¶m_variant) = utob(net_luid); + hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"NetLuid", 0, + ¶m_variant, 0); + VariantClear(¶m_variant); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + + V_VT(¶m_variant) = VT_BSTR; + V_BSTR(¶m_variant) = utob((uint64_t)port_number); + hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"PortNumber", 0, + ¶m_variant, 0); + VariantClear(¶m_variant); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + + V_VT(¶m_variant) = VT_BSTR; + V_BSTR(¶m_variant) = utob(request_id); + hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"RequestId", 0, + ¶m_variant, 0); + VariantClear(¶m_variant); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + + V_VT(¶m_variant) = VT_BSTR; + V_BSTR(¶m_variant) = utob((uint64_t)timeout); + hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"Timeout", 0, + ¶m_variant, 0); + VariantClear(¶m_variant); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + + V_VT(¶m_variant) = VT_BSTR; + V_BSTR(¶m_variant) = utob((uint64_t)0); + hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"Padding", 0, + ¶m_variant, 0); + VariantClear(¶m_variant); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + +release: + ReleaseObject(ndis_object_header); + + return hr; +} + +/** + * \brief polls the NDIS TCP offloading status, namely LSOv1/v2 + */ +static HRESULT GetNdisOffload(LPCWSTR if_description, uint32_t *offload_flags) +{ + HRESULT hr = S_OK; + + ComInstance instance = {}; + WbemMethod method = {}; + WbemMethodCall call = {}; + + IWbemClassObject *ndis_method_header = NULL; + IWbemClassObject *out_params = NULL; + IWbemClassObject *ndis_offload = NULL; + + if (if_description == NULL) { + SCLogWarning("No description specified for device"); + hr = HRESULT_FROM_WIN32(E_INVALIDARG); + goto release; + } + + LPCWSTR class_name = L"MSNdis_TcpOffloadCurrentConfig"; + LPCWSTR instance_name_fmt = L"%s=\"%s\""; + size_t n_chars = wcslen(class_name) + wcslen(if_description) + + wcslen(instance_name_fmt); + LPWSTR instance_name = SCMalloc((n_chars + 1) * sizeof(wchar_t)); + if (instance_name == NULL) { + SCLogWarning("Failed to allocate buffer for instance path"); + goto release; + } + instance_name[n_chars] = 0; /* defensively null-terminate */ + hr = StringCchPrintfW(instance_name, n_chars, instance_name_fmt, class_name, + if_description); + if (hr != S_OK) { + SCLogWarning("Failed to format WMI class instance name: 0x%" PRIx32, (uint32_t)hr); + goto release; + } + /* method name */ + LPCWSTR method_name = L"WmiQueryCurrentOffloadConfig"; + + /* connect to COM/WMI */ + hr = ComInstanceInit(&instance, L"ROOT\\WMI"); + if (hr != S_OK) { + goto release; + } + + /* obtain method */ + hr = GetWbemMethod(&instance, class_name, method_name, &method); + if (hr != S_OK) { + goto release; + } + + /* make parameter instances */ + hr = GetWbemMethodCall(&method, instance_name, &call); + if (hr != S_OK) { + goto release; + } + + /* build parameters */ + + VARIANT param_variant; + VariantInit(¶m_variant); + + /* Make MSNdis_WmiMethodHeader */ + hr = BuildNdisWmiMethodHeader(&instance, 0, 0, 0, 5, &ndis_method_header); + if (hr != WBEM_S_NO_ERROR) { + goto release; + } + V_VT(¶m_variant) = VT_UNKNOWN; + V_UNKNOWN(¶m_variant) = NULL; + hr = GetIUnknown(ndis_method_header, &V_UNKNOWN(¶m_variant)); + if (hr != WBEM_S_NO_ERROR) { + goto release; + } + + /* Set in_params */ + hr = call.in_params->lpVtbl->Put(call.in_params, L"Header", 0, + ¶m_variant, 0); + VariantClear(¶m_variant); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + + /* execute the method */ + hr = WbemMethodCallExec(&call, &out_params); + if (hr != S_OK) { + size_t if_description_len = wcslen(if_description); + char *if_description_ansi = SCMalloc(if_description_len + 1); + if (if_description_ansi == NULL) { + SCLogWarning("Failed to allocate buffer for interface description"); + goto release; + } + if_description_ansi[if_description_len] = 0; + wcstombs(if_description_ansi, if_description, if_description_len); + SCLogInfo("Obtaining offload state failed, device \"%s\" may not " + "support offload. Error: 0x%" PRIx32, + if_description_ansi, (uint32_t)hr); + SCFree(if_description_ansi); + Win32HResultLogDebug(hr); + goto release; + } + + /* inspect the result */ + hr = WbemGetSubObject(out_params, L"Offload", &ndis_offload); + if (hr != WBEM_S_NO_ERROR) { + goto release; + } + ULONG encapsulation = 0; + + /* Checksum */ + hr = GetEncapsulation(ndis_offload, L"Checksum", L"IPv4Receive", + &encapsulation); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + if (encapsulation != 0) { + *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX; + } + hr = GetEncapsulation(ndis_offload, L"Checksum", L"IPv4Transmit", + &encapsulation); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + if (encapsulation != 0) { + *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX; + } + hr = GetEncapsulation(ndis_offload, L"Checksum", L"IPv6Receive", + &encapsulation); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + if (encapsulation != 0) { + *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX; + } + hr = GetEncapsulation(ndis_offload, L"Checksum", L"IPv6Transmit", + &encapsulation); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + if (encapsulation != 0) { + *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX; + } + + /* LsoV1 */ + hr = GetEncapsulation(ndis_offload, L"LsoV1", L"WmiIPv4", &encapsulation); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + if (encapsulation != 0) { + *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4; + } + + /* LsoV2 */ + hr = GetEncapsulation(ndis_offload, L"LsoV2", L"WmiIPv4", &encapsulation); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + if (encapsulation != 0) { + *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4; + } + hr = GetEncapsulation(ndis_offload, L"LsoV2", L"WmiIPv6", &encapsulation); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + if (encapsulation != 0) { + *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6; + } + +release: + ReleaseObject(ndis_method_header); + ReleaseObject(ndis_offload); + ReleaseObject(out_params); + + WbemMethodCallRelease(&call); + WbemMethodRelease(&method); + ComInstanceRelease(&instance); + + return hr; +} + +int GetIfaceOffloadingWin32(const char *pcap_dev, int csum, int other) +{ + SCLogDebug("Querying offloading for device %s", pcap_dev); + + DWORD err = NO_ERROR; + int ret = 0; + uint32_t offload_flags = 0; + + /* WMI uses the description as an identifier... */ + IP_ADAPTER_ADDRESSES *if_info_list = NULL, *if_info = NULL; + err = Win32GetAdaptersAddresses(&if_info_list); + if (err != NO_ERROR) { + ret = -1; + goto release; + } + err = Win32FindAdapterAddresses(if_info_list, pcap_dev, &if_info); + if (err != NO_ERROR) { + ret = -1; + goto release; + } + LPWSTR if_description = if_info->Description; + + /* now query WMI for the offload info */ + err = GetNdisOffload(if_description, &offload_flags); + if (err != S_OK) { + ret = -1; + goto release; + } else if (offload_flags != 0) { + if (csum == 1) { + if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM) != 0) { + ret = 1; + } + } + if (other == 1) { + if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSO) != 0) { + ret = 1; + } + } + } + + if (ret == 0) { + SCLogPerf("NIC offloading on %s: Checksum IPv4 Rx: %d Tx: %d IPv6 " + "Rx: %d Tx: %d LSOv1 IPv4: %d LSOv2 IPv4: %d IPv6: %d", + pcap_dev, + (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX) != 0, + (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX) != 0, + (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX) != 0, + (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX) != 0, + (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4) != 0, + (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4) != 0, + (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6) != 0); + } else { + SCLogWarning("NIC offloading on %s: Checksum IPv4 Rx: %d Tx: %d IPv6 " + "Rx: %d Tx: %d LSOv1 IPv4: %d LSOv2 IPv4: %d IPv6: %d", + pcap_dev, (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX) != 0, + (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX) != 0, + (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX) != 0, + (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX) != 0, + (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4) != 0, + (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4) != 0, + (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6) != 0); + } + +release: + if (ret == -1) { + const char *err_str = Win32GetErrorString(err, WmiUtils()); + SCLogWarning("Failure when trying to get feature via syscall for '%s': " + "%s (0x%08" PRIx32 ")", + pcap_dev, err_str, (uint32_t)err); + LocalFree((LPVOID)err_str); + } + + SCFree(if_info_list); + + return ret; +} + +static HRESULT +BuildNdisTcpOffloadParameters(ComInstance *instance, uint32_t offload_flags, + bool enable, + IWbemClassObject **p_ndis_tcp_offload_parameters) +{ + HRESULT hr = WBEM_S_NO_ERROR; + + IWbemClassObject *ndis_object_header = NULL; + + if (instance == NULL || p_ndis_tcp_offload_parameters == NULL || + *p_ndis_tcp_offload_parameters != NULL) { + + hr = HRESULT_FROM_WIN32(E_INVALIDARG); + Win32HResultLogDebug(hr); + goto release; + } + + /* obtain object */ + hr = GetWbemClassInstance(instance, L"MSNdis_TcpOffloadParameters", + p_ndis_tcp_offload_parameters); + if (hr != WBEM_S_NO_ERROR) { + goto release; + } + + VARIANT param_variant; + VariantInit(¶m_variant); + + /* get embedded MSNdis_ObjectHeader */ + hr = BuildNdisObjectHeader(instance, NDIS_OBJECT_TYPE_DEFAULT, + NDIS_OFFLOAD_PARAMETERS_REVISION_1, + NDIS_SIZEOF_OFFLOAD_PARAMETERS_REVISION_1, + &ndis_object_header); + if (hr != WBEM_S_NO_ERROR) { + goto release; + } + V_VT(¶m_variant) = VT_UNKNOWN; + V_UNKNOWN(¶m_variant) = NULL; + hr = GetIUnknown(ndis_object_header, &V_UNKNOWN(¶m_variant)); + if (hr != WBEM_S_NO_ERROR) { + goto release; + } + + IWbemClassObject *ndis_tcp_offload_parameters = + *p_ndis_tcp_offload_parameters; + + /* set parameters */ + hr = ndis_tcp_offload_parameters->lpVtbl->Put( + ndis_tcp_offload_parameters, L"Header", 0, ¶m_variant, 0); + VariantClear(¶m_variant); + if (hr != WBEM_S_NO_ERROR) { + Win32HResultLogDebug(hr); + goto release; + } + + /* IPv4 csum */ + V_VT(¶m_variant) = VT_BSTR; + V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE); + if (!enable && (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4) != 0) { + /* this is basically all disabled cases */ + V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED); + } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4) == + WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4) { + /* implied enable */ + V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED); + } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX) != 0) { + /* implied enable */ + V_BSTR(¶m_variant) = + utob(NDIS_OFFLOAD_PARAMETERS_RX_ENABLED_TX_DISABLED); + } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX) != 0) { + /* implied enable */ + V_BSTR(¶m_variant) = + utob(NDIS_OFFLOAD_PARAMETERS_TX_ENABLED_RX_DISABLED); + } + hr = ndis_tcp_offload_parameters->lpVtbl->Put( + ndis_tcp_offload_parameters, L"IPv4Checksum", 0, ¶m_variant, 0); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters, + L"TCPIPv4Checksum", 0, + ¶m_variant, 0); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters, + L"UDPIPv4Checksum", 0, + ¶m_variant, 0); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + VariantClear(¶m_variant); + + /* IPv6 csum */ + V_VT(¶m_variant) = VT_BSTR; + V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE); + if (!enable && (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6) != 0) { + /* this is basically all disabled cases */ + V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED); + } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6) == + WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6) { + /* implied enable */ + V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED); + } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX) != 0) { + /* implied enable */ + V_BSTR(¶m_variant) = + utob(NDIS_OFFLOAD_PARAMETERS_RX_ENABLED_TX_DISABLED); + } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX) != 0) { + /* implied enable */ + V_BSTR(¶m_variant) = + utob(NDIS_OFFLOAD_PARAMETERS_TX_ENABLED_RX_DISABLED); + } + hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters, + L"TCPIPv6Checksum", 0, + ¶m_variant, 0); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters, + L"UDPIPv6Checksum", 0, + ¶m_variant, 0); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + VariantClear(¶m_variant); + + /* LSOv1 */ + V_VT(¶m_variant) = VT_BSTR; + V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE); + if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4) != 0) { + if (enable) { + V_BSTR(¶m_variant) = + utob(NDIS_OFFLOAD_PARAMETERS_LSOV1_ENABLED); + } else { + V_BSTR(¶m_variant) = + utob(NDIS_OFFLOAD_PARAMETERS_LSOV1_DISABLED); + } + } + hr = ndis_tcp_offload_parameters->lpVtbl->Put( + ndis_tcp_offload_parameters, L"LsoV1", 0, ¶m_variant, 0); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + VariantClear(¶m_variant); + + /* LSOv2 IPv4 */ + V_VT(¶m_variant) = VT_BSTR; + V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE); + if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4) != 0) { + if (enable) { + V_BSTR(¶m_variant) = + utob(NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED); + } else { + V_BSTR(¶m_variant) = + utob(NDIS_OFFLOAD_PARAMETERS_LSOV2_DISABLED); + } + } + hr = ndis_tcp_offload_parameters->lpVtbl->Put( + ndis_tcp_offload_parameters, L"LsoV2IPv4", 0, ¶m_variant, 0); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + VariantClear(¶m_variant); + + /* LSOv2 IPv4 */ + V_VT(¶m_variant) = VT_BSTR; + V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE); + if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6) != 0) { + if (enable) { + V_BSTR(¶m_variant) = + utob(NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED); + } else { + V_BSTR(¶m_variant) = + utob(NDIS_OFFLOAD_PARAMETERS_LSOV2_DISABLED); + } + } + hr = ndis_tcp_offload_parameters->lpVtbl->Put( + ndis_tcp_offload_parameters, L"LsoV2IPv6", 0, ¶m_variant, 0); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + VariantClear(¶m_variant); + + /* currently unused fields */ + V_VT(¶m_variant) = VT_BSTR; + V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE); + hr = ndis_tcp_offload_parameters->lpVtbl->Put( + ndis_tcp_offload_parameters, L"IPSec", 0, ¶m_variant, 0); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters, + L"TcpConnectionIPv4", 0, + ¶m_variant, 0); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters, + L"TcpConnectionIPv6", 0, + ¶m_variant, 0); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + hr = ndis_tcp_offload_parameters->lpVtbl->Put( + ndis_tcp_offload_parameters, L"Flags", 0, ¶m_variant, 0); + if (hr != WBEM_S_NO_ERROR) { + WbemLogDebug(hr); + goto release; + } + /* further fields are for NDIS 6.1+ */ + +release: + VariantClear(¶m_variant); + + return hr; +} + +static HRESULT SetNdisOffload(LPCWSTR if_description, uint32_t offload_flags, + bool enable) +{ + HRESULT hr = S_OK; + + ComInstance instance = {}; + WbemMethod method = {}; + WbemMethodCall call = {}; + + /* param 0 */ + IWbemClassObject *ndis_method_header = NULL; + /* param 1 */ + IWbemClassObject *ndis_tcp_offload_parameters = NULL; + + if (if_description == NULL) { + SCLogWarning("No description specified for device"); + return E_INVALIDARG; + } + + LPCWSTR class_name = L"MSNdis_SetTcpOffloadParameters"; + LPCWSTR instance_name_fmt = L"%s=\"%s\""; + size_t n_chars = wcslen(class_name) + wcslen(if_description) + + wcslen(instance_name_fmt); + LPWSTR instance_name = SCMalloc((n_chars + 1) * sizeof(wchar_t)); + if (instance_name == NULL) { + SCLogWarning("Failed to allocate buffer for instance path"); + goto release; + } + instance_name[n_chars] = 0; /* defensively null-terminate */ + hr = StringCchPrintfW(instance_name, n_chars, instance_name_fmt, class_name, + if_description); + if (hr != S_OK) { + SCLogWarning("Failed to format WMI class instance name: 0x%" PRIx32, (uint32_t)hr); + goto release; + } + + /* method name */ + LPCWSTR method_name = L"WmiSetTcpOffloadParameters"; + + /* connect to COM/WMI */ + hr = ComInstanceInit(&instance, L"ROOT\\WMI"); + if (hr != S_OK) { + goto release; + } + + /* obtain method */ + hr = GetWbemMethod(&instance, class_name, method_name, &method); + if (hr != S_OK) { + goto release; + } + + /* make parameter instances */ + hr = GetWbemMethodCall(&method, instance_name, &call); + if (hr != S_OK) { + goto release; + } + + /* build parameters */ + + VARIANT param_variant; + VariantInit(¶m_variant); + + /* Make MSNdis_WmiMethodHeader */ + hr = BuildNdisWmiMethodHeader(&instance, 0, 0, 0, 5, &ndis_method_header); + if (hr != WBEM_S_NO_ERROR) { + goto release; + } + + V_VT(¶m_variant) = VT_UNKNOWN; + V_UNKNOWN(¶m_variant) = NULL; + hr = GetIUnknown(ndis_method_header, &V_UNKNOWN(¶m_variant)); + if (hr != WBEM_S_NO_ERROR) { + goto release; + } + hr = call.in_params->lpVtbl->Put(call.in_params, L"MethodHeader", 0, + ¶m_variant, 0); + VariantClear(¶m_variant); + if (hr != WBEM_S_NO_ERROR) { + Win32HResultLogDebug(hr); + goto release; + } + + /* Make MSNdis_TcpOffloadParameters */ + hr = BuildNdisTcpOffloadParameters(&instance, offload_flags, enable, + &ndis_tcp_offload_parameters); + if (hr != WBEM_S_NO_ERROR) { + goto release; + } + + V_VT(¶m_variant) = VT_UNKNOWN; + V_UNKNOWN(¶m_variant) = NULL; + hr = GetIUnknown(ndis_tcp_offload_parameters, &V_UNKNOWN(¶m_variant)); + if (hr != WBEM_S_NO_ERROR) { + goto release; + } + hr = call.in_params->lpVtbl->Put(call.in_params, L"TcpOffloadParameters", 0, + ¶m_variant, 0); + VariantClear(¶m_variant); + if (hr != WBEM_S_NO_ERROR) { + Win32HResultLogDebug(hr); + goto release; + } + + /* execute the method */ + hr = WbemMethodCallExec(&call, NULL); + if (hr != S_OK) { + Win32HResultLogDebug(hr); + goto release; + } + +release: + ReleaseObject(ndis_tcp_offload_parameters); + ReleaseObject(ndis_method_header); + + WbemMethodCallRelease(&call); + WbemMethodRelease(&method); + ComInstanceRelease(&instance); + + return hr; +} + +int DisableIfaceOffloadingWin32(LiveDevice *ldev, int csum, int other) +{ + SCLogDebug("Disabling offloading for device %s", ldev->dev); + + int ret = 0; + DWORD err = NO_ERROR; + uint32_t offload_flags = 0; + + if (ldev == NULL) { + return -1; + } + + /* WMI uses the description as an identifier... */ + IP_ADAPTER_ADDRESSES *if_info_list = NULL, *if_info = NULL; + err = Win32GetAdaptersAddresses(&if_info_list); + if (err != NO_ERROR) { + ret = -1; + goto release; + } + err = Win32FindAdapterAddresses(if_info_list, ldev->dev, &if_info); + if (err != NO_ERROR) { + ret = -1; + goto release; + } + LPWSTR if_description = if_info->Description; + + err = GetNdisOffload(if_description, &offload_flags); + if (err != S_OK) { + ret = -1; + goto release; + } + + if (!csum) { + offload_flags &= ~WIN32_TCP_OFFLOAD_FLAG_CSUM; + } + if (!other) { + offload_flags &= ~WIN32_TCP_OFFLOAD_FLAG_LSO; + } + + err = SetNdisOffload(if_description, offload_flags, 0); + if (err != S_OK) { + ret = -1; + goto release; + } + +release: + SCFree(if_info_list); + + return ret; +} + +int RestoreIfaceOffloadingWin32(LiveDevice *ldev) +{ + SCLogDebug("Enabling offloading for device %s", ldev->dev); + + int ret = 0; + DWORD err = NO_ERROR; + + if (ldev == NULL) { + return -1; + } + + /* WMI uses the description as an identifier... */ + IP_ADAPTER_ADDRESSES *if_info_list = NULL, *if_info = NULL; + err = Win32GetAdaptersAddresses(&if_info_list); + if (err != NO_ERROR) { + ret = -1; + goto release; + } + err = Win32FindAdapterAddresses(if_info_list, ldev->dev, &if_info); + if (err != NO_ERROR) { + ret = -1; + goto release; + } + LPWSTR if_description = if_info->Description; + + err = SetNdisOffload(if_description, ldev->offload_orig, 1); + if (err != S_OK) { + ret = -1; + goto release; + } + +release: + SCFree(if_info_list); + + return ret; +} + +#endif /* NTDDI_VERSION >= NTDDI_VISTA */ + +#ifdef UNITTESTS +static int Win32TestStripPcapPrefix(void) +{ + int result = 1; + + const char *name1 = "\\Device\\NPF_{D4A32435-1BA7-4008-93A6-1518AA4BBD9B}"; + const char *expect_name1 = "{D4A32435-1BA7-4008-93A6-1518AA4BBD9B}"; + + const char *name2 = "{D4A32435-1BA7-4008-93A6-1518AA4BBD9B}"; + const char *expect_name2 = "{D4A32435-1BA7-4008-93A6-1518AA4BBD9B}"; + + result &= (strncmp(expect_name1, StripPcapPrefix(name1), + strlen(expect_name1)) == 0); + + result &= (strncmp(expect_name2, StripPcapPrefix(name2), + strlen(expect_name2)) == 0); + + return result; +} +#endif /* UNITTESTS */ + +void Win32SyscallRegisterTests(void) +{ +#ifdef UNITTESTS + UtRegisterTest("Win32TestStripPcapPrefix", Win32TestStripPcapPrefix); +#endif +} + +#endif /* OS_WIN32 */ |