85 SCLogError(
"Error creating thread %s: you do not have support for WinDivert "
86 "enabled; please recompile with --enable-windivert",
95 typedef struct WinDivertThreadVars_ {
96 WinDivertHandle filter_handle;
99 int64_t qpc_start_time;
100 int64_t qpc_start_count;
101 int64_t qpc_freq_usec;
105 bool offload_enabled;
108 } WinDivertThreadVars;
110 #define WINDIVERT_MAX_QUEUE 16
111 static WinDivertThreadVars g_wd_tv[WINDIVERT_MAX_QUEUE];
112 static WinDivertQueueVars g_wd_qv[WINDIVERT_MAX_QUEUE];
113 static uint16_t g_wd_num = 0;
116 void *WinDivertGetThread(
int n)
121 return (
void *)&g_wd_tv[n];
124 void *WinDivertGetQueue(
int n)
129 return (
void *)&g_wd_qv[n];
133 #ifndef ERROR_INVALID_IMAGE_HASH
134 #define ERROR_INVALID_IMAGE_HASH 577L
136 #ifndef ERROR_DATA_NOT_ACCEPTED
137 #define ERROR_DATA_NOT_ACCEPTED 592L
144 static const char *WinDivertGetErrorString(DWORD error_code)
146 switch (error_code) {
148 case ERROR_FILE_NOT_FOUND:
149 return "The driver files WinDivert32.sys or WinDivert64.sys were "
151 case ERROR_ACCESS_DENIED:
152 return "Suricata must be run with Administrator privileges.";
153 case ERROR_INVALID_PARAMETER:
154 return "The WinDivert packet filter string is invalid.";
155 case ERROR_INVALID_IMAGE_HASH:
156 return "The WinDivert32.sys or WinDivert64.sys driver does not "
157 "have a valid digital signature, or your copy of Windows is "
158 "not up-to-date. Windows 7 and Server 2008 users need to "
159 "run Windows Update or install the following patch from "
160 "Microsoft: http://support.microsoft.com/kb/2949927";
161 case ERROR_DRIVER_BLOCKED:
162 return "This error occurs for various reasons, including: "
163 "attempting to load the 32-bit WinDivert.sys driver on a "
164 "64-bit system (or vice versa); the WinDivert.sys driver is "
165 "blocked by security software; or you are using a "
166 "virtualization environment that does not support "
168 case EPT_S_NOT_REGISTERED:
169 return "This error occurs when the Base Filtering Engine service "
170 "has been disabled.";
171 case ERROR_PROC_NOT_FOUND:
172 return "The error may occur for Windows Vista users. The "
173 "solution is to install the following patch from Microsoft: "
174 "http://support.microsoft.com/kb/2761494.";
177 case ERROR_HOST_UNREACHABLE:
178 return "This error occurs when an impostor packet (with "
179 "pAddr->Impostor set to 1) is injected and the ip.TTL or "
180 "ipv6.HopLimit field goes to zero. This is a defense of "
181 "last resort against infinite loops caused by impostor "
183 case ERROR_DATA_NOT_ACCEPTED:
184 return "This error is returned when the user application attempts "
185 "to inject a malformed packet. It may also be returned for "
186 "valid inbound packets, and the Windows TCP/IP stack "
187 "rejects the packet for some reason.";
189 return "The underlying cause of this error is unknown. However, "
190 "this error usually occurs when certain kinds of "
191 "anti-virus/firewall/security software is installed, and "
192 "the error message usually resolves once the offending "
193 "program is uninstalled. This suggests a software "
194 "compatibility problem.";
203 #define WinDivertLogError(err_code) \
205 const char *win_err_str = Win32GetErrorString((err_code), NULL); \
206 SCLogError("WinDivertOpen failed, error %" PRId32 " (0x%08" PRIx32 "): %s %s", \
207 (uint32_t)(err_code), (uint32_t)(err_code), win_err_str, \
208 WinDivertGetErrorString(err_code)); \
209 LocalFree((LPVOID)win_err_str); \
216 static void WinDivertInitQPCValues(WinDivertThreadVars *wd_tv)
219 (void)QueryPerformanceCounter((LARGE_INTEGER *)&wd_tv->qpc_start_count);
223 (void)QueryPerformanceFrequency((LARGE_INTEGER *)&wd_tv->qpc_freq_usec);
225 wd_tv->qpc_freq_usec /= 1000 * 1000;
231 static SCTime_t WinDivertTimestampToTimeStamp(WinDivertThreadVars *wd_tv, INT64 timestamp_count)
235 int64_t qpc_delta = (int64_t)timestamp_count - wd_tv->qpc_start_count;
236 int64_t unix_usec = wd_tv->qpc_start_time;
237 if (wd_tv->qpc_freq_usec) {
238 unix_usec += qpc_delta / wd_tv->qpc_freq_usec;
241 tv.tv_sec = (long)(unix_usec / (1000 * 1000));
242 tv.tv_usec = (long)(unix_usec - (int64_t)
tv.tv_sec * (1000 * 1000));
256 int WinDivertRegisterQueue(
bool forward,
char *filter_str)
261 WINDIVERT_LAYER layer =
262 forward ? WINDIVERT_LAYER_NETWORK_FORWARD : WINDIVERT_LAYER_NETWORK;
265 const char *error_str;
267 bool valid = WinDivertHelperCheckFilter(filter_str, layer, &error_str,
270 SCLogWarning(
"Invalid filter \"%s\" supplied to WinDivert: %s at position "
272 filter_str, error_str, error_pos);
279 if (g_wd_num >= WINDIVERT_MAX_QUEUE) {
280 SCLogError(
"Too many WinDivert queues specified %" PRId32
"", g_wd_num);
286 memset(&g_wd_tv, 0,
sizeof(g_wd_tv));
287 memset(&g_wd_qv, 0,
sizeof(g_wd_qv));
291 WinDivertThreadVars *wd_tv = &g_wd_tv[g_wd_num];
292 wd_tv->thread_num = g_wd_num;
295 WinDivertQueueVars *wd_qv = &g_wd_qv[g_wd_num];
296 wd_qv->queue_num = g_wd_num;
298 WinDivertInitQPCValues(wd_tv);
301 size_t filter_len = strlen(filter_str);
303 strlcpy(wd_qv->filter_str, filter_str,
sizeof(wd_qv->filter_str));
304 if (filter_len > copy_len) {
305 SCLogWarning(
"Queue length exceeds storage by %" PRId32
" bytes",
306 (int32_t)(filter_len - copy_len));
311 wd_qv->layer = layer;
327 wd_num_str[
sizeof(wd_num_str) - 1] = 0;
328 snprintf(wd_num_str,
sizeof(wd_num_str),
"%" PRId16
"", g_wd_num);
332 SCLogDebug(
"Queue %" PRId16
" registered", wd_qv->queue_num);
343 void ReceiveWinDivertThreadExitStats(
ThreadVars *,
void *);
358 static TmEcode WinDivertCloseHelper(WinDivertThreadVars *);
360 static TmEcode WinDivertCollectFilterDevices(WinDivertThreadVars *,
361 WinDivertQueueVars *);
362 static bool WinDivertIfaceMatchFilter(
const char *filter_string,
int if_index);
363 static void WinDivertDisableOffloading(WinDivertThreadVars *);
364 static void WinDivertRestoreOffloading(WinDivertThreadVars *);
370 tm_ptr->
name =
"ReceiveWinDivert";
371 tm_ptr->
ThreadInit = ReceiveWinDivertThreadInit;
382 tm_ptr->
name =
"VerdictWinDivert";
383 tm_ptr->
ThreadInit = VerdictWinDivertThreadInit;
384 tm_ptr->
Func = VerdictWinDivert;
393 tm_ptr->
name =
"DecodeWinDivert";
394 tm_ptr->
ThreadInit = DecodeWinDivertThreadInit;
395 tm_ptr->
Func = DecodeWinDivert;
407 WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)data;
408 wd_tv->slot = ((
TmSlot *)slot)->slot_next;
434 WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
446 "PacketGetFromQueueOrAlloc() - failed to obtain Packet buffer");
454 bool success =
false;
456 if (wd_tv->offload_enabled) {
461 WinDivertRecv(wd_tv->filter_handle, p->
ext_pkt,
466 &p->windivert_v.addr, &pktlen);
481 SCLogInfo(
"WinDivertRecv failed: error %" PRIu32
"",
482 (uint32_t)(GetLastError()));
487 p->
ts = WinDivertTimestampToTimeStamp(wd_tv, p->windivert_v.addr.Timestamp);
488 p->windivert_v.thread_num = wd_tv->thread_num;
501 if (TmThreadsSlotProcessPkt(
tv, wd_tv->slot, p) !=
TM_ECODE_OK) {
523 WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)initdata;
530 WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
539 if (wd_qv->filter_handle != NULL &&
540 wd_qv->filter_handle != INVALID_HANDLE_VALUE) {
546 if (WinDivertCollectFilterDevices(wd_tv, wd_qv) ==
TM_ECODE_OK) {
547 WinDivertDisableOffloading(wd_tv);
549 SCLogWarning(
"Failed to obtain network devices for WinDivert filter");
555 wd_qv->filter_handle = WinDivertOpen(wd_qv->filter_str, wd_qv->layer,
556 wd_qv->priority, wd_qv->flags);
557 if (wd_qv->filter_handle == INVALID_HANDLE_VALUE) {
558 WinDivertLogError(GetLastError());
565 wd_tv->filter_handle = wd_qv->filter_handle;
582 static TmEcode WinDivertCollectFilterDevices(WinDivertThreadVars *wd_tv,
583 WinDivertQueueVars *wd_qv)
588 IP_ADAPTER_ADDRESSES *if_info_list;
589 DWORD err = (DWORD)Win32GetAdaptersAddresses(&if_info_list);
590 if (err != NO_ERROR) {
595 for (IP_ADAPTER_ADDRESSES *if_info = if_info_list; if_info != NULL;
596 if_info = if_info->Next) {
598 if (WinDivertIfaceMatchFilter(wd_qv->filter_str, if_info->IfIndex)) {
599 SCLogConfig(
"Found adapter %s matching WinDivert filter %s",
600 if_info->AdapterName, wd_qv->filter_str);
603 if (new_ldev == NULL) {
608 if (new_ldev->
dev == NULL) {
614 SCLogDebug(
"Adapter %s does not match WinDivert filter %s",
615 if_info->AdapterName, wd_qv->filter_str);
628 static bool WinDivertIfaceMatchFilter(
const char *filter_string,
int if_index)
632 WINDIVERT_ADDRESS if_addr = {};
633 if_addr.IfIdx = if_index;
635 uint8_t dummy[4] = {4, 4, 4, 4};
637 match = WinDivertHelperEvalFilter(filter_string, WINDIVERT_LAYER_NETWORK,
638 dummy,
sizeof(dummy), &if_addr);
640 int err = GetLastError();
642 SCLogWarning(
"Failed to evaluate filter: 0x%" PRIx32, err);
654 static void WinDivertDisableOffloading(WinDivertThreadVars *wd_tv)
661 wd_tv->offload_enabled =
true;
665 wd_tv->offload_enabled =
true;
676 static void WinDivertRestoreOffloading(WinDivertThreadVars *wd_tv)
695 WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)data;
697 SCReturnCT(WinDivertCloseHelper(wd_tv),
"TmEcode");
707 void ReceiveWinDivertThreadExitStats(
ThreadVars *
tv,
void *data)
711 WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)data;
712 WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
720 SCLogInfo(
"(%s) Packets %" PRIu32
", Bytes %" PRIu64
", Errors %" PRIu32
"",
721 tv->
name, wd_qv->pkts, wd_qv->bytes, wd_qv->errs);
722 SCLogInfo(
"(%s) Verdict: Accepted %" PRIu32
", Dropped %" PRIu32
723 ", Replaced %" PRIu32
"",
724 tv->
name, wd_qv->accepted, wd_qv->dropped, wd_qv->replaced);
739 ret = WinDivertVerdictHelper(
tv, p);
753 WinDivertThreadVars *wd_tv = WinDivertGetThread(p->windivert_v.thread_num);
756 WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
759 p->windivert_v.verdicted =
true;
767 if (wd_tv->filter_handle == INVALID_HANDLE_VALUE ||
768 wd_tv->filter_handle == NULL) {
774 if (PacketIsTunnel(p)) {
775 bool finalVerdict = VerdictTunnelPacket(p);
781 if (p->
root != NULL) {
798 bool success = WinDivertSend(wd_tv->filter_handle,
GET_PKT_DATA(p),
802 WinDivertLogError(GetLastError());
824 WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)initdata;
838 WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)data;
840 SCReturnCT(WinDivertCloseHelper(wd_tv),
"TmEcode");
870 SCLogDebug(
"packet unsupported by WinDivert, first byte: %02x",
910 static TmEcode WinDivertCloseHelper(WinDivertThreadVars *wd_tv)
915 WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
917 SCLogDebug(
"No queue could be found for thread num %" PRId32
"",
925 if (wd_qv->filter_handle == INVALID_HANDLE_VALUE ||
926 wd_qv->filter_handle == NULL) {
930 if (!WinDivertClose(wd_qv->filter_handle)) {
931 SCLogError(
"WinDivertClose failed: error %" PRIu32
"", (uint32_t)(GetLastError()));
936 (void)WinDivertRestoreOffloading(wd_tv);
938 wd_qv->filter_handle = NULL;
952 static int SourceWinDivertTestIfaceMatchFilter(
void)
960 struct testdata tests[] = {
962 {
"ifIdx=11", 11,
true},
963 {
"ifIdx==11", 11,
true},
964 {
"ifIdx!=11", 1,
true},
965 {
"ifIdx!=11", 11,
false},
966 {
"ifIdx=3", 4,
false},
967 {
"ifIdx=11 || ifIdx=5", 5,
true},
968 {
"ifIdx=11 || ifIdx=4", 5,
false},
969 {
"ifIdx<3 || ifIdx>7", 8,
true},
970 {
"ifIdx<3 || ifIdx>7", 5,
false},
971 {
"ifIdx>3 or ifIdx<7", 5,
true},
972 {
"ifIdx>3 && ifIdx<7", 5,
true},
973 {
"ifIdx>3 && ifIdx<7", 1,
false},
974 {
"(ifIdx > 3 && ifIdx < 7) or ifIdx == 11", 11,
true}};
976 size_t count = (
sizeof(tests) /
sizeof(tests[0]));
978 for (
size_t i = 0; i < count; i++) {
979 struct testdata test = tests[i];
981 bool actual = WinDivertIfaceMatchFilter(test.filter, test.if_index);
982 if (actual != test.expected) {
983 printf(
"WinDivertIfaceMatchFilter(\"%s\", %d) == %d, expected %d\n",
984 test.filter, test.if_index, actual, test.expected);
995 void SourceWinDivertRegisterTests(
void)
999 SourceWinDivertTestIfaceMatchFilter);