]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
Adds WinDivert support to Windows builds 3402/head
authorJacob Masen-Smith <smith.jacob@gmail.com>
Wed, 20 Jun 2018 20:44:26 +0000 (13:44 -0700)
committerJacob Masen-Smith <smith.jacob@gmail.com>
Wed, 20 Jun 2018 20:44:26 +0000 (13:44 -0700)
Enables IPS functionality on Windows using the open-source
(LGPLv3/GPLv2) WinDivert driver and API.

From https://www.reqrypt.org/windivert-doc.html : "WinDivert is a
user-mode capture/sniffing/modification/blocking/re-injection package
for Windows Vista, Windows Server 2008, Windows 7, and Windows 8.
WinDivert can be used to implement user-mode packet filters, packet
sniffers, firewalls, NAT, VPNs, tunneling applications, etc., without
the need to write kernel-mode code."

- adds `--windivert [filter string]` and `--windivert-forward [filter
    string]` command-line options to enable WinDivert IPS mode.
    `--windivert[-forward] true` will open a filter for all traffic. See
    https://www.reqrypt.org/windivert-doc.html#filter_language for more
    information.

Limitation: currently limited to `autofp` runmode.

Additionally:
- `tmm_modules` now zeroed during `RegisterAllModules`
- fixed Windows Vista+ `inet_ntop` call in `PrintInet`
- fixed `GetRandom` bug (nonexistent keys) on fresh Windows installs
- fixed `RandomGetClock` building on Windows builds
- Added WMI queries for MTU

26 files changed:
configure.ac
doc/userguide/setting-up-ipsinline-for-windows.rst [new file with mode: 0644]
src/Makefile.am
src/decode.h
src/runmode-unittests.c
src/runmode-windivert.c [new file with mode: 0644]
src/runmode-windivert.h [new file with mode: 0644]
src/runmodes.c
src/runmodes.h
src/source-windivert-prototypes.h [new file with mode: 0644]
src/source-windivert.c [new file with mode: 0644]
src/source-windivert.h [new file with mode: 0644]
src/suricata.c
src/tm-modules.c
src/tm-threads-common.h
src/util-error.c
src/util-error.h
src/util-ioctl.c
src/util-ioctl.h
src/util-print.c
src/util-random.c
src/util-strptime.c
src/win32-misc.c
src/win32-misc.h
src/win32-syscall.c [new file with mode: 0644]
src/win32-syscall.h [new file with mode: 0644]

index bb1821c38ceb04053a80b0f98bd18b65ba45a786..ec4e6a12d026d19dd0884cc8f76c0e75aabc57f9 100644 (file)
             ;;
         *-*-mingw32*)
             CFLAGS="${CFLAGS} -DOS_WIN32"
-            LDFLAGS="${LDFLAGS} -lws2_32"
+            LDFLAGS="${LDFLAGS} -lws2_32 -liphlpapi -lwbemuuid -lOle32 -lOleAut32 -lUuid"
             WINDOWS_PATH="yes"
             PCAP_LIB_NAME="wpcap"
             AC_DEFINE([HAVE_NON_POSIX_MKDIR], [1], [mkdir is not POSIX compliant: single arg])
         fi
     fi
 
+  # WinDivert support
+    AC_ARG_ENABLE(windivert,
+        AS_HELP_STRING([--enable-windivert],[Enable WinDivert support [default=no]]),,
+        [enable_windivert="no"])
+    
+  # WinDivert can only be enabled on Windows builds
+    AC_CHECK_DECL([OS_WIN32],,[enable_windivert="no"])
+
+    if test "x$enable_windivert" = "xyes"; then
+        # WinDivert requires Vista at a minimum. If the user has selected their own NTDDI_VERSION
+        # then don't override it.
+        AC_CHECK_DECL([NTDDI_VERSION],,
+                [CFLAGS="${CFLAGS} -DNTDDI_VERSION=NTDDI_VISTA -D_WIN32_WINNT=_WIN32_WINNT_VISTA"])
+
+        AC_DEFINE_UNQUOTED([WINDIVERT],[1],[Enable Windows WinDivert support for inline IDP])
+
+        AC_ARG_WITH(windivert_include,
+                [  --with-windivert-include=DIR WinDivert include path],
+                [with_windivert_include="$withval"],[with_windivert_include="no"])
+        AC_ARG_WITH(windivert_libraries,
+                [  --with-windivert-libraries=DIR WinDivert library path],
+                [with_windivert_libraries="$withval"],[with_windivert_libraries="no"])
+
+        if test "$with_windivert_include" != "no"; then
+            CPPFLAGS="${CPPFLAGS} -I${with_windivert_include}"
+        fi
+
+        if test "$with_windivert_libraries" != "no"; then
+            LDFLAGS="${LDFLAGS} -L${with_windivert_libraries}"
+        fi
+
+        AC_CHECK_HEADER(windivert.h,,WINDIVERT_INC="no")
+        AC_CHECK_LIB(WinDivert, WinDivertOpen,, WINDIVERT_LIB="no")
+
+        if test "$WINDIVERT_LIB" = "no" || test "$WINDIVERT_INC" = "no"; then
+            echo
+            echo "    ERROR! WinDivert not found, go get it from"
+            echo "    https://www.reqrypt.org/windivert.html"
+            echo
+            exit 1
+        fi
+    fi
+  # /WinDivert
+
   # prelude
     AC_ARG_ENABLE(prelude,
             AS_HELP_STRING([--enable-prelude], [Enable Prelude support for alerts]),,[enable_prelude=no])
@@ -2309,6 +2353,7 @@ SURICATA_BUILD_CONF="Suricata Configuration:
   Netmap support:                          ${enable_netmap}
   DAG enabled:                             ${enable_dag}
   Napatech enabled:                        ${enable_napatech}
+  WinDivert enabled:                       ${enable_windivert}
 
   Unix socket enabled:                     ${enable_unixsocket}
   Detection enabled:                       ${enable_detection}
diff --git a/doc/userguide/setting-up-ipsinline-for-windows.rst b/doc/userguide/setting-up-ipsinline-for-windows.rst
new file mode 100644 (file)
index 0000000..cb37078
--- /dev/null
@@ -0,0 +1,64 @@
+Setting up IPS/inline for Windows\r
+=================================\r
+\r
+This guide explains how to work with Suricata in layer 4 inline mode using\r
+WinDivert on Windows.\r
+\r
+First start by compiling Suricata with WinDivert support. For instructions, see\r
+`Windows Installation\r
+<https://redmine.openinfosecfoundation.org/attachments/download/1175/SuricataWinInstallationGuide_v1.4.3.pdf>`_.\r
+This documentation has not yet been updated with WinDivert information, so make\r
+sure to add the following flags to `configure`:\r
+\r
+::\r
+  \r
+  --enable-windivert=yes --with-windivert-include=<include-dir> --with-windivert-libraries=<libraries-dir>\r
+\r
+WinDivert.dll and WinDivert.sys must be in the same directory as the Suricata\r
+executable. WinDivert automatically installs the driver when it is run. For more\r
+information about WinDivert, see https://www.reqrypt.org/windivert-doc.html.\r
+\r
+To check if you have WinDivert enabled in your Suricata, enter the following\r
+command in an elevated command prompt or terminal:\r
+\r
+::\r
+  \r
+  suricata -c suricata.yaml --windivert [filter string]\r
+\r
+For information on the WinDivert filter language, see\r
+https://www.reqrypt.org/windivert-doc.html#filter_language\r
+\r
+If Suricata is running on a gateway and is meant to protect the network behind\r
+that gateway, you need to run WinDivert at the NETWORK_FORWARD layer. This can\r
+be achieved using the following command:\r
+\r
+::\r
+\r
+  suricata -c suricata.yaml --windivert-forward [filter string]\r
+\r
+The filter is automatically stopped and normal traffic resumes when Suricata is\r
+stopped.\r
+\r
+A quick start is to examine all traffic, in which case you can use the following\r
+command:\r
+\r
+::\r
+  \r
+  suricata -c suricata.yaml --windivert[-forward] true\r
+\r
+A few additional examples:\r
+\r
+Only TCP traffic:\r
+::\r
+\r
+  suricata -c suricata.yaml --windivert tcp\r
+\r
+Only TCP traffic on port 80:\r
+::\r
+\r
+  suricata -c suricata.yaml --windivert "tcp.DstPort == 80"\r
+\r
+TCP and ICMP traffic:\r
+::\r
+\r
+  suricata -c suricata.yaml --windivert "tcp or icmp"
\ No newline at end of file
index e323e26749295270378efaad05d2f2788f449292..b879ab421e195a8c0bb715acbb2a6be92d75af2d 100644 (file)
@@ -2,6 +2,7 @@ noinst_HEADERS = action-globals.h \
     app-layer-nbss.h app-layer-dcerpc-common.h \
     debug.h \
        flow-private.h queue.h source-nfq-prototypes.h \
+       source-windivert-prototypes.h \
        suricata-common.h threadvars.h util-binsearch.h \
     util-validate.h
 bin_PROGRAMS = suricata
@@ -357,6 +358,7 @@ runmode-pfring.c runmode-pfring.h \
 runmode-unittests.c runmode-unittests.h \
 runmode-unix-socket.c runmode-unix-socket.h \
 runmode-tile.c runmode-tile.h \
+runmode-windivert.c runmode-windivert.h \
 runmodes.c runmodes.h \
 rust.h \
 source-af-packet.c source-af-packet.h \
@@ -373,6 +375,7 @@ source-pcap-file.c source-pcap-file.h \
 source-pcap-file-directory-helper.c source-pcap-file-directory-helper.h \
 source-pcap-file-helper.c source-pcap-file-helper.h \
 source-pfring.c source-pfring.h \
+source-windivert.c source-windivert.h \
 stream.c stream.h \
 stream-tcp.c stream-tcp.h stream-tcp-private.h \
 stream-tcp-inline.c stream-tcp-inline.h \
@@ -502,6 +505,7 @@ util-validate.h util-affinity.h util-affinity.c \
 util-var.c util-var.h \
 util-var-name.c util-var-name.h \
 util-vector.h \
+win32-syscall.c win32-syscall.h \
 win32-misc.c win32-misc.h \
 win32-service.c win32-service.h \
 win32-syslog.h
index abe92f87bb43cae670f37d3b0479fc1e257aa7a2..64b0103e94172ee67b4ebd0dee72f7d6d31bc620 100644 (file)
@@ -64,6 +64,7 @@ enum PktSrcEnum {
 #include "source-af-packet.h"
 #include "source-mpipe.h"
 #include "source-netmap.h"
+#include "source-windivert.h"
 #ifdef HAVE_PF_RING_FLOW_OFFLOAD
 #include "source-pfring.h"
 #endif
@@ -474,6 +475,9 @@ typedef struct Packet_
         PfringPacketVars pfring_v;
 #endif
 #endif
+#ifdef WINDIVERT
+        WinDivertPacketVars windivert_v;
+#endif /* WINDIVERT */
 
         /** libpcap vars: shared by Pcap Live mode and Pcap File mode */
         PcapPacketVars pcap_v;
index 848e88d8d84a186f38a6733e84dbc55b179f753c..61fd5e6fda70e56c84a803e8e7a260d8360a2967 100644 (file)
 #include "util-streaming-buffer.h"
 #include "util-lua.h"
 
+#ifdef OS_WIN32
+#include "win32-syscall.h"
+#endif
+
+#ifdef WINDIVERT
+#include "source-windivert.h"
+#endif
+
 #ifdef HAVE_NSS
 #include <prinit.h>
 #include <nss.h>
@@ -218,6 +226,12 @@ static void RegisterUnittests(void)
     AppLayerUnittestsRegister();
     MimeDecRegisterTests();
     StreamingBufferRegisterTests();
+#ifdef OS_WIN32
+    Win32SyscallRegisterTests();
+#endif
+#ifdef WINDIVERT
+    SourceWinDivertRegisterTests();
+#endif
 }
 #endif
 
diff --git a/src/runmode-windivert.c b/src/runmode-windivert.c
new file mode 100644 (file)
index 0000000..adccdb8
--- /dev/null
@@ -0,0 +1,71 @@
+/* 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>
+ *
+ * Handling of WinDivert runmodes.
+ */
+
+#include "suricata-common.h"
+#include "tm-threads.h"
+#include "conf.h"
+#include "runmodes.h"
+#include "runmode-windivert.h"
+#include "output.h"
+
+#include "util-affinity.h"
+#include "util-cpu.h"
+#include "util-debug.h"
+#include "util-device.h"
+#include "util-runmodes.h"
+#include "util-time.h"
+
+
+static const char *default_mode;
+
+
+const char *RunModeIpsWinDivertGetDefaultMode(void) { return default_mode; }
+
+void RunModeIpsWinDivertRegister(void)
+{
+    default_mode = "autofp";
+
+    RunModeRegisterNewRunMode(
+            RUNMODE_WINDIVERT, "autofp",
+            "Multi-threaded WinDivert IPS mode load-balanced by flow",
+            RunModeIpsWinDivertAutoFp);
+}
+
+int RunModeIpsWinDivertAutoFp(void)
+{
+    SCEnter();
+    int ret = 0;
+#ifdef WINDIVERT
+    RunModeInitialize();
+
+    TimeModeSetLive();
+
+    LiveDeviceHasNoStats();
+
+    ret = RunModeSetIPSAutoFp(WinDivertGetThread, "ReceiveWinDivert",
+                              "VerdictWinDivert", "DecodeWinDivert");
+#endif /* WINDIVERT */
+    return ret;
+}
diff --git a/src/runmode-windivert.h b/src/runmode-windivert.h
new file mode 100644 (file)
index 0000000..5071fcf
--- /dev/null
@@ -0,0 +1,32 @@
+/* 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>
+ *
+ */
+
+#ifndef __RUNMODE_WINDIVERT_H__
+#define __RUNMODE_WINDIVERT_H__
+
+int RunModeIpsWinDivertAutoFp(void);
+void RunModeIpsWinDivertRegister(void);
+const char *RunModeIpsWinDivertGetDefaultMode(void);
+
+#endif /* __RUNMODE_WINDIVERT_H__ */
\ No newline at end of file
index 6001fe37cae323b11f80cd8b96288b61bfb54ad4..ca72e53e1f2d1a722f6443d84bd31f058d4a27b0 100644 (file)
@@ -147,6 +147,12 @@ static const char *RunModeTranslateModeToName(int runmode)
 #endif
         case RUNMODE_UNIX_SOCKET:
             return "UNIX_SOCKET";
+        case RUNMODE_WINDIVERT:
+#ifdef WINDIVERT
+            return "WINDIVERT";
+#else
+            return "WINDIVERT(DISABLED)";
+#endif
         default:
             SCLogError(SC_ERR_UNKNOWN_RUN_MODE, "Unknown runtime mode. Aborting");
             exit(EXIT_FAILURE);
@@ -220,6 +226,7 @@ void RunModeRegisterRunModes(void)
     RunModeIdsNflogRegister();
     RunModeTileMpipeRegister();
     RunModeUnixSocketRegister();
+    RunModeIpsWinDivertRegister();
 #ifdef UNITTESTS
     UtRunModeRegister();
 #endif
@@ -328,6 +335,11 @@ void RunModeDispatch(int runmode, const char *custom_mode)
             case RUNMODE_NFLOG:
                 custom_mode = RunModeIdsNflogGetDefaultMode();
                 break;
+#ifdef WINDIVERT
+            case RUNMODE_WINDIVERT:
+                custom_mode = RunModeIpsWinDivertGetDefaultMode();
+                break;
+#endif
             default:
                 SCLogError(SC_ERR_UNKNOWN_RUN_MODE, "Unknown runtime mode. Aborting");
                 exit(EXIT_FAILURE);
index f0bb7735f57f6269c70cbaa81bbc539fd70424be..3087ff9a0eba2a32a43463d130dfc212fd07f89c 100644 (file)
@@ -40,6 +40,7 @@ enum RunModes {
     RUNMODE_UNITTEST,
     RUNMODE_NAPATECH,
     RUNMODE_UNIX_SOCKET,
+    RUNMODE_WINDIVERT,
     RUNMODE_USER_MAX, /* Last standard running mode */
     RUNMODE_LIST_KEYWORDS,
     RUNMODE_LIST_APP_LAYERS,
@@ -106,6 +107,7 @@ int RunModeNeedsBypassManager(void);
 #include "runmode-nflog.h"
 #include "runmode-unix-socket.h"
 #include "runmode-netmap.h"
+#include "runmode-windivert.h"
 
 int threading_set_cpu_affinity;
 extern float threading_detect_ratio;
diff --git a/src/source-windivert-prototypes.h b/src/source-windivert-prototypes.h
new file mode 100644 (file)
index 0000000..acb0d7f
--- /dev/null
@@ -0,0 +1,31 @@
+/* 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>
+ */
+
+#ifndef __SOURCE_WINDIVERT_PROTOTYPES_H__
+#define __SOURCE_WINDIVERT_PROTOTYPES_H__
+
+void TmModuleReceiveWinDivertRegister(void);
+void TmModuleVerdictWinDivertRegister(void);
+void TmModuleDecodeWinDivertRegister(void);
+
+#endif /* __SOURCE_WINDIVERT_PROTOTYPES_H__ */
diff --git a/src/source-windivert.c b/src/source-windivert.c
new file mode 100644 (file)
index 0000000..1c54971
--- /dev/null
@@ -0,0 +1,1021 @@
+/* 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>
+ *
+ * WinDivert emulation of netfilter_queue functionality to hook into Suricata's
+ * IPS mode. Supported solely on Windows.
+ *
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "tm-threads.h"
+
+#include "util-byte.h"
+#include "util-debug.h"
+#include "util-device.h"
+#include "util-error.h"
+#include "util-ioctl.h"
+#include "util-privs.h"
+#include "util-unittest.h"
+
+#include "runmodes.h"
+
+#include "queue.h"
+
+#include "source-windivert-prototypes.h"
+#include "source-windivert.h"
+
+#ifdef WINDIVERT
+// clang-format off
+#include <winsock2.h>
+#include <windows.h>
+#include <iptypes.h>
+#include <winerror.h>
+// clang-format on
+#endif
+
+#ifndef WINDIVERT
+/* Gracefully handle the case where no WinDivert support is compiled in */
+
+TmEcode NoWinDivertSupportExit(ThreadVars *, const void *, void **);
+
+void TmModuleReceiveWinDivertRegister(void)
+{
+    tmm_modules[TMM_RECEIVEWINDIVERT].name = "ReceiveWinDivert";
+    tmm_modules[TMM_RECEIVEWINDIVERT].ThreadInit = NoWinDivertSupportExit;
+    tmm_modules[TMM_RECEIVEWINDIVERT].flags = TM_FLAG_RECEIVE_TM;
+}
+
+void TmModuleVerdictWinDivertRegister(void)
+{
+    tmm_modules[TMM_VERDICTWINDIVERT].name = "VerdictWinDivert";
+    tmm_modules[TMM_VERDICTWINDIVERT].ThreadInit = NoWinDivertSupportExit;
+}
+
+void TmModuleDecodeWinDivertRegister(void)
+{
+    tmm_modules[TMM_DECODEWINDIVERT].name = "DecodeWinDivert";
+    tmm_modules[TMM_DECODEWINDIVERT].ThreadInit = NoWinDivertSupportExit;
+    tmm_modules[TMM_DECODEWINDIVERT].flags = TM_FLAG_DECODE_TM;
+}
+
+TmEcode NoWinDivertSupportExit(ThreadVars *tv, const void *initdata,
+                               void **data)
+{
+    SCLogError(
+            SC_ERR_WINDIVERT_NOSUPPORT,
+            "Error creating thread %s: you do not have support for WinDivert "
+            "enabled; please recompile with --enable-windivert",
+            tv->name);
+    exit(EXIT_FAILURE);
+}
+
+#else /* implied we do have WinDivert support */
+
+typedef struct WinDivertThreadVars_ {
+    WinDivertHandle filter_handle;
+
+    int thread_num;
+    CaptureStats stats;
+    int64_t qpc_start_time;
+    int64_t qpc_start_count;
+    int64_t qpc_freq_usec;
+
+    TmSlot *slot;
+
+    bool offload_enabled;
+
+    TAILQ_HEAD(, LiveDevice_) live_devices;
+} WinDivertThreadVars;
+
+#define WINDIVERT_MAX_QUEUE 16
+static WinDivertThreadVars g_wd_tv[WINDIVERT_MAX_QUEUE];
+static WinDivertQueueVars g_wd_qv[WINDIVERT_MAX_QUEUE];
+static uint16_t g_wd_num = 0;
+static SCMutex g_wd_init_lock = SCMUTEX_INITIALIZER;
+
+void *WinDivertGetThread(int n)
+{
+    if (n >= g_wd_num) {
+        return NULL;
+    }
+    return (void *)&g_wd_tv[n];
+}
+
+void *WinDivertGetQueue(int n)
+{
+    if (n >= g_wd_num) {
+        return NULL;
+    }
+    return (void *)&g_wd_qv[n];
+}
+
+// not defined in MinGW winerror.h
+#define ERROR_INVALID_IMAGE_HASH 577L
+#define ERROR_DATA_NOT_ACCEPTED 592L
+
+/**
+ * \brief return an error description for Win32 error values commonly returned
+ * by WinDivert
+ */
+static const char *WinDivertGetErrorString(DWORD error_code)
+{
+    switch (error_code) {
+        // WinDivertOpen errors
+        case ERROR_FILE_NOT_FOUND:
+            return "The driver files WinDivert32.sys or WinDivert64.sys were "
+                   "not found.";
+        case ERROR_ACCESS_DENIED:
+            return "Suricata must be run with Administrator privileges.";
+        case ERROR_INVALID_PARAMETER:
+            return "The WinDivert packet filter string is invalid.";
+        case ERROR_INVALID_IMAGE_HASH:
+            return "The WinDivert32.sys or WinDivert64.sys driver does not "
+                   "have a valid digital signature, or your copy of Windows is "
+                   "not up-to-date. Windows 7 and Server 2008 users need to "
+                   "run Windows Update or install the following patch from "
+                   "Microsoft: http://support.microsoft.com/kb/2949927";
+        case ERROR_DRIVER_BLOCKED:
+            return "This error occurs for various reasons, including: "
+                   "attempting to load the 32-bit WinDivert.sys driver on a "
+                   "64-bit system (or vice versa); the WinDivert.sys driver is "
+                   "blocked by security software; or you are using a "
+                   "virtualization environment that does not support "
+                   "drivers.";
+        case EPT_S_NOT_REGISTERED:
+            return "This error occurs when the Base Filtering Engine service "
+                   "has been disabled.";
+        case ERROR_PROC_NOT_FOUND:
+            return "The error may occur for Windows Vista users. The "
+                   "solution is to install the following patch from Microsoft: "
+                   "http://support.microsoft.com/kb/2761494.";
+
+        // WinDivertSend errors
+        case ERROR_HOST_UNREACHABLE:
+            return "This error occurs when an impostor packet (with "
+                   "pAddr->Impostor set to 1) is injected and the ip.TTL or "
+                   "ipv6.HopLimit field goes to zero. This is a defense of "
+                   "last resort against infinite loops caused by impostor "
+                   "packets.";
+        case ERROR_DATA_NOT_ACCEPTED:
+            return "This error is returned when the user application attempts "
+                   "to inject a malformed packet. It may also be returned for "
+                   "valid inbound packets, and the Windows TCP/IP stack "
+                   "rejects the packet for some reason.";
+        case ERROR_RETRY:
+            return "The underlying cause of this error is unknown. However, "
+                   "this error usually occurs when certain kinds of "
+                   "anti-virus/firewall/security software is installed, and "
+                   "the error message usually resolves once the offending "
+                   "program is uninstalled. This suggests a software "
+                   "compatibility problem.";
+        default:
+            return "";
+    }
+}
+
+/**
+ * \brief logs a WinDivert error at Error level.
+ */
+#define WinDivertLogError(err_code)                                            \
+    do {                                                                       \
+        const char *win_err_str = Win32GetErrorString((err_code), NULL);       \
+        SCLogError(SC_ERR_WINDIVERT_GENERIC,                                   \
+                   "WinDivertOpen failed, error %" PRId32 " (0x%08" PRIx32     \
+                   "): %s %s",                                                 \
+                   (uint32_t)(err_code), (uint32_t)(err_code), win_err_str,    \
+                   WinDivertGetErrorString(err_code));                         \
+        LocalFree((LPVOID)win_err_str);                                        \
+    } while (0);
+
+/**
+ * \brief initializes QueryPerformanceCounter values so we can get
+ * absolute time from WinDivert timestamps.
+ */
+static void WinDivertInitQPCValues(WinDivertThreadVars *wd_tv)
+{
+    struct timeval now;
+
+    TimeGet(&now);
+    (void)QueryPerformanceCounter((LARGE_INTEGER *)&wd_tv->qpc_start_count);
+
+    wd_tv->qpc_start_time =
+            (uint64_t)now.tv_sec * (1000 * 1000) + (uint64_t)now.tv_usec;
+
+    (void)QueryPerformanceFrequency((LARGE_INTEGER *)&wd_tv->qpc_freq_usec);
+    /* \bug: clock drift? */
+    wd_tv->qpc_freq_usec /= 1000 * 1000;
+}
+
+/**
+ * \brief gets a timeval from a WinDivert timestamp
+ */
+static struct timeval WinDivertTimestampToTimeval(WinDivertThreadVars *wd_tv,
+                                                  INT64 timestamp_count)
+{
+    struct timeval ts;
+
+    int64_t qpc_delta = (int64_t)timestamp_count - wd_tv->qpc_start_count;
+    int64_t unix_usec =
+            wd_tv->qpc_start_time + (qpc_delta / wd_tv->qpc_freq_usec);
+
+    ts.tv_sec = (long)(unix_usec / (1000 * 1000));
+    ts.tv_usec = (long)(unix_usec - (int64_t)ts.tv_sec * (1000 * 1000));
+
+    return ts;
+}
+
+/**
+ * \brief initialize a WinDivert filter
+ *
+ * \param filter a WinDivert filter string as defined at
+ * https://www.reqrypt.org/windivert-doc.html#filter_language
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int WinDivertRegisterQueue(bool forward, char *filter_str)
+{
+    SCEnter();
+    int ret = 0;
+
+    WINDIVERT_LAYER layer =
+            forward ? WINDIVERT_LAYER_NETWORK_FORWARD : WINDIVERT_LAYER_NETWORK;
+
+    /* validate the filter string */
+    const char *error_str;
+    uint32_t error_pos;
+    bool valid = WinDivertHelperCheckFilter(filter_str, layer, &error_str,
+                                            &error_pos);
+    if (!valid) {
+        SCLogWarning(
+                SC_ERR_WINDIVERT_INVALID_FILTER,
+                "Invalid filter \"%s\" supplied to WinDivert: %s at position "
+                "%" PRId32 "",
+                filter_str, error_str, error_pos);
+        SCReturnInt(SC_ERR_WINDIVERT_INVALID_FILTER);
+    }
+
+    /* initialize the queue */
+    SCMutexLock(&g_wd_init_lock);
+
+    if (g_wd_num >= WINDIVERT_MAX_QUEUE) {
+        SCLogError(SC_ERR_INVALID_ARGUMENT,
+                   "Too many WinDivert queues specified %" PRId32 "", g_wd_num);
+        ret = -1;
+        goto unlock;
+    }
+    if (g_wd_num == 0) {
+        /* on first registration, zero-initialize all array structs */
+        memset(&g_wd_tv, 0, sizeof(g_wd_tv));
+        memset(&g_wd_qv, 0, sizeof(g_wd_qv));
+    }
+
+    /* init thread vars */
+    WinDivertThreadVars *wd_tv = &g_wd_tv[g_wd_num];
+    wd_tv->thread_num = g_wd_num;
+
+    /* init queue vars */
+    WinDivertQueueVars *wd_qv = &g_wd_qv[g_wd_num];
+    wd_qv->queue_num = g_wd_num;
+
+    WinDivertInitQPCValues(wd_tv);
+
+    /* copy filter to persistent storage */
+    size_t filter_len = strlen(filter_str);
+    size_t copy_len =
+            strlcpy(wd_qv->filter_str, filter_str, sizeof(wd_qv->filter_str));
+    if (filter_len > copy_len) {
+        SCLogWarning(SC_ERR_WINDIVERT_TOOLONG_FILTER,
+                     "Queue length exceeds storage by %" PRId32 " bytes",
+                     (int32_t)(filter_len - copy_len));
+        ret = -1;
+        goto unlock;
+    }
+
+    wd_qv->layer = layer;
+    wd_qv->priority =
+            g_wd_num; /* priority set in the order filters are defined */
+    wd_qv->flags = 0; /* normal inline function */
+
+    SCMutexInit(&wd_qv->filter_init_mutex, NULL);
+    SCMutexInit(&wd_qv->counters_mutex, NULL);
+
+    g_wd_num++;
+
+unlock:
+    SCMutexUnlock(&g_wd_init_lock);
+
+    if (ret == 0) {
+        // stringify queue index to use as thread name descriptor
+        char wd_num_str[6];
+        wd_num_str[sizeof(wd_num_str) - 1] = 0;
+        snprintf(wd_num_str, sizeof(wd_num_str), "%" PRId16 "", g_wd_num);
+
+        LiveRegisterDevice(wd_num_str);
+
+        SCLogDebug("Queue %" PRId16 " registered", wd_qv->queue_num);
+    }
+
+    return ret;
+}
+
+/* forward declarations of internal functions */
+/* Receive functions */
+TmEcode ReceiveWinDivertLoop(ThreadVars *, void *, void *);
+TmEcode ReceiveWinDivertThreadInit(ThreadVars *, const void *, void **);
+TmEcode ReceiveWinDivertThreadDeinit(ThreadVars *, void *);
+void ReceiveWinDivertThreadExitStats(ThreadVars *, void *);
+
+/* Verdict functions */
+TmEcode VerdictWinDivert(ThreadVars *, Packet *, void *, PacketQueue *,
+                         PacketQueue *);
+TmEcode VerdictWinDivertThreadInit(ThreadVars *, const void *, void **);
+TmEcode VerdictWinDivertThreadDeinit(ThreadVars *, void *);
+
+/* Decode functions */
+TmEcode DecodeWinDivert(ThreadVars *, Packet *, void *, PacketQueue *,
+                        PacketQueue *);
+TmEcode DecodeWinDivertThreadInit(ThreadVars *, const void *, void **);
+TmEcode DecodeWinDivertThreadDeinit(ThreadVars *, void *);
+
+/* internal helper functions */
+static TmEcode WinDivertRecvHelper(ThreadVars *tv, WinDivertThreadVars *);
+static TmEcode WinDivertVerdictHelper(ThreadVars *tv, Packet *p);
+static TmEcode WinDivertCloseHelper(WinDivertThreadVars *);
+
+static TmEcode WinDivertCollectFilterDevices(WinDivertThreadVars *,
+                                             WinDivertQueueVars *);
+static bool WinDivertIfaceMatchFilter(const char *filter_string, int if_index);
+static void WinDivertDisableOffloading(WinDivertThreadVars *);
+static void WinDivertRestoreOffloading(WinDivertThreadVars *);
+
+void TmModuleReceiveWinDivertRegister(void)
+{
+    TmModule *tm_ptr = &tmm_modules[TMM_RECEIVEWINDIVERT];
+
+    tm_ptr->name = "ReceiveWinDivert";
+    tm_ptr->ThreadInit = ReceiveWinDivertThreadInit;
+    tm_ptr->PktAcqLoop = ReceiveWinDivertLoop;
+    tm_ptr->ThreadExitPrintStats = ReceiveWinDivertThreadExitStats;
+    tm_ptr->ThreadDeinit = ReceiveWinDivertThreadDeinit;
+    tm_ptr->flags = TM_FLAG_RECEIVE_TM;
+}
+
+void TmModuleVerdictWinDivertRegister(void)
+{
+    TmModule *tm_ptr = &tmm_modules[TMM_VERDICTWINDIVERT];
+
+    tm_ptr->name = "VerdictWinDivert";
+    tm_ptr->ThreadInit = VerdictWinDivertThreadInit;
+    tm_ptr->Func = VerdictWinDivert;
+    tm_ptr->ThreadDeinit = VerdictWinDivertThreadDeinit;
+}
+
+void TmModuleDecodeWinDivertRegister(void)
+{
+    TmModule *tm_ptr = &tmm_modules[TMM_DECODEWINDIVERT];
+
+    tm_ptr->name = "DecodeWinDivert";
+    tm_ptr->ThreadInit = DecodeWinDivertThreadInit;
+    tm_ptr->Func = DecodeWinDivert;
+    tm_ptr->ThreadDeinit = DecodeWinDivertThreadDeinit;
+    tm_ptr->flags = TM_FLAG_DECODE_TM;
+}
+
+/**
+ * \brief Main WinDivert packet receive pump
+ */
+TmEcode ReceiveWinDivertLoop(ThreadVars *tv, void *data, void *slot)
+{
+    SCEnter();
+
+    WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)data;
+    wd_tv->slot = ((TmSlot *)slot)->slot_next;
+
+    while (true) {
+        if (suricata_ctl_flags & SURICATA_STOP) {
+            SCReturnInt(TM_ECODE_OK);
+        }
+
+        if (unlikely(WinDivertRecvHelper(tv, wd_tv) != TM_ECODE_OK)) {
+            SCReturnInt(TM_ECODE_FAILED);
+        }
+
+        StatsSyncCountersIfSignalled(tv);
+    }
+
+    SCReturnInt(TM_ECODE_OK);
+}
+
+static TmEcode WinDivertRecvHelper(ThreadVars *tv, WinDivertThreadVars *wd_tv)
+{
+    SCEnter();
+
+#ifdef COUNTERS
+    WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
+#endif /* COUNTERS */
+
+    /* make sure we have at least one packet in the packet pool, to prevent us
+     * from alloc'ing packets at line rate
+     */
+    PacketPoolWait();
+
+    /* obtain a packet buffer */
+    Packet *p = PacketGetFromQueueOrAlloc();
+    if (unlikely(p == NULL)) {
+        SCLogDebug(
+                "PacketGetFromQueueOrAlloc() - failed to obtain Packet buffer");
+        SCReturnInt(TM_ECODE_FAILED);
+    }
+    PKT_SET_SRC(p, PKT_SRC_WIRE);
+
+    /* receive packet, depending on offload status. MTU is used as an estimator
+     * for direct data alloc size, and this is meaningless if large segments are
+     * coalesced before they reach WinDivert */
+    bool success = false;
+    uint32_t pktlen = 0;
+    if (wd_tv->offload_enabled) {
+        /* allocate external, if not already */
+        PacketCallocExtPkt(p, MAX_PAYLOAD_SIZE);
+
+        success =
+                WinDivertRecv(wd_tv->filter_handle, p->ext_pkt,
+                              MAX_PAYLOAD_SIZE, &p->windivert_v.addr, &pktlen);
+    } else {
+        success = WinDivertRecv(wd_tv->filter_handle, GET_PKT_DIRECT_DATA(p),
+                                GET_PKT_DIRECT_MAX_SIZE(p),
+                                &p->windivert_v.addr, &pktlen);
+    }
+    SET_PKT_LEN(p, pktlen);
+
+    if (!success) {
+#ifdef COUNTERS
+        SCMutexLock(&wd_qv->counters_mutex);
+        wd_qv->errs++;
+        SCMutexUnlock(&wd_qv->counters_mutex);
+#endif /* COUNTERS */
+
+        /* ensure packet length is zero to trigger an error in packet decoding
+         */
+        SET_PKT_LEN(p, 0);
+
+        SCLogInfo("WinDivertRecv failed: error %" PRIu32 "",
+                  (uint32_t)(GetLastError()));
+        SCReturnInt(TM_ECODE_FAILED);
+    }
+    SCLogDebug("Packet received, length %" PRId32 "", GET_PKT_LEN(p));
+
+    p->ts = WinDivertTimestampToTimeval(wd_tv, p->windivert_v.addr.Timestamp);
+    p->windivert_v.thread_num = wd_tv->thread_num;
+
+#ifdef COUNTERS
+    SCMutexLock(&wd_qv->counters_mutex);
+    wd_qv->pkts++;
+    wd_qv->bytes += GET_PKT_LEN(p);
+    SCMutexUnlock(&wd_qv->counters_mutex);
+#endif /* COUNTERS */
+
+    /* Do the packet processing by calling TmThreadsSlotProcessPkt, this will,
+     * depending on the running mode, pass the packet to the treatment functions
+     * or push it to a packet pool. So processing time can vary.
+     */
+    if (TmThreadsSlotProcessPkt(tv, wd_tv->slot, p) != TM_ECODE_OK) {
+        TmqhOutputPacketpool(tv, p);
+        SCReturnInt(TM_ECODE_FAILED);
+    }
+
+    SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief Init function for ReceiveWinDivert
+ *
+ * ReceiveWinDivertThreadInit sets up receiving packets via WinDivert.
+ *
+ * \param tv pointer to generic thread vars
+ * \param initdata pointer to the interface passed from the user
+ * \param data out-pointer to the WinDivert-specific thread vars
+ */
+TmEcode ReceiveWinDivertThreadInit(ThreadVars *tv, const void *initdata,
+                                   void **data)
+{
+    SCEnter();
+    TmEcode ret = TM_ECODE_OK;
+
+    WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)initdata;
+
+    if (wd_tv == NULL) {
+        SCLogError(SC_ERR_INVALID_ARGUMENT, "initdata == NULL");
+        SCReturnInt(TM_ECODE_FAILED);
+    }
+
+    WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
+
+    if (wd_qv == NULL) {
+        SCLogError(SC_ERR_INVALID_ARGUMENT, "queue == NULL");
+        SCReturnInt(TM_ECODE_FAILED);
+    }
+
+    SCMutexLock(&wd_qv->filter_init_mutex);
+    /* does the queue already have an active handle? */
+    if (wd_qv->filter_handle != NULL &&
+        wd_qv->filter_handle != INVALID_HANDLE_VALUE) {
+        goto unlock;
+    }
+
+    TAILQ_INIT(&wd_tv->live_devices);
+
+    if (WinDivertCollectFilterDevices(wd_tv, wd_qv) == TM_ECODE_OK) {
+        WinDivertDisableOffloading(wd_tv);
+    } else {
+        SCLogWarning(SC_ERR_SYSCALL,
+                     "Failed to obtain network devices for WinDivert filter");
+    }
+
+    /* we open now so that we can immediately start handling packets,
+     * instead of losing however many would occur between registering the
+     * queue and starting a receive thread. */
+    wd_qv->filter_handle = WinDivertOpen(wd_qv->filter_str, wd_qv->layer,
+                                         wd_qv->priority, wd_qv->flags);
+    if (wd_qv->filter_handle == INVALID_HANDLE_VALUE) {
+        WinDivertLogError(GetLastError());
+        ret = TM_ECODE_FAILED;
+        goto unlock;
+    }
+
+unlock:
+    if (ret == 0) { /* success */
+        wd_tv->filter_handle = wd_qv->filter_handle;
+
+        /* set our return context */
+        *data = wd_tv;
+    }
+
+    SCMutexUnlock(&wd_qv->filter_init_mutex);
+    SCReturnInt(ret);
+}
+
+/**
+ * \brief collect all devices covered by this filter in the thread vars'
+ * live devices list
+ *
+ * \param wd_tv pointer to WinDivert thread vars
+ * \param wd_qv pointer to WinDivert queue vars
+ */
+static TmEcode WinDivertCollectFilterDevices(WinDivertThreadVars *wd_tv,
+                                             WinDivertQueueVars *wd_qv)
+{
+    SCEnter();
+    TmEcode ret = TM_ECODE_OK;
+
+    IP_ADAPTER_ADDRESSES *if_info_list;
+    DWORD err = (DWORD)Win32GetAdaptersAddresses(&if_info_list);
+    if (err != NO_ERROR) {
+        ret = TM_ECODE_FAILED;
+        goto release;
+    }
+
+    for (IP_ADAPTER_ADDRESSES *if_info = if_info_list; if_info != NULL;
+         if_info = if_info->Next) {
+
+        if (WinDivertIfaceMatchFilter(wd_qv->filter_str, if_info->IfIndex)) {
+            SCLogConfig("Found adapter %s matching WinDivert filter %s",
+                      if_info->AdapterName, wd_qv->filter_str);
+
+            LiveDevice *new_ldev = SCCalloc(1, sizeof(LiveDevice));
+            if (new_ldev == NULL) {
+                ret = TM_ECODE_FAILED;
+                goto release;
+            }
+            new_ldev->dev = SCStrdup(if_info->AdapterName);
+            if (new_ldev->dev == NULL) {
+                ret = TM_ECODE_FAILED;
+                goto release;
+            }
+            TAILQ_INSERT_TAIL(&wd_tv->live_devices, new_ldev, next);
+        } else {
+            SCLogDebug("Adapter %s does not match WinDivert filter %s",
+                       if_info->AdapterName, wd_qv->filter_str);
+        }
+    }
+
+release:
+    SCFree(if_info_list);
+
+    SCReturnInt(ret);
+}
+
+/**
+ * \brief test if the specified interface index matches the filter
+ */
+static bool WinDivertIfaceMatchFilter(const char *filter_string, int if_index)
+{
+    bool match = false;
+
+    WINDIVERT_ADDRESS if_addr = {};
+    if_addr.IfIdx = if_index;
+
+    uint8_t dummy[4] = {4, 4, 4, 4};
+
+    match = WinDivertHelperEvalFilter(filter_string, WINDIVERT_LAYER_NETWORK,
+                                      dummy, sizeof(dummy), &if_addr);
+    if (!match) {
+        int err = GetLastError();
+        if (err != 0) {
+            SCLogWarning(SC_ERR_WINDIVERT_GENERIC,
+                         "Failed to evaluate filter: 0x%" PRIx32, err);
+        }
+    }
+
+    return match;
+}
+
+/**
+ * \brief disable offload status on devices for this filter
+ *
+ * \param wd_tv pointer to WinDivert thread vars
+ */
+static void WinDivertDisableOffloading(WinDivertThreadVars *wd_tv)
+{
+    for (LiveDevice *ldev = TAILQ_FIRST(&wd_tv->live_devices); ldev != NULL;
+         ldev = TAILQ_NEXT(ldev, next)) {
+
+        if (LiveGetOffload() == 0) {
+            if (GetIfaceOffloading(ldev->dev, 1, 1) == 1) {
+                wd_tv->offload_enabled = true;
+            }
+        } else {
+            if (DisableIfaceOffloading(ldev, 1, 1) != 1) {
+                wd_tv->offload_enabled = true;
+            }
+        }
+    }
+}
+
+/**
+ * \brief enable offload status on devices for this filter
+ *
+ * \param wd_tv pointer to WinDivert thread vars
+ */
+static void WinDivertRestoreOffloading(WinDivertThreadVars *wd_tv)
+{
+    for (LiveDevice *ldev = TAILQ_FIRST(&wd_tv->live_devices); ldev != NULL;
+         ldev = TAILQ_NEXT(ldev, next)) {
+
+        RestoreIfaceOffloading(ldev);
+    }
+}
+
+/**
+ * \brief Deinit function releases resources at exit.
+ *
+ * \param tv pointer to generic thread vars
+ * \param data pointer to WinDivert-specific thread vars
+ */
+TmEcode ReceiveWinDivertThreadDeinit(ThreadVars *tv, void *data)
+{
+    SCEnter();
+
+    WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)data;
+
+    SCReturnCT(WinDivertCloseHelper(wd_tv), "TmEcode");
+}
+
+/**
+ * \brief ExitStats prints stats to stdout at exit
+ *
+ *
+ * \param tv pointer to generic thread vars
+ * \param data pointer to WinDivert-specific thread vars
+ */
+void ReceiveWinDivertThreadExitStats(ThreadVars *tv, void *data)
+{
+    SCEnter();
+
+    WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)data;
+    WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
+    if (wd_qv == NULL) {
+        SCLogError(SC_ERR_INVALID_ARGUMENT, "queue == NULL");
+        SCReturn;
+    }
+
+    SCMutexLock(&wd_qv->counters_mutex);
+
+    SCLogInfo("(%s) Packets %" PRIu32 ", Bytes %" PRIu64 ", Errors %" PRIu32 "",
+              tv->name, wd_qv->pkts, wd_qv->bytes, wd_qv->errs);
+    SCLogInfo("(%s) Verdict: Accepted %" PRIu32 ", Dropped %" PRIu32
+              ", Replaced %" PRIu32 "",
+              tv->name, wd_qv->accepted, wd_qv->dropped, wd_qv->replaced);
+
+    SCMutexUnlock(&wd_qv->counters_mutex);
+    SCReturn;
+}
+
+/**
+ * \brief WinDivert verdict module packet entry function
+ */
+TmEcode VerdictWinDivert(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq,
+                         PacketQueue *postpq)
+{
+    SCEnter();
+
+    TmEcode ret = TM_ECODE_OK;
+
+    ret = WinDivertVerdictHelper(tv, p);
+    if (ret != TM_ECODE_OK) {
+        SCReturnInt(ret);
+    }
+
+    SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief internal helper function to do the bulk of verdict work
+ */
+static TmEcode WinDivertVerdictHelper(ThreadVars *tv, Packet *p)
+{
+    SCEnter();
+    WinDivertThreadVars *wd_tv = WinDivertGetThread(p->windivert_v.thread_num);
+
+    /* update counters */
+    CaptureStatsUpdate(tv, &wd_tv->stats, p);
+
+#ifdef COUNTERS
+    WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
+#endif /* COUNTERS */
+
+    p->windivert_v.verdicted = true;
+
+    /* can't verdict a "fake" packet */
+    if (PKT_IS_PSEUDOPKT(p)) {
+        SCReturnInt(TM_ECODE_OK);
+    }
+
+    /* the handle has been closed and we can no longer use it */
+    if (wd_tv->filter_handle == INVALID_HANDLE_VALUE ||
+        wd_tv->filter_handle == NULL) {
+        SCReturnInt(TM_ECODE_OK);
+    }
+
+    /* we can't verdict tunnel packets without ensuring all encapsulated
+     * packets are verdicted */
+    if (IS_TUNNEL_PKT(p)) {
+        bool finalVerdict = VerdictTunnelPacket(p);
+        if (!finalVerdict) {
+            SCReturnInt(TM_ECODE_OK);
+        }
+
+        // the action needs to occur on the root packet.
+        if (p->root != NULL) {
+            p = p->root;
+        }
+    }
+
+    /* DROP simply means we do nothing; the WinDivert driver does the rest.
+     */
+    if (PACKET_TEST_ACTION(p, ACTION_DROP)) {
+#ifdef COUNTERS
+        SCMutexLock(&wd_qv->counters_mutex);
+        wd_qv->dropped++;
+        SCMutexUnlock(&wd_qv->counters_mutex);
+#endif /* counters */
+
+        SCReturnInt(TM_ECODE_OK);
+    }
+
+    bool success = WinDivertSend(wd_tv->filter_handle, GET_PKT_DATA(p),
+                                 GET_PKT_LEN(p), &p->windivert_v.addr, NULL);
+
+    if (unlikely(!success)) {
+        WinDivertLogError(GetLastError());
+        SCReturnInt(TM_ECODE_FAILED);
+    }
+
+#ifdef COUNTERS
+    SCMutexLock(&wd_qv->counters_mutex);
+    wd_qv->accepted++;
+    SCMutexUnlock(&wd_qv->counters_mutex);
+#endif /* counters */
+
+    SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief init the verdict thread, which is piggybacked off the receive
+ * thread
+ */
+TmEcode VerdictWinDivertThreadInit(ThreadVars *tv, const void *initdata,
+                                   void **data)
+{
+    SCEnter();
+
+    WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)initdata;
+
+    CaptureStatsSetup(tv, &wd_tv->stats);
+
+    *data = wd_tv;
+
+    SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief deinit the verdict thread and shut down the WinDivert driver if
+ * it's still up.
+ */
+TmEcode VerdictWinDivertThreadDeinit(ThreadVars *tv, void *data)
+{
+    SCEnter();
+
+    WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)data;
+
+    SCReturnCT(WinDivertCloseHelper(wd_tv), "TmEcode");
+}
+
+/**
+ * \brief decode a raw packet submitted to suricata from the WinDivert
+ * driver
+ *
+ * All WinDivert packets are IPv4/v6, but do not include the network layer
+ * to differentiate the two, so instead we must check the version and go
+ * from there.
+ */
+TmEcode DecodeWinDivert(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq,
+                        PacketQueue *postpq)
+{
+    SCEnter();
+
+    IPV4Hdr *ip4h = (IPV4Hdr *)GET_PKT_DATA(p);
+    IPV6Hdr *ip6h = (IPV6Hdr *)GET_PKT_DATA(p);
+    DecodeThreadVars *d_tv = (DecodeThreadVars *)data;
+
+    /* XXX HACK: flow timeout can call us for injected pseudo packets
+     *           see bug:
+     * https://redmine.openinfosecfoundation.org/issues/1107
+     */
+    if (PKT_IS_PSEUDOPKT(p))
+        SCReturnInt(TM_ECODE_OK);
+
+    DecodeUpdatePacketCounters(tv, d_tv, p);
+
+    if (IPV4_GET_RAW_VER(ip4h) == 4) {
+        SCLogDebug("IPv4 packet");
+        DecodeIPV4(tv, d_tv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+    } else if (IPV6_GET_RAW_VER(ip6h) == 6) {
+        SCLogDebug("IPv6 packet");
+        DecodeIPV6(tv, d_tv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
+    } else {
+        SCLogDebug("packet unsupported by WinDivert, first byte: %02x",
+                   *GET_PKT_DATA(p));
+    }
+
+    PacketDecodeFinalize(tv, d_tv, p);
+
+    SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode DecodeWinDivertThreadInit(ThreadVars *tv, const void *initdata,
+                                  void **data)
+{
+    SCEnter();
+
+    DecodeThreadVars *d_tv = DecodeThreadVarsAlloc(tv);
+    if (d_tv == NULL) {
+        SCReturnInt(TM_ECODE_FAILED);
+    }
+
+    DecodeRegisterPerfCounters(d_tv, tv);
+
+    *data = d_tv;
+
+    SCReturnInt(TM_ECODE_OK);
+}
+
+TmEcode DecodeWinDivertThreadDeinit(ThreadVars *tv, void *data)
+{
+    SCEnter();
+
+    if (data != NULL) {
+        DecodeThreadVarsFree(tv, data);
+    }
+
+    SCReturnInt(TM_ECODE_OK);
+}
+
+/**
+ * \brief helper function for use with ThreadDeinit functions
+ */
+static TmEcode WinDivertCloseHelper(WinDivertThreadVars *wd_tv)
+{
+    SCEnter();
+    TmEcode ret = TM_ECODE_OK;
+
+    WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
+    if (wd_qv == NULL) {
+        SCLogDebug("No queue could be found for thread num %" PRId32 "",
+                   wd_tv->thread_num);
+        SCReturnInt(TM_ECODE_FAILED);
+    }
+
+    SCMutexLock(&wd_qv->filter_init_mutex);
+
+    /* check if there's nothing to close */
+    if (wd_qv->filter_handle == INVALID_HANDLE_VALUE ||
+        wd_qv->filter_handle == NULL) {
+        goto unlock;
+    }
+
+    if (!WinDivertClose(wd_qv->filter_handle)) {
+        SCLogError(SC_ERR_FATAL, "WinDivertClose failed: error %" PRIu32 "",
+                   (uint32_t)(GetLastError()));
+        ret = TM_ECODE_FAILED;
+        goto unlock;
+    }
+
+    (void)WinDivertRestoreOffloading(wd_tv);
+
+    wd_qv->filter_handle = NULL;
+
+unlock:
+    SCMutexUnlock(&wd_qv->filter_init_mutex);
+
+    if (ret == TM_ECODE_OK) {
+        SCMutexDestroy(&wd_qv->filter_init_mutex);
+        SCMutexDestroy(&wd_qv->counters_mutex);
+    }
+
+    SCReturnInt(ret);
+}
+
+#ifdef UNITTESTS
+static int SourceWinDivertTestIfaceMatchFilter(void)
+{
+    struct testdata {
+        const char *filter;
+        int if_index;
+        bool expected;
+    };
+
+    struct testdata tests[] = {
+            {"true", 11, true},
+            {"ifIdx=11", 11, true},
+            {"ifIdx==11", 11, true},
+            {"ifIdx!=11", 1, true},
+            {"ifIdx!=11", 11, false},
+            {"ifIdx=3", 4, false},
+            {"ifIdx=11 || ifIdx=5", 5, true},
+            {"ifIdx=11 || ifIdx=4", 5, false},
+            {"ifIdx<3 || ifIdx>7", 8, true},
+            {"ifIdx<3 || ifIdx>7", 5, false},
+            {"ifIdx>3 or ifIdx<7", 5, true},
+            {"ifIdx>3 && ifIdx<7", 5, true},
+            {"ifIdx>3 && ifIdx<7", 1, false},
+            {"(ifIdx > 3 && ifIdx < 7) or ifIdx == 11", 11, true}};
+
+    size_t count = (sizeof(tests) / sizeof(tests[0]));
+
+    for (size_t i = 0; i < count; i++) {
+        struct testdata test = tests[i];
+
+        bool actual = WinDivertIfaceMatchFilter(test.filter, test.if_index);
+        if (actual != test.expected) {
+            printf("WinDivertIfaceMatchFilter(\"%s\", %d) == %d, expected %d\n",
+                   test.filter, test.if_index, actual, test.expected);
+            FAIL;
+        }
+    }
+    PASS;
+}
+#endif
+
+/**
+ * \brief this function registers unit tests for the WinDivert Source
+ */
+void SourceWinDivertRegisterTests()
+{
+#ifdef UNITTESTS
+    UtRegisterTest("SourceWinDivertTestIfaceMatchFilter",
+                   SourceWinDivertTestIfaceMatchFilter);
+#endif
+}
+
+#endif /* WINDIVERT */
\ No newline at end of file
diff --git a/src/source-windivert.h b/src/source-windivert.h
new file mode 100644 (file)
index 0000000..7157499
--- /dev/null
@@ -0,0 +1,82 @@
+/* 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>
+ *
+ */
+
+#ifndef __SOURCE_WINDIVERT_H__
+#define __SOURCE_WINDIVERT_H__
+
+#ifdef WINDIVERT
+
+#include "windivert.h"
+
+#define WINDIVERT_FILTER_MAXLEN 128 /* from windivert_device.h */
+
+typedef void *WinDivertHandle;
+
+/**
+ * \brief WinDivertQueueVars is the queue configuration and other miscellaneous
+ * information about the specific queue/filter.
+ *
+ * see https://reqrypt.org/windivert-doc.html#divert_open for more info
+ */
+typedef struct WinDivertQueueVars_
+{
+    int queue_num;
+
+    /* see https://reqrypt.org/windivert-doc.html#filter_language */
+    char filter_str[WINDIVERT_FILTER_MAXLEN + 1];
+    WINDIVERT_LAYER layer;
+    int16_t priority;
+    uint64_t flags;
+
+    WinDivertHandle filter_handle;
+    /* only needed for setup/teardown; Recv/Send are internally synchronized */
+    SCMutex filter_init_mutex;
+
+    /* counters */
+    uint32_t pkts;
+    uint64_t bytes;
+    uint32_t errs;
+    uint32_t accepted;
+    uint32_t dropped;
+    uint32_t replaced;
+    SCMutex counters_mutex;
+} WinDivertQueueVars;
+
+typedef struct WinDivertPacketVars_
+{
+    int thread_num;
+
+    WINDIVERT_ADDRESS addr;
+    bool verdicted;
+} WinDivertPacketVars;
+
+int WinDivertRegisterQueue(bool forward, char *filter_str);
+void *WinDivertGetThread(int thread);
+void *WinDivertGetQueue(int queue);
+
+void SourceWinDivertRegisterTests(void);
+
+#endif /* WINDIVERT */
+#endif /* __SOURCE_WINDIVERT_H__ */
\ No newline at end of file
index 2784ad5ef9b9412605a61f86694bfd755cbbbb2b..8fb17db279a4c7bf5b8604b4071a33fdbe963057 100644 (file)
@@ -95,6 +95,9 @@
 #include "source-netmap.h"
 #include "source-mpipe.h"
 
+#include "source-windivert.h"
+#include "source-windivert-prototypes.h"
+
 #include "respond-reject.h"
 
 #include "flow.h"
@@ -651,6 +654,10 @@ static void PrintUsage(const char *progname)
 #endif
 #ifdef HAVE_MPIPE
     printf("\t--mpipe                              : run with tilegx mpipe interface(s)\n");
+#endif
+#ifdef WINDIVERT
+    printf("\t--windivert <filter>                 : run in inline WinDivert mode\n");
+    printf("\t--windivert-forward <filter>         : run in inline WinDivert mode, as a gateway\n");
 #endif
     printf("\t--set name=value                     : set a configuration value\n");
     printf("\n");
@@ -855,6 +862,9 @@ int g_ut_covered;
 
 void RegisterAllModules(void)
 {
+    // zero all module storage
+    memset(tmm_modules, 0, TMM_SIZE * sizeof(TmModule));
+
     /* commanders */
     TmModuleUnixManagerRegister();
     /* managers */
@@ -912,6 +922,11 @@ void RegisterAllModules(void)
     /* nflog */
     TmModuleReceiveNFLOGRegister();
     TmModuleDecodeNFLOGRegister();
+
+    /* windivert */
+    TmModuleReceiveWinDivertRegister();
+    TmModuleVerdictWinDivertRegister();
+    TmModuleDecodeWinDivertRegister();
 }
 
 static TmEcode LoadYamlConfig(SCInstance *suri)
@@ -1533,6 +1548,10 @@ static TmEcode ParseCommandLine(int argc, char** argv, SCInstance *suri)
         {"build-info", 0, &build_info, 1},
 #ifdef HAVE_MPIPE
         {"mpipe", optional_argument, 0, 0},
+#endif
+#ifdef WINDIVERT
+        {"windivert", required_argument, 0, 0},
+        {"windivert-forward", required_argument, 0, 0},
 #endif
         {"set", required_argument, 0, 0},
 #ifdef HAVE_NFLOG
@@ -1812,6 +1831,45 @@ static TmEcode ParseCommandLine(int argc, char** argv, SCInstance *suri)
                 }
             }
 #endif
+            else if(strcmp((long_opts[option_index]).name, "windivert-forward") == 0) {
+#ifdef WINDIVERT
+                if (suri->run_mode == RUNMODE_UNKNOWN) {
+                    suri->run_mode = RUNMODE_WINDIVERT;
+                    if (WinDivertRegisterQueue(true, optarg) == -1) {
+                        exit(EXIT_FAILURE);
+                    }
+                } else if (suri->run_mode == RUNMODE_WINDIVERT) {
+                    if (WinDivertRegisterQueue(true, optarg) == -1) {
+                        exit(EXIT_FAILURE);
+                    }
+                } else {
+                    SCLogError(SC_ERR_MULTIPLE_RUN_MODE, "more than one run mode "
+                                                        "has been specified");
+                    PrintUsage(argv[0]);
+                    exit(EXIT_FAILURE);
+                }
+            }
+            else if(strcmp((long_opts[option_index]).name, "windivert") == 0) {
+                if (suri->run_mode == RUNMODE_UNKNOWN) {
+                    suri->run_mode = RUNMODE_WINDIVERT;
+                    if (WinDivertRegisterQueue(false, optarg) == -1) {
+                        exit(EXIT_FAILURE);
+                    }
+                } else if (suri->run_mode == RUNMODE_WINDIVERT) {
+                    if (WinDivertRegisterQueue(false, optarg) == -1) {
+                        exit(EXIT_FAILURE);
+                    }
+                } else {
+                    SCLogError(SC_ERR_MULTIPLE_RUN_MODE, "more than one run mode "
+                                                        "has been specified");
+                    PrintUsage(argv[0]);
+                    exit(EXIT_FAILURE);
+                }
+#else
+                SCLogError(SC_ERR_WINDIVERT_NOSUPPORT,"WinDivert not enabled. Make sure to pass --enable-windivert to configure when building.");
+                return TM_ECODE_FAILED;            
+#endif /* WINDIVERT */
+            }
             else if (strcmp((long_opts[option_index]).name, "set") == 0) {
                 if (optarg != NULL) {
                     /* Quick validation. */
@@ -2405,10 +2463,27 @@ static int ConfigGetCaptureValue(SCInstance *suri)
      * back on a sane default. */
     const char *temp_default_packet_size;
     if ((ConfGet("default-packet-size", &temp_default_packet_size)) != 1) {
+        int mtu = 0;
         int lthread;
         int nlive;
         int strip_trailing_plus = 0;
         switch (suri->run_mode) {
+#ifdef WINDIVERT
+            case RUNMODE_WINDIVERT:
+                /* by default, WinDivert collects from all devices */
+                mtu = GetGlobalMTUWin32();
+
+                if (mtu > 0) {
+                    g_default_mtu = mtu;
+                    /* SLL_HEADER_LEN is the longest header + 8 for VLAN */
+                    default_packet_size = mtu + SLL_HEADER_LEN + 8;
+                    break;
+                }
+
+                g_default_mtu = DEFAULT_MTU;
+                default_packet_size = DEFAULT_PACKET_SIZE;
+                break;
+#endif /* WINDIVERT */
             case RUNMODE_PCAP_DEV:
             case RUNMODE_AFP_DEV:
             case RUNMODE_NETMAP:
@@ -2420,7 +2495,7 @@ static int ConfigGetCaptureValue(SCInstance *suri)
                 nlive = LiveGetDeviceCount();
                 for (lthread = 0; lthread < nlive; lthread++) {
                     const char *live_dev = LiveGetDeviceName(lthread);
-                    char dev[32];
+                    char dev[128]; /* need to be able to support GUID names on Windows */
                     (void)strlcpy(dev, live_dev, sizeof(dev));
 
                     if (strip_trailing_plus) {
@@ -2429,7 +2504,7 @@ static int ConfigGetCaptureValue(SCInstance *suri)
                             dev[len-1] = '\0';
                         }
                     }
-                    int mtu = GetIfaceMTU(dev);
+                    mtu = GetIfaceMTU(dev);
                     g_default_mtu = MAX(mtu, g_default_mtu);
 
                     unsigned int iface_max_packet_size = GetIfaceMaxPacketSize(dev);
index 869ba184de43d6989c393faeb109428370ac70d3..310e969240dc784baa2cd62dfb993281cd74ba6a 100644 (file)
@@ -234,6 +234,9 @@ const char * TmModuleTmmIdToString(TmmId id)
         CASE_CODE (TMM_DETECTLOADER);
         CASE_CODE (TMM_RECEIVENETMAP);
         CASE_CODE (TMM_DECODENETMAP);
+        CASE_CODE (TMM_RECEIVEWINDIVERT);
+        CASE_CODE (TMM_VERDICTWINDIVERT);
+        CASE_CODE (TMM_DECODEWINDIVERT);
 
         CASE_CODE (TMM_SIZE);
     }
index d55884db81e8d2ab44f06b6a20e5d0a03f153f03..33a95f9214e477cba0d3c788c26458fc00003b0e 100644 (file)
@@ -61,6 +61,9 @@ typedef enum {
     TMM_STATSLOGGER,
     TMM_RECEIVENFLOG,
     TMM_DECODENFLOG,
+    TMM_RECEIVEWINDIVERT,
+    TMM_VERDICTWINDIVERT,
+    TMM_DECODEWINDIVERT,
 
     TMM_FLOWMANAGER,
     TMM_FLOWRECYCLER,
index 6a394da3fa8e2d46c22033de295ba1757b8cacb2..7fc7dd6f178ffb9dbc69e0c5024de5742e64cbcc 100644 (file)
@@ -352,6 +352,10 @@ const char * SCErrorToString(SCError err)
         CASE_CODE (SC_WARN_JA3_DISABLED);
         CASE_CODE (SC_ERR_PCAP_LOG_COMPRESS);
         CASE_CODE (SC_ERR_FSEEK);
+        CASE_CODE (SC_ERR_WINDIVERT_GENERIC);
+        CASE_CODE (SC_ERR_WINDIVERT_NOSUPPORT);
+        CASE_CODE (SC_ERR_WINDIVERT_INVALID_FILTER);
+        CASE_CODE (SC_ERR_WINDIVERT_TOOLONG_FILTER);
 
         CASE_CODE (SC_ERR_MAX);
     }
index 4654f046fe69b0f505ffd2e64bdd50a20c3ced45..8e9015753f680f370cdb01bae451f308c3316e20 100644 (file)
@@ -342,6 +342,10 @@ typedef enum {
     SC_WARN_JA3_DISABLED,
     SC_ERR_PCAP_LOG_COMPRESS,
     SC_ERR_FSEEK,
+    SC_ERR_WINDIVERT_GENERIC,
+    SC_ERR_WINDIVERT_NOSUPPORT,
+    SC_ERR_WINDIVERT_INVALID_FILTER,
+    SC_ERR_WINDIVERT_TOOLONG_FILTER,
 
     SC_ERR_MAX,
 } SCError;
index 22de89b45677342b432d57174f9db58afa9864d7..dbbda654f64b91e3131906ac9b47868ab37a720c 100644 (file)
 #include <net/if.h>
 #endif
 
+#ifdef OS_WIN32
+#include "win32-syscall.h"
+#endif
+
 #include "util-ioctl.h"
 
 /**
@@ -77,6 +81,7 @@ static int GetIfaceMaxHWHeaderLength(const char *pcap_dev)
     return 8 + SLL_HEADER_LEN;
 }
 
+
 /**
  * \brief output the link MTU
  *
@@ -85,7 +90,7 @@ static int GetIfaceMaxHWHeaderLength(const char *pcap_dev)
  */
 int GetIfaceMTU(const char *pcap_dev)
 {
-#ifdef SIOCGIFMTU
+#if defined SIOCGIFMTU
     struct ifreq ifr;
     int fd;
 
@@ -106,6 +111,8 @@ int GetIfaceMTU(const char *pcap_dev)
     SCLogInfo("Found an MTU of %d for '%s'", ifr.ifr_mtu,
             pcap_dev);
     return ifr.ifr_mtu;
+#elif defined OS_WIN32
+    return GetIfaceMTUWin32(pcap_dev);
 #else
     /* ioctl is not defined, let's pretend returning 0 is ok */
     return 0;
@@ -675,6 +682,8 @@ int GetIfaceOffloading(const char *dev, int csum, int other)
     return GetIfaceOffloadingLinux(dev, csum, other);
 #elif defined SIOCGIFCAP
     return GetIfaceOffloadingBSD(dev);
+#elif defined OS_WIN32
+    return GetIfaceOffloadingWin32(dev, csum, other);
 #else
     return 0;
 #endif
@@ -686,6 +695,8 @@ int DisableIfaceOffloading(LiveDevice *dev, int csum, int other)
     return DisableIfaceOffloadingLinux(dev, csum, other);
 #elif defined SIOCSIFCAP
     return DisableIfaceOffloadingBSD(dev);
+#elif defined OS_WIN32
+    return DisableIfaceOffloadingWin32(dev, csum, other);
 #else
     return 0;
 #endif
@@ -699,6 +710,8 @@ void RestoreIfaceOffloading(LiveDevice *dev)
         RestoreIfaceOffloadingLinux(dev);
 #elif defined SIOCSIFCAP
         RestoreIfaceOffloadingBSD(dev);
+#elif defined OS_WIN32
+        RestoreIfaceOffloadingWin32(dev);
 #endif
     }
 }
index 2d0c74740dd88b941c9ee1c33aa4a9b2f66296e3..50884389724611911d9cfd9cbc3c5a3225842f98 100644 (file)
 #include "suricata-common.h"
 #include "util-device.h"
 
+#ifdef OS_WIN32
+#include "win32-syscall.h"
+#endif
+
 int GetIfaceMTU(const char *pcap_dev);
 int GetIfaceMaxPacketSize(const char *pcap_dev);
 int GetIfaceOffloading(const char *dev, int csum, int other);
index f654f4b5c0ca23d2724499ced04efaf64c207105..c0d72f2d2d8970d47fa556cb2c9a807f956532d5 100644 (file)
@@ -268,7 +268,17 @@ const char *PrintInet(int af, const void *src, char *dst, socklen_t size)
 {
     switch (af) {
         case AF_INET:
+#if defined(OS_WIN32) && NTDDI_VERSION >= NTDDI_VISTA
+{
+            // because Windows has to provide a non-conformant inet_ntop, of
+            // course!
+            struct in_addr _src;
+            memcpy(&_src, src, sizeof(struct in_addr));
+            return inet_ntop(af, &_src, dst, size);
+}
+#else
             return inet_ntop(af, src, dst, size);
+#endif
         case AF_INET6:
             /* Format IPv6 without deleting zeroes */
             return PrintInetIPv6(src, dst, size);
index b2945e53b69c1ce1fde8e166416218f0580dddb9..eef70e2c3ea6e466e199994bd144afa4e37f41a8 100644 (file)
@@ -27,6 +27,8 @@
 #include "suricata-common.h"
 #include "util-random.h"
 
+
+#if !(defined(HAVE_WINCRYPT_H) &&  defined(OS_WIN32))
 #if defined(HAVE_CLOCK_GETTIME)
 
 static long int RandomGetClock(void)
@@ -39,7 +41,7 @@ static long int RandomGetClock(void)
     return value;
 }
 
-#elif !(defined(HAVE_WINCRYPT_H) &&  defined(OS_WIN32))
+#else
 
 static long int RandomGetPosix(void)
 {
@@ -53,6 +55,7 @@ static long int RandomGetPosix(void)
 }
 
 #endif
+#endif /* !(defined(HAVE_WINCRYPT_H) &&  defined(OS_WIN32)) */
 
 #if defined(HAVE_WINCRYPT_H) && defined(OS_WIN32)
 #include <wincrypt.h>
@@ -63,9 +66,21 @@ long int RandomGet(void)
         return 0;
 
     HCRYPTPROV p;
-    if (!(CryptAcquireContext(&p, NULL, NULL,
-                PROV_RSA_FULL, 0))) {
-        return -1;
+    if (!CryptAcquireContext(&p, NULL, NULL, PROV_RSA_FULL, 0)) {
+        DWORD err = GetLastError();
+        SCLogDebug("CryptAcquireContext error: %" PRIu32, (uint32_t)err);
+        if (err == (DWORD)NTE_BAD_KEYSET) {
+            /* The key doesn't exist yet, create it */
+            if (!CryptAcquireContext(&p, NULL, NULL, PROV_RSA_FULL,
+                                     CRYPT_NEWKEYSET)) {
+
+                SCLogDebug("CryptAcquireContext error: %" PRIu32,
+                           (uint32_t)err);
+                return -1;
+            }
+        } else {
+            return -1;
+        }
     }
 
     long int value = 0;
index b5163436d709de03172f2dfc955c3ce970cd2628..e0475ec611f0cb1f281b303faa2b3477b64d5d44 100644 (file)
@@ -291,7 +291,7 @@ recurse:
                                        sse *= 10;
                                        sse += *bp++ - '0';
                                        rulim /= 10;
-                               } while ((sse * 10 <= TIME_MAX) &&
+                               } while (((uint64_t)sse * 10 <= TIME_MAX) &&
                                         rulim && *bp >= '0' && *bp <= '9');
 
                                if (sse < 0 || (uint64_t)sse > TIME_MAX) {
index ed52eb5b7a346f417a20cf8e7a0b1b87f72dbc45..1dc28a69268a3565307e1513cef296e441af0e63 100644 (file)
@@ -52,6 +52,8 @@ void unsetenv(const char *name)
     SCFree(str);
 }
 
+/* these functions have been defined on Vista and later */
+#if NTDDI_VERSION < NTDDI_VISTA
 const char* inet_ntop(int af, const void *src, char *dst, uint32_t cnt)
 {
     if (af == AF_INET)
@@ -116,5 +118,6 @@ int inet_pton(int af, const char *src, void *dst)
 
     return -1;
 }
+#endif
 
 #endif /* OS_WIN32 */
index 7242f9c44865a2a697b543817a5a705b9f139211..24bb42e31d3b674bf981b583e0b43950857c4fc1 100644 (file)
@@ -24,6 +24,8 @@
 #ifndef __WIN32_MISC_H__
 #define __WIN32_MISC_H__
 
+#include <sdkddkver.h>
+
 #define index strchr
 #define rindex strrchr
 
 void setenv(const char *name, const char *value, int overwrite);
 void unsetenv(const char *name);
 
+#if NTDDI_VERSION < NTDDI_VISTA
 const char* inet_ntop(int af, const void *src, char *dst, uint32_t cnt);
 int inet_pton(int af, const char *src, void *dst);
+#endif
 
 #define geteuid() (0)
 
diff --git a/src/win32-syscall.c b/src/win32-syscall.c
new file mode 100644 (file)
index 0000000..b17674c
--- /dev/null
@@ -0,0 +1,1691 @@
+/* Copyright (C) 2018 Open Information Security Foundation\r
+ *\r
+ * You can copy, redistribute or modify this Program under the terms of\r
+ * the GNU General Public License version 2 as published by the Free\r
+ * Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * version 2 along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA\r
+ * 02110-1301, USA.\r
+ */\r
+\r
+/**\r
+ * \file\r
+ *\r
+ * \author Jacob Masen-Smith <jacob@evengx.com>\r
+ *\r
+ * Isolation for WMI/COM functionality\r
+ *\r
+ * References:\r
+ * https://msdn.microsoft.com/en-us/library/aa390421(v=vs.85).aspx\r
+ * https://blogs.msdn.microsoft.com/ndis/2015/03/21/mapping-from-ndis-oids-to-wmi-classes/\r
+ * https://stackoverflow.com/questions/1431103/how-to-obtain-data-from-wmi-using-a-c-application\r
+ * https://docs.microsoft.com/en-us/windows-hardware/drivers/network/oid-tcp-offload-parameters\r
+ * https://wutils.com/wmi/root/wmi/ms_409/msndis_tcpoffloadcurrentconfig/\r
+ * https://docs.microsoft.com/en-us/windows-hardware/drivers/network/oid-tcp-offload-current-config\r
+ * https://wutils.com/wmi/root/wmi/msndis_tcpoffloadparameters/\r
+ */\r
+\r
+#ifdef OS_WIN32\r
+\r
+#include <inttypes.h>\r
+#include <stdbool.h>\r
+\r
+// clang-format off\r
+#include <winsock2.h>\r
+#include <windows.h>\r
+#include <wbemidl.h>\r
+#include <strsafe.h>\r
+#include <ntddndis.h>\r
+#include <ws2ipdef.h>\r
+#include <iphlpapi.h>\r
+// clang-format on\r
+\r
+/* Windows strsafe.h defines _snprintf as an undefined warning type */\r
+#undef _snprintf\r
+#define _snprintf StringCbPrintfA\r
+\r
+#include "util-debug.h"\r
+#include "util-device.h"\r
+#include "util-mem.h"\r
+#include "util-unittest.h"\r
+\r
+#include "suricata.h"\r
+\r
+#include "win32-syscall.h"\r
+\r
+/**\r
+ * \brief return only the GUID portion of the name\r
+ */\r
+static const char *StripPcapPrefix(const char *pcap_dev)\r
+{\r
+    return strchr(pcap_dev, '{');\r
+}\r
+\r
+/**\r
+ * \brief get the adapter address list, which includes IP status/details\r
+ *\r
+ * Clients MUST FREE the returned list to avoid memory leaks.\r
+ */\r
+uint32_t Win32GetAdaptersAddresses(IP_ADAPTER_ADDRESSES **pif_info_list)\r
+{\r
+    DWORD err = NO_ERROR;\r
+    IP_ADAPTER_ADDRESSES *if_info_list;\r
+\r
+    ULONG size = 0;\r
+    err = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &size);\r
+    if (err != ERROR_BUFFER_OVERFLOW) {\r
+        return err;\r
+    }\r
+    if_info_list = SCMalloc((size_t)size);\r
+    if (if_info_list == NULL) {\r
+        return ERROR_NOT_ENOUGH_MEMORY;\r
+    }\r
+    err = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, if_info_list, &size);\r
+    if (err != NO_ERROR) {\r
+        SCFree(if_info_list);\r
+        return err;\r
+    }\r
+\r
+    *pif_info_list = if_info_list;\r
+    return NO_ERROR;\r
+}\r
+\r
+uint32_t Win32FindAdapterAddresses(IP_ADAPTER_ADDRESSES *if_info_list,\r
+                                   const char *adapter_name,\r
+                                   IP_ADAPTER_ADDRESSES **pif_info)\r
+{\r
+    DWORD ret = NO_ERROR;\r
+    adapter_name = StripPcapPrefix(adapter_name);\r
+    *pif_info = NULL;\r
+\r
+    for (IP_ADAPTER_ADDRESSES *current = if_info_list; current != NULL;\r
+         current = current->Next) {\r
+\r
+        /* if we find the adapter, return that data */\r
+        if (strncmp(adapter_name, current->AdapterName, strlen(adapter_name)) ==\r
+            0) {\r
+\r
+            *pif_info = current;\r
+            break;\r
+        }\r
+    }\r
+\r
+    if (*pif_info == NULL) {\r
+        ret = ERROR_NOT_FOUND;\r
+    }\r
+\r
+    return ret;\r
+}\r
+\r
+#if NTDDI_VERSION < NTDDI_VISTA\r
+\r
+int GetIfaceMTUWin32(const char *pcap_dev) { return 0; }\r
+int GetGlobalMTUWin32(void) { return 0; }\r
+\r
+int GetIfaceOffloadingWin32(const char *ifname, int csum, int other)\r
+{\r
+    SCLogWarning(SC_ERR_SYSCALL, "Suricata not targeted for Windows Vista or "\r
+                                 "higher. Network offload interrogation not "\r
+                                 "available.");\r
+    return -1;\r
+}\r
+int DisableIfaceOffloadingWin32(LiveDevice *ldev, int csum, int other)\r
+{\r
+    SCLogWarning(SC_ERR_SYSCALL, "Suricata not targeted for Windows Vista or "\r
+                                 "higher. Network offload interrogation not "\r
+                                 "available.");\r
+    return -1;\r
+}\r
+int RestoreIfaceOffloadingWin32(LiveDevice *ldev)\r
+{\r
+    SCLogWarning(SC_ERR_SYSCALL, "Suricata not targeted for Windows Vista or "\r
+                                 "higher. Network offload interrogation not "\r
+                                 "available.");\r
+    return -1;\r
+}\r
+\r
+#else /* NTDDI_VERSION >= NTDDI_VISTA */\r
+\r
+static HMODULE wmiutils_dll = NULL;\r
+\r
+/**\r
+ * \brief obtain the WMI utilities DLL\r
+ */\r
+static HMODULE WmiUtils(void)\r
+{\r
+    if (wmiutils_dll == NULL) {\r
+        wmiutils_dll =\r
+                LoadLibraryA("C:\\Windows\\System32\\wbem\\wmiutils.dll");\r
+    }\r
+\r
+    return wmiutils_dll;\r
+}\r
+\r
+/**\r
+ * \brief allocate a BSTR from a converted unsigned integer\r
+ */\r
+static BSTR utob(uint64_t ui)\r
+{\r
+    wchar_t buf[20];\r
+    _ui64tow(ui, buf, 10);\r
+    return SysAllocString(buf);\r
+}\r
+\r
+/**\r
+ * \brief Get the win32/wmi error string\r
+ *\r
+ * The caller should use the LocalFree function on the returned pointer to free\r
+ * the buffer when it is no longer needed.\r
+ */\r
+const char *Win32GetErrorString(DWORD error_code, HMODULE ext_module)\r
+{\r
+    char *error_string = NULL;\r
+\r
+    DWORD flags =\r
+            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS;\r
+    if (ext_module != NULL) {\r
+        flags |= FORMAT_MESSAGE_FROM_HMODULE;\r
+    } else {\r
+        flags |= FORMAT_MESSAGE_FROM_SYSTEM;\r
+    }\r
+\r
+    FormatMessageA(flags, ext_module, error_code, 0, (LPTSTR)&error_string, 0,\r
+                   NULL);\r
+\r
+    if (error_string == NULL) {\r
+        return "";\r
+    }\r
+\r
+    error_string[strlen(error_string) - 2] = 0; // remove line breaks\r
+\r
+    return error_string;\r
+}\r
+\r
+#ifdef DEBUG\r
+#define Win32HResultLogDebug(hr)                                               \\r
+    _Win32HResultLog(SC_LOG_DEBUG, (hr), __FILE__, __FUNCTION__, __LINE__)\r
+#else\r
+#define Win32HResultLogDebug(hr)\r
+#endif /* DEBUG */\r
+\r
+/**\r
+ * \brief log an HRESULT\r
+ */\r
+static void _Win32HResultLog(SCLogLevel level, HRESULT hr, const char *file,\r
+                             const char *function, const int line)\r
+{\r
+    const char *err_str = Win32GetErrorString(hr, WmiUtils());\r
+    SCLog(level, file, function, line, "HRESULT: %s (0x%08" PRIx32 ")", err_str,\r
+          (uint32_t)(hr));\r
+    LocalFree((LPVOID)err_str);\r
+}\r
+\r
+/**\r
+ * \brief log a WBEM error\r
+ */\r
+#define WbemLogDebug(hr) (_WbemLogDebug)((hr), __FILE__, __FUNCTION__, __LINE__)\r
+\r
+static void _WbemLogDebug(HRESULT hr, const char *file, const char *function,\r
+                          const int line)\r
+{\r
+#ifdef DEBUG\r
+    IErrorInfo *err_info;\r
+    BSTR err_description;\r
+    char *err_description_mb = NULL;\r
+\r
+    _Win32HResultLog(SC_LOG_DEBUG, hr, file, function, line);\r
+\r
+    GetErrorInfo(0, &err_info);\r
+    if (!SUCCEEDED(\r
+                err_info->lpVtbl->GetDescription(err_info, &err_description))) {\r
+        // not much to do when your error log errors out...\r
+        goto release;\r
+    }\r
+\r
+    err_description_mb = SCMalloc(SysStringLen(err_description) + 1);\r
+\r
+    if (err_description_mb == NULL) {\r
+        // not much to do when your error log errors out...\r
+        goto release;\r
+    }\r
+\r
+    // do the actual multibyte conversion\r
+    err_description_mb[SysStringLen(err_description)] = 0;\r
+    wcstombs(err_description_mb, err_description,\r
+             SysStringLen(err_description));\r
+\r
+    // log the description\r
+    SCLog(SC_LOG_DEBUG, file, function, line, "WBEM error: %s",\r
+          err_description_mb);\r
+\r
+release:\r
+    SCFree(err_description_mb);\r
+    SysFreeString(err_description);\r
+#endif /* DEBUG */\r
+}\r
+\r
+/**\r
+ * \brief get the maximum transmissible unit for the specified pcap device name\r
+ */\r
+int GetIfaceMTUWin32(const char *pcap_dev)\r
+{\r
+    DWORD err = NO_ERROR;\r
+\r
+    int mtu = 0;\r
+\r
+    IP_ADAPTER_ADDRESSES *if_info_list = NULL, *if_info = NULL;\r
+    err = Win32GetAdaptersAddresses(&if_info_list);\r
+    if (err != NO_ERROR) {\r
+        mtu = -1;\r
+        goto release;\r
+    }\r
+    err = Win32FindAdapterAddresses(if_info_list, pcap_dev, &if_info);\r
+    if (err != NO_ERROR) {\r
+        mtu = -1;\r
+        goto release;\r
+    }\r
+\r
+    mtu = if_info->Mtu;\r
+\r
+release:\r
+    SCFree(if_info_list);\r
+\r
+    if (err != S_OK) {\r
+        const char *errbuf = Win32GetErrorString(err, WmiUtils());\r
+        SCLogWarning(SC_ERR_SYSCALL,\r
+                     "Failure when trying to get MTU via syscall for '%s': %s "\r
+                     "(0x%08" PRIx32 ")",\r
+                     pcap_dev, errbuf, (uint32_t)err);\r
+        LocalFree((LPVOID)errbuf);\r
+    } else {\r
+        SCLogInfo("Found an MTU of %d for '%s'", mtu, pcap_dev);\r
+    }\r
+\r
+    return mtu;\r
+}\r
+\r
+/**\r
+ * \brief get the maximum transmissible unit for all devices on the system\r
+ */\r
+int GetGlobalMTUWin32()\r
+{\r
+    uint32_t mtu = 0;\r
+\r
+    DWORD err = NO_ERROR;\r
+    IP_ADAPTER_ADDRESSES *if_info_list = NULL;\r
+\r
+    /* get a list of all adapters' data */\r
+    err = Win32GetAdaptersAddresses(&if_info_list);\r
+    if (err != NO_ERROR) {\r
+        goto fail;\r
+    }\r
+\r
+    /* now search for the right adapter in the list */\r
+    IP_ADAPTER_ADDRESSES *if_info = NULL;\r
+    for (if_info = if_info_list; if_info != NULL; if_info = if_info->Next) {\r
+        /* -1 (uint) is an invalid value */\r
+        if (if_info->Mtu == (uint32_t)-1) {\r
+            continue;\r
+        }\r
+\r
+        /* we want to return the largest MTU value so we allocate enough */\r
+        mtu = max(mtu, if_info->Mtu);\r
+    }\r
+\r
+    SCFree(if_info_list);\r
+\r
+    SCLogInfo("Found a global MTU of %" PRIu32, mtu);\r
+    return (int)mtu;\r
+\r
+fail:\r
+    SCFree(if_info_list);\r
+\r
+    const char *errbuf = NULL;\r
+    FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |\r
+                           FORMAT_MESSAGE_IGNORE_INSERTS,\r
+                   NULL, err, 0, (LPTSTR)&errbuf, 0, NULL);\r
+\r
+    SCLogWarning(\r
+            SC_ERR_SYSCALL,\r
+            "Failure when trying to get global MTU via syscall: %s (%" PRId32\r
+            ")",\r
+            errbuf, (uint32_t)err);\r
+\r
+    return -1;\r
+}\r
+\r
+#define ReleaseObject(objptr)                                                  \\r
+    do {                                                                       \\r
+        if ((objptr) != NULL) {                                                \\r
+            (objptr)->lpVtbl->Release(objptr);                                 \\r
+            (objptr) = NULL;                                                   \\r
+        }                                                                      \\r
+    } while (0);\r
+\r
+typedef enum Win32TcpOffloadFlags_ {\r
+    WIN32_TCP_OFFLOAD_FLAG_NONE = 0,\r
+    WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX = 1,\r
+    WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX = 1 << 1,\r
+    WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX = 1 << 2,\r
+    WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX = 1 << 3,\r
+    WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4 = 1 << 4,\r
+    WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4 = 1 << 5,\r
+    WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6 = 1 << 6,\r
+\r
+    /* aggregates */\r
+    WIN32_TCP_OFFLOAD_FLAG_CSUM = WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX |\r
+                                  WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX |\r
+                                  WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX |\r
+                                  WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX,\r
+    WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4 = WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX |\r
+                                      WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX,\r
+    WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6 = WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX |\r
+                                      WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX,\r
+    WIN32_TCP_OFFLOAD_FLAG_LSO = WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4 |\r
+                                 WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4 |\r
+                                 WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6,\r
+} Win32TcpOffloadFlags;\r
+\r
+typedef struct ComInstance_ {\r
+    IWbemLocator *locator;\r
+    IWbemServices *services;\r
+} ComInstance;\r
+\r
+/**\r
+ * \brief Creates a COM instance connected to the specified resource\r
+ */\r
+static HRESULT ComInstanceInit(ComInstance *instance, LPCWSTR resource)\r
+{\r
+    HRESULT hr = S_OK;\r
+\r
+    instance->locator = NULL;\r
+    instance->services = NULL;\r
+\r
+    BSTR resource_bstr = SysAllocString(resource);\r
+    if (resource_bstr == NULL) {\r
+        hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY);\r
+        SCLogWarning(SC_ERR_SYSCALL, "Failed to allocate BSTR");\r
+        goto release;\r
+    }\r
+\r
+    /* connect to COM */\r
+    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);\r
+    if (hr == S_FALSE) {\r
+        /* already initialized */\r
+        hr = S_OK;\r
+    } else {\r
+        if (hr != S_OK) {\r
+            SCLogWarning(SC_ERR_SYSCALL,\r
+                         "COM CoInitializeEx failed: 0x%" PRIx32, (uint32_t)hr);\r
+            goto release;\r
+        }\r
+        hr = CoInitializeSecurity(\r
+                NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT,\r
+                RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);\r
+        if (hr != S_OK) {\r
+            SCLogWarning(SC_ERR_SYSCALL,\r
+                         "COM CoInitializeSecurity failed: 0x%" PRIx32,\r
+                         (uint32_t)hr);\r
+            goto release;\r
+        }\r
+    }\r
+\r
+    /* connect to WMI */\r
+    hr = CoCreateInstance(&CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER,\r
+                          &IID_IWbemLocator, (LPVOID *)&instance->locator);\r
+    if (hr != S_OK) {\r
+        SCLogWarning(SC_ERR_SYSCALL, "COM CoCreateInstance failed: 0x%" PRIx32,\r
+                     (uint32_t)hr);\r
+        goto release;\r
+    }\r
+    hr = instance->locator->lpVtbl->ConnectServer(\r
+            instance->locator, resource_bstr, NULL, NULL, NULL, 0, NULL, NULL,\r
+            &instance->services);\r
+    if (hr != S_OK) {\r
+        SCLogWarning(SC_ERR_SYSCALL, "COM ConnectServer failed: 0x%" PRIx32,\r
+                     (uint32_t)hr);\r
+        goto release;\r
+    }\r
+\r
+release:\r
+    SysFreeString(resource_bstr);\r
+\r
+    return hr;\r
+}\r
+\r
+/**\r
+ * \brief Releases resources for a COM instance.\r
+ */\r
+static void ComInstanceRelease(ComInstance *instance)\r
+{\r
+    if (instance == NULL) {\r
+        return;\r
+    }\r
+    ReleaseObject(instance->services);\r
+    ReleaseObject(instance->locator);\r
+}\r
+\r
+/**\r
+ * \brief obtains a class definition from COM services\r
+ */\r
+static HRESULT GetWbemClass(ComInstance *instance, LPCWSTR name,\r
+                            IWbemClassObject **p_class)\r
+{\r
+    HRESULT hr = WBEM_S_NO_ERROR;\r
+    BSTR name_bstr = NULL;\r
+\r
+    if (instance == NULL || name == NULL || p_class == NULL ||\r
+        *p_class != NULL) {\r
+        hr = HRESULT_FROM_WIN32(E_INVALIDARG);\r
+        Win32HResultLogDebug(hr);\r
+        goto release;\r
+    }\r
+\r
+    /* allocate name string */\r
+    name_bstr = SysAllocString(name);\r
+    if (name_bstr == NULL) {\r
+        hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY);\r
+        SCLogWarning(SC_ERR_SYSCALL, "Failed to allocate BSTR");\r
+        goto release;\r
+    }\r
+\r
+    /* obtain object */\r
+    hr = instance->services->lpVtbl->GetObject(instance->services, name_bstr,\r
+                                               WBEM_FLAG_RETURN_WBEM_COMPLETE,\r
+                                               NULL, p_class, NULL);\r
+    if (hr != S_OK) {\r
+        WbemLogDebug(hr);\r
+        SCLogWarning(SC_ERR_SYSCALL, "WMI GetObject failed: 0x%" PRIx32,\r
+                     (uint32_t)hr);\r
+        goto release;\r
+    }\r
+\r
+release:\r
+    SysFreeString(name_bstr);\r
+\r
+    return hr;\r
+}\r
+\r
+/**\r
+ * \brief spawns an empty class instance of the specified type\r
+ */\r
+static HRESULT GetWbemClassInstance(ComInstance *instance, LPCWSTR name,\r
+                                    IWbemClassObject **p_instance)\r
+{\r
+    HRESULT hr = WBEM_S_NO_ERROR;\r
+\r
+    IWbemClassObject *class = NULL;\r
+\r
+    hr = GetWbemClass(instance, name, &class);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        goto release;\r
+    }\r
+\r
+    hr = class->lpVtbl->SpawnInstance(class, 0, p_instance);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        SCLogWarning(SC_ERR_SYSCALL, "WMI SpawnInstance failed: 0x%" PRIx32,\r
+                     (uint32_t)hr);\r
+        goto release;\r
+    }\r
+\r
+release:\r
+    return hr;\r
+}\r
+\r
+typedef struct WbemMethod_ {\r
+    ComInstance *com_instance;\r
+\r
+    BSTR method_name;\r
+\r
+    IWbemClassObject *in_params, *out_params;\r
+} WbemMethod;\r
+\r
+/**\r
+ * \brief initializes resources for a WMI method handle\r
+ */\r
+static HRESULT GetWbemMethod(ComInstance *com_instance, LPCWSTR class_name,\r
+                             LPCWSTR method_name, WbemMethod *method)\r
+{\r
+    HRESULT hr = S_OK;\r
+    IWbemClassObject *class = NULL;\r
+\r
+    method->com_instance = com_instance;\r
+\r
+    BSTR class_name_bstr = SysAllocString(class_name);\r
+    if (class_name_bstr == NULL) {\r
+        hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY);\r
+        SCLogWarning(SC_ERR_SYSCALL, "Failed to allocate BSTR");\r
+        goto release;\r
+    }\r
+    method->method_name = SysAllocString(method_name);\r
+    if (method->method_name == NULL) {\r
+        hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY);\r
+        SCLogWarning(SC_ERR_SYSCALL, "Failed to allocate BSTR");\r
+        goto release;\r
+    }\r
+\r
+    /* find our class definition to retrieve parameters */\r
+    hr = GetWbemClass(com_instance, class_name, &class);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        goto release;\r
+    }\r
+\r
+    /* find the method on the retrieved class */\r
+    hr = class->lpVtbl->GetMethod(class, method_name, 0, &method->in_params,\r
+                                  &method->out_params);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        SCLogWarning(SC_ERR_SYSCALL, "WMI GetMethod failed: 0x%" PRIx32,\r
+                     (uint32_t)hr);\r
+        goto release;\r
+    }\r
+\r
+release:\r
+    ReleaseObject(class);\r
+\r
+    SysFreeString(class_name_bstr);\r
+\r
+    return hr;\r
+}\r
+\r
+/**\r
+ * \brief Releases resources for a WMI method handle\r
+ */\r
+static void WbemMethodRelease(WbemMethod *method)\r
+{\r
+    if (method == NULL) {\r
+        return;\r
+    }\r
+    ReleaseObject(method->in_params);\r
+    ReleaseObject(method->out_params);\r
+\r
+    SysFreeString(method->method_name);\r
+}\r
+\r
+typedef struct WbemMethodCall_ {\r
+    WbemMethod *method;\r
+\r
+    BSTR instance_path;\r
+\r
+    IWbemClassObject *in_params;\r
+} WbemMethodCall;\r
+\r
+/**\r
+ * \brief generates a single-use WMI method call\r
+ */\r
+static HRESULT GetWbemMethodCall(WbemMethod *method, LPCWSTR instance_path,\r
+                                 WbemMethodCall *call)\r
+{\r
+    HRESULT hr = S_OK;\r
+\r
+    call->method = method;\r
+    call->instance_path = SysAllocString(instance_path);\r
+    if (call->instance_path == NULL) {\r
+        hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY);\r
+        SCLogWarning(SC_ERR_SYSCALL, "Failed to allocate BSTR: 0x%" PRIx32,\r
+                     (uint32_t)hr);\r
+        goto release;\r
+    }\r
+\r
+    /* make an instance of the in/out params */\r
+    hr = method->in_params->lpVtbl->SpawnInstance(method->in_params, 0,\r
+                                                  &call->in_params);\r
+    if (hr != S_OK) {\r
+        WbemLogDebug(hr);\r
+        SCLogWarning(SC_ERR_SYSCALL,\r
+                     "WMI SpawnInstance failed on in_params: 0x%" PRIx32,\r
+                     (uint32_t)hr);\r
+        goto release;\r
+    }\r
+\r
+release:\r
+    return hr;\r
+}\r
+\r
+/**\r
+ *  \brief releases the WMI method call resources\r
+ */\r
+static void WbemMethodCallRelease(WbemMethodCall *call)\r
+{\r
+    if (call == NULL) {\r
+        return;\r
+    }\r
+    ReleaseObject(call->in_params);\r
+\r
+    SysFreeString(call->instance_path);\r
+}\r
+\r
+/**\r
+ * \brief executes the method after the client has set applicable parameters.\r
+ */\r
+static HRESULT WbemMethodCallExec(WbemMethodCall *call,\r
+                                  IWbemClassObject **p_out_params)\r
+{\r
+    HRESULT hr = S_OK;\r
+\r
+    hr = call->method->com_instance->services->lpVtbl->ExecMethod(\r
+            call->method->com_instance->services, call->instance_path,\r
+            call->method->method_name, 0, NULL, call->in_params, p_out_params,\r
+            NULL);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        SCLogDebug("WMI ExecMethod failed: 0x%" PRIx32, (uint32_t)hr);\r
+        goto release;\r
+    }\r
+\r
+release:\r
+    return hr;\r
+}\r
+\r
+/**\r
+ * Obtains an IWbemClassObject named property of a parent IWbemClassObject\r
+ */\r
+static HRESULT WbemGetSubObject(IWbemClassObject *object, LPCWSTR property_name,\r
+                                IWbemClassObject **sub_object)\r
+{\r
+    HRESULT hr = S_OK;\r
+\r
+    VARIANT out_var;\r
+    VariantInit(&out_var);\r
+    hr = object->lpVtbl->Get(object, property_name, 0, &out_var, NULL, NULL);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        goto release;\r
+    }\r
+\r
+    IUnknown *unknown = V_UNKNOWN(&out_var);\r
+    hr = unknown->lpVtbl->QueryInterface(unknown, &IID_IWbemClassObject,\r
+                                         (void **)sub_object);\r
+    if (hr != S_OK) {\r
+        SCLogWarning(SC_ERR_SYSCALL,\r
+                     "WMI QueryInterface (IWbemClassObject) failed: 0x%" PRIx32,\r
+                     (uint32_t)hr);\r
+        goto release;\r
+    }\r
+\r
+release:\r
+    VariantClear(&out_var);\r
+    return hr;\r
+}\r
+\r
+/**\r
+ * Obtains an Encapsulation value from an MSNdis_WmiOffload property\r
+ */\r
+static HRESULT GetEncapsulation(IWbemClassObject *object, LPCWSTR category,\r
+                                LPCWSTR subcategory, ULONG *encapsulation)\r
+{\r
+    HRESULT hr = WBEM_S_NO_ERROR;\r
+\r
+    IWbemClassObject *category_object = NULL;\r
+    IWbemClassObject *subcategory_object = NULL;\r
+\r
+    VARIANT out_var;\r
+    VariantInit(&out_var);\r
+\r
+    /* get category object */\r
+    hr = WbemGetSubObject(object, category, &category_object);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        goto release;\r
+    }\r
+\r
+    /* get sub-category object */\r
+    hr = WbemGetSubObject(category_object, subcategory, &subcategory_object);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        goto release;\r
+    }\r
+    hr = subcategory_object->lpVtbl->Get(subcategory_object, L"Encapsulation",\r
+                                         0, &out_var, NULL, NULL);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        goto release;\r
+    }\r
+    *encapsulation = V_UI4(&out_var);\r
+\r
+release:\r
+    VariantClear(&out_var);\r
+    ReleaseObject(subcategory_object);\r
+    ReleaseObject(category_object);\r
+    return hr;\r
+}\r
+\r
+static HRESULT GetIUnknown(IWbemClassObject *object, IUnknown **p_unknown)\r
+{\r
+    HRESULT hr = WBEM_S_NO_ERROR;\r
+\r
+    if (object == NULL || p_unknown == NULL || *p_unknown != NULL) {\r
+        hr = HRESULT_FROM_WIN32(E_INVALIDARG);\r
+        Win32HResultLogDebug(hr);\r
+        goto release;\r
+    }\r
+\r
+    hr = object->lpVtbl->QueryInterface(object, &IID_IUnknown,\r
+                                        (void **)p_unknown);\r
+    if (hr != S_OK) {\r
+        SCLogWarning(SC_ERR_SYSCALL,\r
+                     "WMI QueryInterface (IUnknown) failed: 0x%" PRIx32,\r
+                     (uint32_t)hr);\r
+        goto release;\r
+    }\r
+\r
+release:\r
+    return hr;\r
+}\r
+\r
+static HRESULT BuildNdisObjectHeader(ComInstance *instance, uint8_t type,\r
+                                     uint8_t revision, uint16_t size,\r
+                                     IWbemClassObject **p_ndis_object_header)\r
+{\r
+    HRESULT hr = WBEM_S_NO_ERROR;\r
+\r
+    if (instance == NULL || p_ndis_object_header == NULL ||\r
+        *p_ndis_object_header != NULL) {\r
+\r
+        hr = HRESULT_FROM_WIN32(E_INVALIDARG);\r
+        Win32HResultLogDebug(hr);\r
+        goto release;\r
+    }\r
+\r
+    /* obtain object */\r
+    hr = GetWbemClassInstance(instance, L"MSNdis_ObjectHeader",\r
+                              p_ndis_object_header);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        goto release;\r
+    }\r
+\r
+    VARIANT param_variant;\r
+    VariantInit(&param_variant);\r
+    IWbemClassObject *ndis_object_header = *p_ndis_object_header;\r
+\r
+    /* set parameters */\r
+    V_VT(&param_variant) = VT_UI1;\r
+    V_UI1(&param_variant) = type;\r
+    hr = ndis_object_header->lpVtbl->Put(ndis_object_header, L"Type", 0,\r
+                                         &param_variant, 0);\r
+    VariantClear(&param_variant);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+\r
+    V_VT(&param_variant) = VT_UI1;\r
+    V_UI1(&param_variant) = revision;\r
+    hr = ndis_object_header->lpVtbl->Put(ndis_object_header, L"Revision", 0,\r
+                                         &param_variant, 0);\r
+    VariantClear(&param_variant);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+\r
+    /* https://docs.microsoft.com/en-us/windows-hardware/drivers/network/ndis-object-version-issues-for-wmi\r
+     */\r
+    V_VT(&param_variant) = VT_I4;\r
+    V_I4(&param_variant) = size;\r
+    hr = ndis_object_header->lpVtbl->Put(ndis_object_header, L"Size", 0,\r
+                                         &param_variant, 0);\r
+    VariantClear(&param_variant);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+\r
+release:\r
+    return hr;\r
+}\r
+\r
+static HRESULT BuildNdisWmiMethodHeader(ComInstance *instance,\r
+                                        uint64_t net_luid, uint32_t port_number,\r
+                                        uint64_t request_id, uint32_t timeout,\r
+                                        IWbemClassObject **p_ndis_method_header)\r
+{\r
+    HRESULT hr = WBEM_S_NO_ERROR;\r
+\r
+    IWbemClassObject *ndis_object_header = NULL;\r
+\r
+    if (instance == NULL || p_ndis_method_header == NULL ||\r
+        *p_ndis_method_header != NULL) {\r
+\r
+        hr = HRESULT_FROM_WIN32(E_INVALIDARG);\r
+        Win32HResultLogDebug(hr);\r
+        goto release;\r
+    }\r
+\r
+    /* obtain object */\r
+    hr = GetWbemClassInstance(instance, L"MSNdis_WmiMethodHeader",\r
+                              p_ndis_method_header);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        goto release;\r
+    }\r
+\r
+    VARIANT param_variant;\r
+    VariantInit(&param_variant);\r
+\r
+    /* get embedded MSNdis_ObjectHeader */\r
+    hr = BuildNdisObjectHeader(instance, NDIS_WMI_OBJECT_TYPE_METHOD,\r
+                               NDIS_WMI_METHOD_HEADER_REVISION_1, 0xFFFF,\r
+                               &ndis_object_header);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        goto release;\r
+    }\r
+    V_VT(&param_variant) = VT_UNKNOWN;\r
+    V_UNKNOWN(&param_variant) = NULL;\r
+    hr = GetIUnknown(ndis_object_header, &V_UNKNOWN(&param_variant));\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        goto release;\r
+    }\r
+\r
+    IWbemClassObject *ndis_method_header = *p_ndis_method_header;\r
+\r
+    /* set parameters */\r
+    hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"Header", 0,\r
+                                         &param_variant, 0);\r
+    VariantClear(&param_variant);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+\r
+    V_VT(&param_variant) = VT_BSTR;\r
+    V_BSTR(&param_variant) = utob(net_luid);\r
+    hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"NetLuid", 0,\r
+                                         &param_variant, 0);\r
+    VariantClear(&param_variant);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+\r
+    V_VT(&param_variant) = VT_BSTR;\r
+    V_BSTR(&param_variant) = utob((uint64_t)port_number);\r
+    hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"PortNumber", 0,\r
+                                         &param_variant, 0);\r
+    VariantClear(&param_variant);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+\r
+    V_VT(&param_variant) = VT_BSTR;\r
+    V_BSTR(&param_variant) = utob(request_id);\r
+    hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"RequestId", 0,\r
+                                         &param_variant, 0);\r
+    VariantClear(&param_variant);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+\r
+    V_VT(&param_variant) = VT_BSTR;\r
+    V_BSTR(&param_variant) = utob((uint64_t)timeout);\r
+    hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"Timeout", 0,\r
+                                         &param_variant, 0);\r
+    VariantClear(&param_variant);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+\r
+    V_VT(&param_variant) = VT_BSTR;\r
+    V_BSTR(&param_variant) = utob((uint64_t)0);\r
+    hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"Padding", 0,\r
+                                         &param_variant, 0);\r
+    VariantClear(&param_variant);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+\r
+release:\r
+    ReleaseObject(ndis_object_header);\r
+\r
+    return hr;\r
+}\r
+\r
+/**\r
+ * \brief polls the NDIS TCP offloading status, namely LSOv1/v2\r
+ */\r
+static HRESULT GetNdisOffload(LPCWSTR if_description, uint32_t *offload_flags)\r
+{\r
+    HRESULT hr = S_OK;\r
+\r
+    ComInstance instance = {};\r
+    WbemMethod method = {};\r
+    WbemMethodCall call = {};\r
+\r
+    IWbemClassObject *ndis_method_header = NULL;\r
+    IWbemClassObject *out_params = NULL;\r
+    IWbemClassObject *ndis_offload = NULL;\r
+\r
+    if (if_description == NULL) {\r
+        SCLogWarning(SC_ERR_SYSCALL, "No description specified for device");\r
+        hr = HRESULT_FROM_WIN32(E_INVALIDARG);\r
+        goto release;\r
+    }\r
+\r
+    LPCWSTR class_name = L"MSNdis_TcpOffloadCurrentConfig";\r
+    LPCWSTR instance_name_fmt = L"%s=\"%s\"";\r
+    size_t n_chars = wcslen(class_name) + wcslen(if_description) +\r
+                     wcslen(instance_name_fmt);\r
+    LPWSTR instance_name = SCMalloc((n_chars + 1) * sizeof(wchar_t));\r
+    if (instance_name == NULL) {\r
+        SCLogWarning(SC_ERR_SYSCALL,\r
+                     "Failed to allocate buffer for instance path");\r
+        goto release;\r
+    }\r
+    instance_name[n_chars] = 0; /* defensively null-terminate */\r
+    hr = StringCchPrintfW(instance_name, n_chars, instance_name_fmt, class_name,\r
+                          if_description);\r
+    if (hr != S_OK) {\r
+        SCLogWarning(SC_ERR_SYSCALL,\r
+                     "Failed to format WMI class instance name: 0x%" PRIx32,\r
+                     (uint32_t)hr);\r
+        goto release;\r
+    }\r
+    /* method name */\r
+    LPCWSTR method_name = L"WmiQueryCurrentOffloadConfig";\r
+\r
+    /* connect to COM/WMI */\r
+    hr = ComInstanceInit(&instance, L"ROOT\\WMI");\r
+    if (hr != S_OK) {\r
+        goto release;\r
+    }\r
+\r
+    /* obtain method */\r
+    hr = GetWbemMethod(&instance, class_name, method_name, &method);\r
+    if (hr != S_OK) {\r
+        goto release;\r
+    }\r
+\r
+    /* make parameter instances */\r
+    hr = GetWbemMethodCall(&method, instance_name, &call);\r
+    if (hr != S_OK) {\r
+        goto release;\r
+    }\r
+\r
+    /* build parameters */\r
+\r
+    VARIANT param_variant;\r
+    VariantInit(&param_variant);\r
+\r
+    /* Make MSNdis_WmiMethodHeader */\r
+    hr = BuildNdisWmiMethodHeader(&instance, 0, 0, 0, 5, &ndis_method_header);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        goto release;\r
+    }\r
+    V_VT(&param_variant) = VT_UNKNOWN;\r
+    V_UNKNOWN(&param_variant) = NULL;\r
+    hr = GetIUnknown(ndis_method_header, &V_UNKNOWN(&param_variant));\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        goto release;\r
+    }\r
+\r
+    /* Set in_params */\r
+    hr = call.in_params->lpVtbl->Put(call.in_params, L"Header", 0,\r
+                                     &param_variant, 0);\r
+    VariantClear(&param_variant);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+\r
+    /* execute the method */\r
+    hr = WbemMethodCallExec(&call, &out_params);\r
+    if (hr != S_OK) {\r
+        size_t if_description_len = wcslen(if_description);\r
+        char *if_description_ansi = SCMalloc(if_description_len + 1);\r
+        if (if_description_ansi == NULL) {\r
+            SCLogWarning(SC_ERR_SYSCALL,\r
+                         "Failed to allocate buffer for interface description");\r
+            goto release;\r
+        }\r
+        if_description_ansi[if_description_len] = 0;\r
+        wcstombs(if_description_ansi, if_description, if_description_len);\r
+        SCLogInfo("Obtaining offload state failed, device \"%s\" may not "\r
+                  "support offload. Error: 0x%" PRIx32,\r
+                  if_description_ansi, (uint32_t)hr);\r
+        SCFree(if_description_ansi);\r
+        Win32HResultLogDebug(hr);\r
+        goto release;\r
+    }\r
+\r
+    /* inspect the result */\r
+    hr = WbemGetSubObject(out_params, L"Offload", &ndis_offload);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        goto release;\r
+    }\r
+    ULONG encapsulation = 0;\r
+\r
+    /* Checksum */\r
+    hr = GetEncapsulation(ndis_offload, L"Checksum", L"IPv4Receive",\r
+                          &encapsulation);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+    if (encapsulation != 0) {\r
+        *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX;\r
+    }\r
+    hr = GetEncapsulation(ndis_offload, L"Checksum", L"IPv4Transmit",\r
+                          &encapsulation);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+    if (encapsulation != 0) {\r
+        *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX;\r
+    }\r
+    hr = GetEncapsulation(ndis_offload, L"Checksum", L"IPv6Receive",\r
+                          &encapsulation);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+    if (encapsulation != 0) {\r
+        *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX;\r
+    }\r
+    hr = GetEncapsulation(ndis_offload, L"Checksum", L"IPv6Transmit",\r
+                          &encapsulation);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+    if (encapsulation != 0) {\r
+        *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX;\r
+    }\r
+\r
+    /* LsoV1 */\r
+    hr = GetEncapsulation(ndis_offload, L"LsoV1", L"WmiIPv4", &encapsulation);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+    if (encapsulation != 0) {\r
+        *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4;\r
+    }\r
+\r
+    /* LsoV2 */\r
+    hr = GetEncapsulation(ndis_offload, L"LsoV2", L"WmiIPv4", &encapsulation);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+    if (encapsulation != 0) {\r
+        *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4;\r
+    }\r
+    hr = GetEncapsulation(ndis_offload, L"LsoV2", L"WmiIPv6", &encapsulation);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+    if (encapsulation != 0) {\r
+        *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6;\r
+    }\r
+\r
+release:\r
+    ReleaseObject(ndis_method_header);\r
+    ReleaseObject(ndis_offload);\r
+    ReleaseObject(out_params);\r
+\r
+    WbemMethodCallRelease(&call);\r
+    WbemMethodRelease(&method);\r
+    ComInstanceRelease(&instance);\r
+\r
+    return hr;\r
+}\r
+\r
+int GetIfaceOffloadingWin32(const char *pcap_dev, int csum, int other)\r
+{\r
+    SCLogDebug("Querying offloading for device %s", pcap_dev);\r
+\r
+    DWORD err = NO_ERROR;\r
+    int ret = 0;\r
+    uint32_t offload_flags = 0;\r
+\r
+    /* WMI uses the description as an identifier... */\r
+    IP_ADAPTER_ADDRESSES *if_info_list = NULL, *if_info = NULL;\r
+    err = Win32GetAdaptersAddresses(&if_info_list);\r
+    if (err != NO_ERROR) {\r
+        ret = -1;\r
+        goto release;\r
+    }\r
+    err = Win32FindAdapterAddresses(if_info_list, pcap_dev, &if_info);\r
+    if (err != NO_ERROR) {\r
+        ret = -1;\r
+        goto release;\r
+    }\r
+    LPWSTR if_description = if_info->Description;\r
+\r
+    /* now query WMI for the offload info */\r
+    err = GetNdisOffload(if_description, &offload_flags);\r
+    if (err != S_OK) {\r
+        ret = -1;\r
+        goto release;\r
+    } else if (offload_flags != 0) {\r
+        if (csum == 1) {\r
+            if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM) != 0) {\r
+                ret = 1;\r
+            }\r
+        }\r
+        if (other == 1) {\r
+            if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSO) != 0) {\r
+                ret = 1;\r
+            }\r
+        }\r
+    }\r
+\r
+    if (ret == 0) {\r
+        SCLogPerf("NIC offloading on %s: Checksum IPv4 Rx: %d Tx: %d IPv6 "\r
+                  "Rx: %d Tx: %d LSOv1 IPv4: %d LSOv2 IPv4: %d IPv6: %d",\r
+                  pcap_dev,\r
+                  (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX) != 0,\r
+                  (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX) != 0,\r
+                  (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX) != 0,\r
+                  (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX) != 0,\r
+                  (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4) != 0,\r
+                  (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4) != 0,\r
+                  (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6) != 0);\r
+    } else {\r
+        SCLogWarning(SC_ERR_NIC_OFFLOADING,\r
+                     "NIC offloading on %s: Checksum IPv4 Rx: %d Tx: %d IPv6 "\r
+                     "Rx: %d Tx: %d LSOv1 IPv4: %d LSOv2 IPv4: %d IPv6: %d",\r
+                     pcap_dev,\r
+                     (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX) != 0,\r
+                     (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX) != 0,\r
+                     (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX) != 0,\r
+                     (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX) != 0,\r
+                     (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4) != 0,\r
+                     (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4) != 0,\r
+                     (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6) != 0);\r
+    }\r
+\r
+release:\r
+    if (ret == -1) {\r
+        const char *err_str = Win32GetErrorString(err, WmiUtils());\r
+        SCLogWarning(SC_ERR_SYSCALL,\r
+                     "Failure when trying to get feature via syscall for '%s': "\r
+                     "%s (0x%08" PRIx32 ")",\r
+                     pcap_dev, err_str, (uint32_t)err);\r
+        LocalFree((LPVOID)err_str);\r
+    }\r
+\r
+    SCFree(if_info_list);\r
+\r
+    return ret;\r
+}\r
+\r
+static HRESULT\r
+BuildNdisTcpOffloadParameters(ComInstance *instance, uint32_t offload_flags,\r
+                              bool enable,\r
+                              IWbemClassObject **p_ndis_tcp_offload_parameters)\r
+{\r
+    HRESULT hr = WBEM_S_NO_ERROR;\r
+\r
+    IWbemClassObject *ndis_object_header = NULL;\r
+\r
+    if (instance == NULL || p_ndis_tcp_offload_parameters == NULL ||\r
+        *p_ndis_tcp_offload_parameters != NULL) {\r
+\r
+        hr = HRESULT_FROM_WIN32(E_INVALIDARG);\r
+        Win32HResultLogDebug(hr);\r
+        goto release;\r
+    }\r
+\r
+    /* obtain object */\r
+    hr = GetWbemClassInstance(instance, L"MSNdis_TcpOffloadParameters",\r
+                              p_ndis_tcp_offload_parameters);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        goto release;\r
+    }\r
+\r
+    VARIANT param_variant;\r
+    VariantInit(&param_variant);\r
+\r
+    /* get embedded MSNdis_ObjectHeader */\r
+    hr = BuildNdisObjectHeader(instance, NDIS_OBJECT_TYPE_DEFAULT,\r
+                               NDIS_OFFLOAD_PARAMETERS_REVISION_1,\r
+                               NDIS_SIZEOF_OFFLOAD_PARAMETERS_REVISION_1,\r
+                               &ndis_object_header);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        goto release;\r
+    }\r
+    V_VT(&param_variant) = VT_UNKNOWN;\r
+    V_UNKNOWN(&param_variant) = NULL;\r
+    hr = GetIUnknown(ndis_object_header, &V_UNKNOWN(&param_variant));\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        goto release;\r
+    }\r
+\r
+    IWbemClassObject *ndis_tcp_offload_parameters =\r
+            *p_ndis_tcp_offload_parameters;\r
+\r
+    /* set parameters */\r
+    hr = ndis_tcp_offload_parameters->lpVtbl->Put(\r
+            ndis_tcp_offload_parameters, L"Header", 0, &param_variant, 0);\r
+    VariantClear(&param_variant);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        Win32HResultLogDebug(hr);\r
+        goto release;\r
+    }\r
+\r
+    /* IPv4 csum */\r
+    V_VT(&param_variant) = VT_BSTR;\r
+    V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);\r
+    if (!enable && (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4) != 0) {\r
+        /* this is basically all disabled cases */\r
+        V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED);\r
+    } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4) ==\r
+               WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4) {\r
+        /* implied enable */\r
+        V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED);\r
+    } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX) != 0) {\r
+        /* implied enable */\r
+        V_BSTR(&param_variant) =\r
+                utob(NDIS_OFFLOAD_PARAMETERS_RX_ENABLED_TX_DISABLED);\r
+    } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX) != 0) {\r
+        /* implied enable */\r
+        V_BSTR(&param_variant) =\r
+                utob(NDIS_OFFLOAD_PARAMETERS_TX_ENABLED_RX_DISABLED);\r
+    }\r
+    hr = ndis_tcp_offload_parameters->lpVtbl->Put(\r
+            ndis_tcp_offload_parameters, L"IPv4Checksum", 0, &param_variant, 0);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+    hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters,\r
+                                                  L"TCPIPv4Checksum", 0,\r
+                                                  &param_variant, 0);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+    hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters,\r
+                                                  L"UDPIPv4Checksum", 0,\r
+                                                  &param_variant, 0);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+    VariantClear(&param_variant);\r
+\r
+    /* IPv6 csum */\r
+    V_VT(&param_variant) = VT_BSTR;\r
+    V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);\r
+    if (!enable && (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6) != 0) {\r
+        /* this is basically all disabled cases */\r
+        V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED);\r
+    } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6) ==\r
+               WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6) {\r
+        /* implied enable */\r
+        V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED);\r
+    } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX) != 0) {\r
+        /* implied enable */\r
+        V_BSTR(&param_variant) =\r
+                utob(NDIS_OFFLOAD_PARAMETERS_RX_ENABLED_TX_DISABLED);\r
+    } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX) != 0) {\r
+        /* implied enable */\r
+        V_BSTR(&param_variant) =\r
+                utob(NDIS_OFFLOAD_PARAMETERS_TX_ENABLED_RX_DISABLED);\r
+    }\r
+    hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters,\r
+                                                  L"TCPIPv6Checksum", 0,\r
+                                                  &param_variant, 0);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+    hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters,\r
+                                                  L"UDPIPv6Checksum", 0,\r
+                                                  &param_variant, 0);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+    VariantClear(&param_variant);\r
+\r
+    /* LSOv1 */\r
+    V_VT(&param_variant) = VT_BSTR;\r
+    V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);\r
+    if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4) != 0) {\r
+        if (enable) {\r
+            V_BSTR(&param_variant) =\r
+                    utob(NDIS_OFFLOAD_PARAMETERS_LSOV1_ENABLED);\r
+        } else {\r
+            V_BSTR(&param_variant) =\r
+                    utob(NDIS_OFFLOAD_PARAMETERS_LSOV1_DISABLED);\r
+        }\r
+    }\r
+    hr = ndis_tcp_offload_parameters->lpVtbl->Put(\r
+            ndis_tcp_offload_parameters, L"LsoV1", 0, &param_variant, 0);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+    VariantClear(&param_variant);\r
+\r
+    /* LSOv2 IPv4 */\r
+    V_VT(&param_variant) = VT_BSTR;\r
+    V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);\r
+    if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4) != 0) {\r
+        if (enable) {\r
+            V_BSTR(&param_variant) =\r
+                    utob(NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED);\r
+        } else {\r
+            V_BSTR(&param_variant) =\r
+                    utob(NDIS_OFFLOAD_PARAMETERS_LSOV2_DISABLED);\r
+        }\r
+    }\r
+    hr = ndis_tcp_offload_parameters->lpVtbl->Put(\r
+            ndis_tcp_offload_parameters, L"LsoV2IPv4", 0, &param_variant, 0);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+    VariantClear(&param_variant);\r
+\r
+    /* LSOv2 IPv4 */\r
+    V_VT(&param_variant) = VT_BSTR;\r
+    V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);\r
+    if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6) != 0) {\r
+        if (enable) {\r
+            V_BSTR(&param_variant) =\r
+                    utob(NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED);\r
+        } else {\r
+            V_BSTR(&param_variant) =\r
+                    utob(NDIS_OFFLOAD_PARAMETERS_LSOV2_DISABLED);\r
+        }\r
+    }\r
+    hr = ndis_tcp_offload_parameters->lpVtbl->Put(\r
+            ndis_tcp_offload_parameters, L"LsoV2IPv6", 0, &param_variant, 0);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+    VariantClear(&param_variant);\r
+\r
+    /* currently unused fields */\r
+    V_VT(&param_variant) = VT_BSTR;\r
+    V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);\r
+    hr = ndis_tcp_offload_parameters->lpVtbl->Put(\r
+            ndis_tcp_offload_parameters, L"IPSec", 0, &param_variant, 0);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+    hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters,\r
+                                                  L"TcpConnectionIPv4", 0,\r
+                                                  &param_variant, 0);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+    hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters,\r
+                                                  L"TcpConnectionIPv6", 0,\r
+                                                  &param_variant, 0);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+    hr = ndis_tcp_offload_parameters->lpVtbl->Put(\r
+            ndis_tcp_offload_parameters, L"Flags", 0, &param_variant, 0);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        WbemLogDebug(hr);\r
+        goto release;\r
+    }\r
+    /* further fields are for NDIS 6.1+ */\r
+\r
+release:\r
+    VariantClear(&param_variant);\r
+\r
+    return hr;\r
+}\r
+\r
+static HRESULT SetNdisOffload(LPCWSTR if_description, uint32_t offload_flags,\r
+                              bool enable)\r
+{\r
+    HRESULT hr = S_OK;\r
+\r
+    ComInstance instance = {};\r
+    WbemMethod method = {};\r
+    WbemMethodCall call = {};\r
+\r
+    /* param 0 */\r
+    IWbemClassObject *ndis_method_header = NULL;\r
+    /* param 1 */\r
+    IWbemClassObject *ndis_tcp_offload_parameters = NULL;\r
+\r
+    if (if_description == NULL) {\r
+        SCLogWarning(SC_ERR_SYSCALL, "No description specified for device");\r
+        return E_INVALIDARG;\r
+    }\r
+\r
+    LPCWSTR class_name = L"MSNdis_SetTcpOffloadParameters";\r
+    LPCWSTR instance_name_fmt = L"%s=\"%s\"";\r
+    size_t n_chars = wcslen(class_name) + wcslen(if_description) +\r
+                     wcslen(instance_name_fmt);\r
+    LPWSTR instance_name = SCMalloc((n_chars + 1) * sizeof(wchar_t));\r
+    if (instance_name == NULL) {\r
+        SCLogWarning(SC_ERR_SYSCALL,\r
+                     "Failed to allocate buffer for instance path");\r
+        goto release;\r
+    }\r
+    instance_name[n_chars] = 0; /* defensively null-terminate */\r
+    hr = StringCchPrintfW(instance_name, n_chars, instance_name_fmt, class_name,\r
+                          if_description);\r
+    if (hr != S_OK) {\r
+        SCLogWarning(SC_ERR_SYSCALL,\r
+                     "Failed to format WMI class instance name: 0x%" PRIx32,\r
+                     (uint32_t)hr);\r
+        goto release;\r
+    }\r
+\r
+    /* method name */\r
+    LPCWSTR method_name = L"WmiSetTcpOffloadParameters";\r
+\r
+    /* connect to COM/WMI */\r
+    hr = ComInstanceInit(&instance, L"ROOT\\WMI");\r
+    if (hr != S_OK) {\r
+        goto release;\r
+    }\r
+\r
+    /* obtain method */\r
+    hr = GetWbemMethod(&instance, class_name, method_name, &method);\r
+    if (hr != S_OK) {\r
+        goto release;\r
+    }\r
+\r
+    /* make parameter instances */\r
+    hr = GetWbemMethodCall(&method, instance_name, &call);\r
+    if (hr != S_OK) {\r
+        goto release;\r
+    }\r
+\r
+    /* build parameters */\r
+\r
+    VARIANT param_variant;\r
+    VariantInit(&param_variant);\r
+\r
+    /* Make MSNdis_WmiMethodHeader */\r
+    hr = BuildNdisWmiMethodHeader(&instance, 0, 0, 0, 5, &ndis_method_header);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        goto release;\r
+    }\r
+\r
+    V_VT(&param_variant) = VT_UNKNOWN;\r
+    V_UNKNOWN(&param_variant) = NULL;\r
+    hr = GetIUnknown(ndis_method_header, &V_UNKNOWN(&param_variant));\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        goto release;\r
+    }\r
+    hr = call.in_params->lpVtbl->Put(call.in_params, L"MethodHeader", 0,\r
+                                     &param_variant, 0);\r
+    VariantClear(&param_variant);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        Win32HResultLogDebug(hr);\r
+        goto release;\r
+    }\r
+\r
+    /* Make MSNdis_TcpOffloadParameters */\r
+    hr = BuildNdisTcpOffloadParameters(&instance, offload_flags, enable,\r
+                                       &ndis_tcp_offload_parameters);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        goto release;\r
+    }\r
+\r
+    V_VT(&param_variant) = VT_UNKNOWN;\r
+    V_UNKNOWN(&param_variant) = NULL;\r
+    hr = GetIUnknown(ndis_tcp_offload_parameters, &V_UNKNOWN(&param_variant));\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        goto release;\r
+    }\r
+    hr = call.in_params->lpVtbl->Put(call.in_params, L"TcpOffloadParameters", 0,\r
+                                     &param_variant, 0);\r
+    VariantClear(&param_variant);\r
+    if (hr != WBEM_S_NO_ERROR) {\r
+        Win32HResultLogDebug(hr);\r
+        goto release;\r
+    }\r
+\r
+    /* execute the method */\r
+    hr = WbemMethodCallExec(&call, NULL);\r
+    if (hr != S_OK) {\r
+        Win32HResultLogDebug(hr);\r
+        goto release;\r
+    }\r
+\r
+release:\r
+    ReleaseObject(ndis_tcp_offload_parameters);\r
+    ReleaseObject(ndis_method_header);\r
+\r
+    WbemMethodCallRelease(&call);\r
+    WbemMethodRelease(&method);\r
+    ComInstanceRelease(&instance);\r
+\r
+    return hr;\r
+}\r
+\r
+int DisableIfaceOffloadingWin32(LiveDevice *ldev, int csum, int other)\r
+{\r
+    SCLogDebug("Disabling offloading for device %s", ldev->dev);\r
+\r
+    int ret = 0;\r
+    DWORD err = NO_ERROR;\r
+    uint32_t offload_flags = 0;\r
+\r
+    if (ldev == NULL) {\r
+        return -1;\r
+    }\r
+\r
+    /* WMI uses the description as an identifier... */\r
+    IP_ADAPTER_ADDRESSES *if_info_list = NULL, *if_info = NULL;\r
+    err = Win32GetAdaptersAddresses(&if_info_list);\r
+    if (err != NO_ERROR) {\r
+        ret = -1;\r
+        goto release;\r
+    }\r
+    err = Win32FindAdapterAddresses(if_info_list, ldev->dev, &if_info);\r
+    if (err != NO_ERROR) {\r
+        ret = -1;\r
+        goto release;\r
+    }\r
+    LPWSTR if_description = if_info->Description;\r
+\r
+    err = GetNdisOffload(if_description, &offload_flags);\r
+    if (err != S_OK) {\r
+        ret = -1;\r
+        goto release;\r
+    }\r
+\r
+    if (!csum) {\r
+        offload_flags &= ~WIN32_TCP_OFFLOAD_FLAG_CSUM;\r
+    }\r
+    if (!other) {\r
+        offload_flags &= ~WIN32_TCP_OFFLOAD_FLAG_LSO;\r
+    }\r
+\r
+    err = SetNdisOffload(if_description, offload_flags, 0);\r
+    if (err != S_OK) {\r
+        ret = -1;\r
+        goto release;\r
+    }\r
+\r
+release:\r
+    SCFree(if_info_list);\r
+\r
+    return ret;\r
+}\r
+\r
+int RestoreIfaceOffloadingWin32(LiveDevice *ldev)\r
+{\r
+    SCLogDebug("Enabling offloading for device %s", ldev->dev);\r
+\r
+    int ret = 0;\r
+    DWORD err = NO_ERROR;\r
+\r
+    if (ldev == NULL) {\r
+        return -1;\r
+    }\r
+\r
+    /* WMI uses the description as an identifier... */\r
+    IP_ADAPTER_ADDRESSES *if_info_list = NULL, *if_info = NULL;\r
+    err = Win32GetAdaptersAddresses(&if_info_list);\r
+    if (err != NO_ERROR) {\r
+        ret = -1;\r
+        goto release;\r
+    }\r
+    err = Win32FindAdapterAddresses(if_info_list, ldev->dev, &if_info);\r
+    if (err != NO_ERROR) {\r
+        ret = -1;\r
+        goto release;\r
+    }\r
+    LPWSTR if_description = if_info->Description;\r
+\r
+    err = SetNdisOffload(if_description, ldev->offload_orig, 1);\r
+    if (err != S_OK) {\r
+        ret = -1;\r
+        goto release;\r
+    }\r
+\r
+release:\r
+    SCFree(if_info_list);\r
+\r
+    return ret;\r
+}\r
+\r
+#endif /* NTDDI_VERSION >= NTDDI_VISTA */\r
+\r
+#ifdef UNITTESTS\r
+static int Win32TestStripPcapPrefix(void)\r
+{\r
+    int result = 1;\r
+\r
+    const char *name1 = "\\Device\\NPF_{D4A32435-1BA7-4008-93A6-1518AA4BBD9B}";\r
+    const char *expect_name1 = "{D4A32435-1BA7-4008-93A6-1518AA4BBD9B}";\r
+\r
+    const char *name2 = "{D4A32435-1BA7-4008-93A6-1518AA4BBD9B}";\r
+    const char *expect_name2 = "{D4A32435-1BA7-4008-93A6-1518AA4BBD9B}";\r
+\r
+    result &= (strncmp(expect_name1, StripPcapPrefix(name1),\r
+                       strlen(expect_name1)) == 0);\r
+\r
+    result &= (strncmp(expect_name2, StripPcapPrefix(name2),\r
+                       strlen(expect_name2)) == 0);\r
+\r
+    return result;\r
+}\r
+#endif /* UNITTESTS */\r
+\r
+void Win32SyscallRegisterTests()\r
+{\r
+#ifdef UNITTESTS\r
+    UtRegisterTest("Win32TestStripPcapPrefix", Win32TestStripPcapPrefix);\r
+#endif\r
+}\r
+\r
+#endif /* OS_WIN32 */
\ No newline at end of file
diff --git a/src/win32-syscall.h b/src/win32-syscall.h
new file mode 100644 (file)
index 0000000..9ceb77f
--- /dev/null
@@ -0,0 +1,53 @@
+/* Copyright (C) 2018 Open Information Security Foundation\r
+ *\r
+ * You can copy, redistribute or modify this Program under the terms of\r
+ * the GNU General Public License version 2 as published by the Free\r
+ * Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * version 2 along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA\r
+ * 02110-1301, USA.\r
+ */\r
+\r
+/**\r
+ * \file\r
+ *\r
+ * \author Jacob Masen-Smith <jacob@evengx.com>\r
+ *\r
+ * Isolation for WMI/COM functionality\r
+ */\r
+\r
+#ifndef __WIN32_SYSCALL_H__\r
+#define __WIN32_SYSCALL_H__\r
+#ifdef OS_WIN32\r
+\r
+#include <inttypes.h>\r
+\r
+#include <iptypes.h>\r
+\r
+#include "util-device.h"\r
+\r
+const char *Win32GetErrorString(DWORD error_code, HMODULE ext_module);\r
+\r
+uint32_t Win32GetAdaptersAddresses(IP_ADAPTER_ADDRESSES **pif_info_list);\r
+uint32_t Win32FindAdapterAddresses(IP_ADAPTER_ADDRESSES *if_info_list,\r
+                                   const char *adapter_name,\r
+                                   IP_ADAPTER_ADDRESSES **pif_info);\r
+\r
+int GetIfaceMTUWin32(const char *pcap_dev);\r
+int GetGlobalMTUWin32(void);\r
+\r
+int GetIfaceOffloadingWin32(const char *ifname, int csum, int other);\r
+int DisableIfaceOffloadingWin32(LiveDevice *ldev, int csum, int other);\r
+int RestoreIfaceOffloadingWin32(LiveDevice *ldev);\r
+\r
+void Win32SyscallRegisterTests(void);\r
+\r
+#endif /* OS_WIN32 */\r
+#endif /* __WIN32_SYSCALL_H__ */
\ No newline at end of file