;;
*-*-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])
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}
--- /dev/null
+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
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
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 \
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 \
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
#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
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;
#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>
AppLayerUnittestsRegister();
MimeDecRegisterTests();
StreamingBufferRegisterTests();
+#ifdef OS_WIN32
+ Win32SyscallRegisterTests();
+#endif
+#ifdef WINDIVERT
+ SourceWinDivertRegisterTests();
+#endif
}
#endif
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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
#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);
RunModeIdsNflogRegister();
RunModeTileMpipeRegister();
RunModeUnixSocketRegister();
+ RunModeIpsWinDivertRegister();
#ifdef UNITTESTS
UtRunModeRegister();
#endif
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);
RUNMODE_UNITTEST,
RUNMODE_NAPATECH,
RUNMODE_UNIX_SOCKET,
+ RUNMODE_WINDIVERT,
RUNMODE_USER_MAX, /* Last standard running mode */
RUNMODE_LIST_KEYWORDS,
RUNMODE_LIST_APP_LAYERS,
#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;
--- /dev/null
+/* 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__ */
--- /dev/null
+/* 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
--- /dev/null
+/* 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
#include "source-netmap.h"
#include "source-mpipe.h"
+#include "source-windivert.h"
+#include "source-windivert-prototypes.h"
+
#include "respond-reject.h"
#include "flow.h"
#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");
void RegisterAllModules(void)
{
+ // zero all module storage
+ memset(tmm_modules, 0, TMM_SIZE * sizeof(TmModule));
+
/* commanders */
TmModuleUnixManagerRegister();
/* managers */
/* nflog */
TmModuleReceiveNFLOGRegister();
TmModuleDecodeNFLOGRegister();
+
+ /* windivert */
+ TmModuleReceiveWinDivertRegister();
+ TmModuleVerdictWinDivertRegister();
+ TmModuleDecodeWinDivertRegister();
}
static TmEcode LoadYamlConfig(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
}
}
#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. */
* 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:
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) {
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);
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);
}
TMM_STATSLOGGER,
TMM_RECEIVENFLOG,
TMM_DECODENFLOG,
+ TMM_RECEIVEWINDIVERT,
+ TMM_VERDICTWINDIVERT,
+ TMM_DECODEWINDIVERT,
TMM_FLOWMANAGER,
TMM_FLOWRECYCLER,
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);
}
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;
#include <net/if.h>
#endif
+#ifdef OS_WIN32
+#include "win32-syscall.h"
+#endif
+
#include "util-ioctl.h"
/**
return 8 + SLL_HEADER_LEN;
}
+
/**
* \brief output the link MTU
*
*/
int GetIfaceMTU(const char *pcap_dev)
{
-#ifdef SIOCGIFMTU
+#if defined SIOCGIFMTU
struct ifreq ifr;
int fd;
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;
return GetIfaceOffloadingLinux(dev, csum, other);
#elif defined SIOCGIFCAP
return GetIfaceOffloadingBSD(dev);
+#elif defined OS_WIN32
+ return GetIfaceOffloadingWin32(dev, csum, other);
#else
return 0;
#endif
return DisableIfaceOffloadingLinux(dev, csum, other);
#elif defined SIOCSIFCAP
return DisableIfaceOffloadingBSD(dev);
+#elif defined OS_WIN32
+ return DisableIfaceOffloadingWin32(dev, csum, other);
#else
return 0;
#endif
RestoreIfaceOffloadingLinux(dev);
#elif defined SIOCSIFCAP
RestoreIfaceOffloadingBSD(dev);
+#elif defined OS_WIN32
+ RestoreIfaceOffloadingWin32(dev);
#endif
}
}
#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);
{
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);
#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)
return value;
}
-#elif !(defined(HAVE_WINCRYPT_H) && defined(OS_WIN32))
+#else
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>
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;
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) {
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)
return -1;
}
+#endif
#endif /* OS_WIN32 */
#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)
--- /dev/null
+/* 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(¶m_variant);\r
+ IWbemClassObject *ndis_object_header = *p_ndis_object_header;\r
+\r
+ /* set parameters */\r
+ V_VT(¶m_variant) = VT_UI1;\r
+ V_UI1(¶m_variant) = type;\r
+ hr = ndis_object_header->lpVtbl->Put(ndis_object_header, L"Type", 0,\r
+ ¶m_variant, 0);\r
+ VariantClear(¶m_variant);\r
+ if (hr != WBEM_S_NO_ERROR) {\r
+ WbemLogDebug(hr);\r
+ goto release;\r
+ }\r
+\r
+ V_VT(¶m_variant) = VT_UI1;\r
+ V_UI1(¶m_variant) = revision;\r
+ hr = ndis_object_header->lpVtbl->Put(ndis_object_header, L"Revision", 0,\r
+ ¶m_variant, 0);\r
+ VariantClear(¶m_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(¶m_variant) = VT_I4;\r
+ V_I4(¶m_variant) = size;\r
+ hr = ndis_object_header->lpVtbl->Put(ndis_object_header, L"Size", 0,\r
+ ¶m_variant, 0);\r
+ VariantClear(¶m_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(¶m_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(¶m_variant) = VT_UNKNOWN;\r
+ V_UNKNOWN(¶m_variant) = NULL;\r
+ hr = GetIUnknown(ndis_object_header, &V_UNKNOWN(¶m_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
+ ¶m_variant, 0);\r
+ VariantClear(¶m_variant);\r
+ if (hr != WBEM_S_NO_ERROR) {\r
+ WbemLogDebug(hr);\r
+ goto release;\r
+ }\r
+\r
+ V_VT(¶m_variant) = VT_BSTR;\r
+ V_BSTR(¶m_variant) = utob(net_luid);\r
+ hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"NetLuid", 0,\r
+ ¶m_variant, 0);\r
+ VariantClear(¶m_variant);\r
+ if (hr != WBEM_S_NO_ERROR) {\r
+ WbemLogDebug(hr);\r
+ goto release;\r
+ }\r
+\r
+ V_VT(¶m_variant) = VT_BSTR;\r
+ V_BSTR(¶m_variant) = utob((uint64_t)port_number);\r
+ hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"PortNumber", 0,\r
+ ¶m_variant, 0);\r
+ VariantClear(¶m_variant);\r
+ if (hr != WBEM_S_NO_ERROR) {\r
+ WbemLogDebug(hr);\r
+ goto release;\r
+ }\r
+\r
+ V_VT(¶m_variant) = VT_BSTR;\r
+ V_BSTR(¶m_variant) = utob(request_id);\r
+ hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"RequestId", 0,\r
+ ¶m_variant, 0);\r
+ VariantClear(¶m_variant);\r
+ if (hr != WBEM_S_NO_ERROR) {\r
+ WbemLogDebug(hr);\r
+ goto release;\r
+ }\r
+\r
+ V_VT(¶m_variant) = VT_BSTR;\r
+ V_BSTR(¶m_variant) = utob((uint64_t)timeout);\r
+ hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"Timeout", 0,\r
+ ¶m_variant, 0);\r
+ VariantClear(¶m_variant);\r
+ if (hr != WBEM_S_NO_ERROR) {\r
+ WbemLogDebug(hr);\r
+ goto release;\r
+ }\r
+\r
+ V_VT(¶m_variant) = VT_BSTR;\r
+ V_BSTR(¶m_variant) = utob((uint64_t)0);\r
+ hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"Padding", 0,\r
+ ¶m_variant, 0);\r
+ VariantClear(¶m_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(¶m_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(¶m_variant) = VT_UNKNOWN;\r
+ V_UNKNOWN(¶m_variant) = NULL;\r
+ hr = GetIUnknown(ndis_method_header, &V_UNKNOWN(¶m_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
+ ¶m_variant, 0);\r
+ VariantClear(¶m_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(¶m_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(¶m_variant) = VT_UNKNOWN;\r
+ V_UNKNOWN(¶m_variant) = NULL;\r
+ hr = GetIUnknown(ndis_object_header, &V_UNKNOWN(¶m_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, ¶m_variant, 0);\r
+ VariantClear(¶m_variant);\r
+ if (hr != WBEM_S_NO_ERROR) {\r
+ Win32HResultLogDebug(hr);\r
+ goto release;\r
+ }\r
+\r
+ /* IPv4 csum */\r
+ V_VT(¶m_variant) = VT_BSTR;\r
+ V_BSTR(¶m_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(¶m_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(¶m_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(¶m_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(¶m_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, ¶m_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
+ ¶m_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
+ ¶m_variant, 0);\r
+ if (hr != WBEM_S_NO_ERROR) {\r
+ WbemLogDebug(hr);\r
+ goto release;\r
+ }\r
+ VariantClear(¶m_variant);\r
+\r
+ /* IPv6 csum */\r
+ V_VT(¶m_variant) = VT_BSTR;\r
+ V_BSTR(¶m_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(¶m_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(¶m_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(¶m_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(¶m_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
+ ¶m_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
+ ¶m_variant, 0);\r
+ if (hr != WBEM_S_NO_ERROR) {\r
+ WbemLogDebug(hr);\r
+ goto release;\r
+ }\r
+ VariantClear(¶m_variant);\r
+\r
+ /* LSOv1 */\r
+ V_VT(¶m_variant) = VT_BSTR;\r
+ V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);\r
+ if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4) != 0) {\r
+ if (enable) {\r
+ V_BSTR(¶m_variant) =\r
+ utob(NDIS_OFFLOAD_PARAMETERS_LSOV1_ENABLED);\r
+ } else {\r
+ V_BSTR(¶m_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, ¶m_variant, 0);\r
+ if (hr != WBEM_S_NO_ERROR) {\r
+ WbemLogDebug(hr);\r
+ goto release;\r
+ }\r
+ VariantClear(¶m_variant);\r
+\r
+ /* LSOv2 IPv4 */\r
+ V_VT(¶m_variant) = VT_BSTR;\r
+ V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);\r
+ if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4) != 0) {\r
+ if (enable) {\r
+ V_BSTR(¶m_variant) =\r
+ utob(NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED);\r
+ } else {\r
+ V_BSTR(¶m_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, ¶m_variant, 0);\r
+ if (hr != WBEM_S_NO_ERROR) {\r
+ WbemLogDebug(hr);\r
+ goto release;\r
+ }\r
+ VariantClear(¶m_variant);\r
+\r
+ /* LSOv2 IPv4 */\r
+ V_VT(¶m_variant) = VT_BSTR;\r
+ V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);\r
+ if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6) != 0) {\r
+ if (enable) {\r
+ V_BSTR(¶m_variant) =\r
+ utob(NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED);\r
+ } else {\r
+ V_BSTR(¶m_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, ¶m_variant, 0);\r
+ if (hr != WBEM_S_NO_ERROR) {\r
+ WbemLogDebug(hr);\r
+ goto release;\r
+ }\r
+ VariantClear(¶m_variant);\r
+\r
+ /* currently unused fields */\r
+ V_VT(¶m_variant) = VT_BSTR;\r
+ V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);\r
+ hr = ndis_tcp_offload_parameters->lpVtbl->Put(\r
+ ndis_tcp_offload_parameters, L"IPSec", 0, ¶m_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
+ ¶m_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
+ ¶m_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, ¶m_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(¶m_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(¶m_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(¶m_variant) = VT_UNKNOWN;\r
+ V_UNKNOWN(¶m_variant) = NULL;\r
+ hr = GetIUnknown(ndis_method_header, &V_UNKNOWN(¶m_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
+ ¶m_variant, 0);\r
+ VariantClear(¶m_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(¶m_variant) = VT_UNKNOWN;\r
+ V_UNKNOWN(¶m_variant) = NULL;\r
+ hr = GetIUnknown(ndis_tcp_offload_parameters, &V_UNKNOWN(¶m_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
+ ¶m_variant, 0);\r
+ VariantClear(¶m_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
--- /dev/null
+/* 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