suricata
source-windivert.c
Go to the documentation of this file.
1/* Copyright (C) 2018 Open Information Security Foundation
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18/**
19 * \file
20 *
21 * \author Jacob Masen-Smith <jacob@evengx.com>
22 *
23 * WinDivert emulation of netfilter_queue functionality to hook into Suricata's
24 * IPS mode. Supported solely on Windows.
25 *
26 */
27
28#include "suricata-common.h"
29#include "suricata.h"
30#include "tm-threads.h"
31#include "packet.h"
32#include "util-byte.h"
33#include "util-debug.h"
34#include "util-device-private.h"
35#include "util-error.h"
36#include "util-ioctl.h"
37#include "util-privs.h"
38#include "util-unittest.h"
39
40#include "runmodes.h"
41
42#include "queue.h"
43
45#include "source-windivert.h"
46
47#ifdef WINDIVERT
48// clang-format off
49#include <winsock2.h>
50#include <windows.h>
51#include <iptypes.h>
52#include <winerror.h>
53// clang-format on
54#endif
55
56#ifndef WINDIVERT
57/* Gracefully handle the case where no WinDivert support is compiled in */
58
59TmEcode NoWinDivertSupportExit(ThreadVars *, const void *, void **);
60
67
74
81
83 void **data)
84{
85 SCLogError("Error creating thread %s: you do not have support for WinDivert "
86 "enabled; please recompile with --enable-windivert",
87 tv->name);
88 exit(EXIT_FAILURE);
89}
90
91#else /* implied we do have WinDivert support */
92#include "action-globals.h"
93#include "win32-syscall.h"
94
95typedef struct WinDivertThreadVars_ {
96 WinDivertHandle filter_handle;
97
98 int thread_num;
99 int64_t qpc_start_time;
100 int64_t qpc_start_count;
101 int64_t qpc_freq_usec;
102
103 TmSlot *slot;
104
105 bool offload_enabled;
106
107 TAILQ_HEAD(, LiveDevice_) live_devices;
108} WinDivertThreadVars;
109
110#define WINDIVERT_MAX_QUEUE 16
111static WinDivertThreadVars g_wd_tv[WINDIVERT_MAX_QUEUE];
112static WinDivertQueueVars g_wd_qv[WINDIVERT_MAX_QUEUE];
113static uint16_t g_wd_num = 0;
114static SCMutex g_wd_init_lock = SCMUTEX_INITIALIZER;
115
116void *WinDivertGetThread(int n)
117{
118 if (n >= g_wd_num) {
119 return NULL;
120 }
121 return (void *)&g_wd_tv[n];
122}
123
124void *WinDivertGetQueue(int n)
125{
126 if (n >= g_wd_num) {
127 return NULL;
128 }
129 return (void *)&g_wd_qv[n];
130}
131
132// not defined in MinGW winerror.h
133#ifndef ERROR_INVALID_IMAGE_HASH
134#define ERROR_INVALID_IMAGE_HASH 577L
135#endif
136#ifndef ERROR_DATA_NOT_ACCEPTED
137#define ERROR_DATA_NOT_ACCEPTED 592L
138#endif
139
140/**
141 * \brief return an error description for Win32 error values commonly returned
142 * by WinDivert
143 */
144static const char *WinDivertGetErrorString(DWORD error_code)
145{
146 switch (error_code) {
147 // WinDivertOpen errors
148 case ERROR_FILE_NOT_FOUND:
149 return "The driver files WinDivert32.sys or WinDivert64.sys were "
150 "not found.";
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 "
167 "drivers.";
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.";
175
176 // WinDivertSend errors
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 "
182 "packets.";
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.";
188 case ERROR_RETRY:
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.";
195 default:
196 return "";
197 }
198}
199
200/**
201 * \brief logs a WinDivert error at Error level.
202 */
203#define WinDivertLogError(err_code) \
204 do { \
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); \
210 } while (0);
211
212/**
213 * \brief initializes QueryPerformanceCounter values so we can get
214 * absolute time from WinDivert timestamps.
215 */
216static void WinDivertInitQPCValues(WinDivertThreadVars *wd_tv)
217{
218 SCTime_t now = TimeGet();
219 (void)QueryPerformanceCounter((LARGE_INTEGER *)&wd_tv->qpc_start_count);
220
221 wd_tv->qpc_start_time = (uint64_t)SCTIME_SECS(now) * (1000 * 1000) + (uint64_t)SCTIME_SECS(now);
222
223 (void)QueryPerformanceFrequency((LARGE_INTEGER *)&wd_tv->qpc_freq_usec);
224 /* \bug: clock drift? */
225 wd_tv->qpc_freq_usec /= 1000 * 1000;
226}
227
228/**
229 * \brief WinDivert timestamp to a SCTime_t
230 */
231static SCTime_t WinDivertTimestampToTimeStamp(WinDivertThreadVars *wd_tv, INT64 timestamp_count)
232{
233 struct timeval tv;
234
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;
239 }
240
241 tv.tv_sec = (long)(unix_usec / (1000 * 1000));
242 tv.tv_usec = (long)(unix_usec - (int64_t)tv.tv_sec * (1000 * 1000));
243
244 return SCTIME_FROM_TIMEVAL(&tv);
245}
246
247/**
248 * \brief initialize a WinDivert filter
249 *
250 * \param filter a WinDivert filter string as defined at
251 * https://www.reqrypt.org/windivert-doc.html#filter_language
252 *
253 * \retval 0 on success
254 * \retval -1 on failure
255 */
256int WinDivertRegisterQueue(bool forward, char *filter_str)
257{
258 SCEnter();
259 int ret = 0;
260
261 WINDIVERT_LAYER layer =
262 forward ? WINDIVERT_LAYER_NETWORK_FORWARD : WINDIVERT_LAYER_NETWORK;
263
264 /* validate the filter string */
265 const char *error_str;
266 uint32_t error_pos;
267 bool valid = WinDivertHelperCheckFilter(filter_str, layer, &error_str,
268 &error_pos);
269 if (!valid) {
270 SCLogWarning("Invalid filter \"%s\" supplied to WinDivert: %s at position "
271 "%" PRId32 "",
272 filter_str, error_str, error_pos);
273 SCReturnInt(-1);
274 }
275
276 /* initialize the queue */
277 SCMutexLock(&g_wd_init_lock);
278
279 if (g_wd_num >= WINDIVERT_MAX_QUEUE) {
280 SCLogError("Too many WinDivert queues specified %" PRId32 "", g_wd_num);
281 ret = -1;
282 goto unlock;
283 }
284 if (g_wd_num == 0) {
285 /* on first registration, zero-initialize all array structs */
286 memset(&g_wd_tv, 0, sizeof(g_wd_tv));
287 memset(&g_wd_qv, 0, sizeof(g_wd_qv));
288 }
289
290 /* init thread vars */
291 WinDivertThreadVars *wd_tv = &g_wd_tv[g_wd_num];
292 wd_tv->thread_num = g_wd_num;
293
294 /* init queue vars */
295 WinDivertQueueVars *wd_qv = &g_wd_qv[g_wd_num];
296 wd_qv->queue_num = g_wd_num;
297
298 WinDivertInitQPCValues(wd_tv);
299
300 /* copy filter to persistent storage */
301 size_t filter_len = strlen(filter_str);
302 size_t copy_len =
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));
307 ret = -1;
308 goto unlock;
309 }
310
311 wd_qv->layer = layer;
312 wd_qv->priority =
313 g_wd_num; /* priority set in the order filters are defined */
314 wd_qv->flags = 0; /* normal inline function */
315
316 SCMutexInit(&wd_qv->filter_init_mutex, NULL);
317 SCMutexInit(&wd_qv->counters_mutex, NULL);
318
319 g_wd_num++;
320
321unlock:
322 SCMutexUnlock(&g_wd_init_lock);
323
324 if (ret == 0) {
325 // stringify queue index to use as thread name descriptor
326 char wd_num_str[6];
327 wd_num_str[sizeof(wd_num_str) - 1] = 0;
328 snprintf(wd_num_str, sizeof(wd_num_str), "%" PRId16 "", g_wd_num);
329
330 LiveRegisterDevice(wd_num_str);
331
332 SCLogDebug("Queue %" PRId16 " registered", wd_qv->queue_num);
333 }
334
335 return ret;
336}
337
338/* forward declarations of internal functions */
339/* Receive functions */
340TmEcode ReceiveWinDivertLoop(ThreadVars *, void *, void *);
341TmEcode ReceiveWinDivertThreadInit(ThreadVars *, const void *, void **);
342TmEcode ReceiveWinDivertThreadDeinit(ThreadVars *, void *);
343void ReceiveWinDivertThreadExitStats(ThreadVars *, void *);
344
345/* Verdict functions */
346TmEcode VerdictWinDivert(ThreadVars *, Packet *, void *);
347TmEcode VerdictWinDivertThreadInit(ThreadVars *, const void *, void **);
348TmEcode VerdictWinDivertThreadDeinit(ThreadVars *, void *);
349
350/* Decode functions */
351TmEcode DecodeWinDivert(ThreadVars *, Packet *, void *);
352TmEcode DecodeWinDivertThreadInit(ThreadVars *, const void *, void **);
353TmEcode DecodeWinDivertThreadDeinit(ThreadVars *, void *);
354
355/* internal helper functions */
356static TmEcode WinDivertRecvHelper(ThreadVars *tv, WinDivertThreadVars *);
357static TmEcode WinDivertVerdictHelper(ThreadVars *tv, Packet *p);
358static TmEcode WinDivertCloseHelper(WinDivertThreadVars *);
359
360static TmEcode WinDivertCollectFilterDevices(WinDivertThreadVars *,
361 WinDivertQueueVars *);
362static bool WinDivertIfaceMatchFilter(const char *filter_string, int if_index);
363static void WinDivertDisableOffloading(WinDivertThreadVars *);
364static void WinDivertRestoreOffloading(WinDivertThreadVars *);
365
367{
369
370 tm_ptr->name = "ReceiveWinDivert";
371 tm_ptr->ThreadInit = ReceiveWinDivertThreadInit;
372 tm_ptr->PktAcqLoop = ReceiveWinDivertLoop;
373 tm_ptr->ThreadExitPrintStats = ReceiveWinDivertThreadExitStats;
374 tm_ptr->ThreadDeinit = ReceiveWinDivertThreadDeinit;
375 tm_ptr->flags = TM_FLAG_RECEIVE_TM;
376}
377
379{
381
382 tm_ptr->name = "VerdictWinDivert";
383 tm_ptr->ThreadInit = VerdictWinDivertThreadInit;
384 tm_ptr->Func = VerdictWinDivert;
385 tm_ptr->ThreadDeinit = VerdictWinDivertThreadDeinit;
386 tm_ptr->flags = TM_FLAG_VERDICT_TM;
387}
388
390{
392
393 tm_ptr->name = "DecodeWinDivert";
394 tm_ptr->ThreadInit = DecodeWinDivertThreadInit;
395 tm_ptr->Func = DecodeWinDivert;
396 tm_ptr->ThreadDeinit = DecodeWinDivertThreadDeinit;
397 tm_ptr->flags = TM_FLAG_DECODE_TM;
398}
399
400/**
401 * \brief Main WinDivert packet receive pump
402 */
403TmEcode ReceiveWinDivertLoop(ThreadVars *tv, void *data, void *slot)
404{
405 SCEnter();
406
407 WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)data;
408 wd_tv->slot = ((TmSlot *)slot)->slot_next;
409
410 // Indicate that the thread is actually running its application level code (i.e., it can poll
411 // packets)
413
414 while (true) {
417 }
418
419 if (unlikely(WinDivertRecvHelper(tv, wd_tv) != TM_ECODE_OK)) {
421 }
422
424 }
425
427}
428
429static TmEcode WinDivertRecvHelper(ThreadVars *tv, WinDivertThreadVars *wd_tv)
430{
431 SCEnter();
432
433#ifdef COUNTERS
434 WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
435#endif /* COUNTERS */
436
437 /* make sure we have at least one packet in the packet pool, to prevent us
438 * from alloc'ing packets at line rate
439 */
441
442 /* obtain a packet buffer */
444 if (unlikely(p == NULL)) {
446 "PacketGetFromQueueOrAlloc() - failed to obtain Packet buffer");
448 }
450
451 /* receive packet, depending on offload status. MTU is used as an estimator
452 * for direct data alloc size, and this is meaningless if large segments are
453 * coalesced before they reach WinDivert */
454 bool success = false;
455 uint32_t pktlen = 0;
456 if (wd_tv->offload_enabled) {
457 /* allocate external, if not already */
459
460 success =
461 WinDivertRecv(wd_tv->filter_handle, p->ext_pkt,
462 MAX_PAYLOAD_SIZE, &p->windivert_v.addr, &pktlen);
463 } else {
464 success = WinDivertRecv(wd_tv->filter_handle, GET_PKT_DIRECT_DATA(p),
466 &p->windivert_v.addr, &pktlen);
467 }
468 SET_PKT_LEN(p, pktlen);
469
470 if (!success) {
471#ifdef COUNTERS
472 SCMutexLock(&wd_qv->counters_mutex);
473 wd_qv->errs++;
474 SCMutexUnlock(&wd_qv->counters_mutex);
475#endif /* COUNTERS */
476
477 /* ensure packet length is zero to trigger an error in packet decoding
478 */
479 SET_PKT_LEN(p, 0);
480
481 SCLogInfo("WinDivertRecv failed: error %" PRIu32 "",
482 (uint32_t)(GetLastError()));
484 }
485 SCLogDebug("Packet received, length %" PRId32 "", GET_PKT_LEN(p));
486
487 p->ts = WinDivertTimestampToTimeStamp(wd_tv, p->windivert_v.addr.Timestamp);
488 p->windivert_v.thread_num = wd_tv->thread_num;
489
490#ifdef COUNTERS
491 SCMutexLock(&wd_qv->counters_mutex);
492 wd_qv->pkts++;
493 wd_qv->bytes += GET_PKT_LEN(p);
494 SCMutexUnlock(&wd_qv->counters_mutex);
495#endif /* COUNTERS */
496
497 /* Do the packet processing by calling TmThreadsSlotProcessPkt, this will,
498 * depending on the running mode, pass the packet to the treatment functions
499 * or push it to a packet pool. So processing time can vary.
500 */
501 if (TmThreadsSlotProcessPkt(tv, wd_tv->slot, p) != TM_ECODE_OK) {
503 }
504
506}
507
508/**
509 * \brief Init function for ReceiveWinDivert
510 *
511 * ReceiveWinDivertThreadInit sets up receiving packets via WinDivert.
512 *
513 * \param tv pointer to generic thread vars
514 * \param initdata pointer to the interface passed from the user
515 * \param data out-pointer to the WinDivert-specific thread vars
516 */
517TmEcode ReceiveWinDivertThreadInit(ThreadVars *tv, const void *initdata,
518 void **data)
519{
520 SCEnter();
521 TmEcode ret = TM_ECODE_OK;
522
523 WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)initdata;
524
525 if (wd_tv == NULL) {
526 SCLogError("initdata == NULL");
528 }
529
530 WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
531
532 if (wd_qv == NULL) {
533 SCLogError("queue == NULL");
535 }
536
537 SCMutexLock(&wd_qv->filter_init_mutex);
538 /* does the queue already have an active handle? */
539 if (wd_qv->filter_handle != NULL &&
540 wd_qv->filter_handle != INVALID_HANDLE_VALUE) {
541 goto unlock;
542 }
543
544 TAILQ_INIT(&wd_tv->live_devices);
545
546 if (WinDivertCollectFilterDevices(wd_tv, wd_qv) == TM_ECODE_OK) {
547 WinDivertDisableOffloading(wd_tv);
548 } else {
549 SCLogWarning("Failed to obtain network devices for WinDivert filter");
550 }
551
552 /* we open now so that we can immediately start handling packets,
553 * instead of losing however many would occur between registering the
554 * queue and starting a receive thread. */
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());
559 ret = TM_ECODE_FAILED;
560 goto unlock;
561 }
562
563unlock:
564 if (ret == 0) { /* success */
565 wd_tv->filter_handle = wd_qv->filter_handle;
566
567 /* set our return context */
568 *data = wd_tv;
569 }
570
571 SCMutexUnlock(&wd_qv->filter_init_mutex);
572 SCReturnInt(ret);
573}
574
575/**
576 * \brief collect all devices covered by this filter in the thread vars'
577 * live devices list
578 *
579 * \param wd_tv pointer to WinDivert thread vars
580 * \param wd_qv pointer to WinDivert queue vars
581 */
582static TmEcode WinDivertCollectFilterDevices(WinDivertThreadVars *wd_tv,
583 WinDivertQueueVars *wd_qv)
584{
585 SCEnter();
586 TmEcode ret = TM_ECODE_OK;
587
588 IP_ADAPTER_ADDRESSES *if_info_list;
589 DWORD err = (DWORD)Win32GetAdaptersAddresses(&if_info_list);
590 if (err != NO_ERROR) {
591 ret = TM_ECODE_FAILED;
592 goto release;
593 }
594
595 for (IP_ADAPTER_ADDRESSES *if_info = if_info_list; if_info != NULL;
596 if_info = if_info->Next) {
597
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);
601
602 LiveDevice *new_ldev = SCCalloc(1, sizeof(LiveDevice));
603 if (new_ldev == NULL) {
604 ret = TM_ECODE_FAILED;
605 goto release;
606 }
607 new_ldev->dev = SCStrdup(if_info->AdapterName);
608 if (new_ldev->dev == NULL) {
609 ret = TM_ECODE_FAILED;
610 goto release;
611 }
612 TAILQ_INSERT_TAIL(&wd_tv->live_devices, new_ldev, next);
613 } else {
614 SCLogDebug("Adapter %s does not match WinDivert filter %s",
615 if_info->AdapterName, wd_qv->filter_str);
616 }
617 }
618
619release:
620 SCFree(if_info_list);
621
622 SCReturnInt(ret);
623}
624
625/**
626 * \brief test if the specified interface index matches the filter
627 */
628static bool WinDivertIfaceMatchFilter(const char *filter_string, int if_index)
629{
630 bool match = false;
631
632 WINDIVERT_ADDRESS if_addr = {};
633 if_addr.IfIdx = if_index;
634
635 uint8_t dummy[4] = {4, 4, 4, 4};
636
637 match = WinDivertHelperEvalFilter(filter_string, WINDIVERT_LAYER_NETWORK,
638 dummy, sizeof(dummy), &if_addr);
639 if (!match) {
640 int err = GetLastError();
641 if (err != 0) {
642 SCLogWarning("Failed to evaluate filter: 0x%" PRIx32, err);
643 }
644 }
645
646 return match;
647}
648
649/**
650 * \brief disable offload status on devices for this filter
651 *
652 * \param wd_tv pointer to WinDivert thread vars
653 */
654static void WinDivertDisableOffloading(WinDivertThreadVars *wd_tv)
655{
656 for (LiveDevice *ldev = TAILQ_FIRST(&wd_tv->live_devices); ldev != NULL;
657 ldev = TAILQ_NEXT(ldev, next)) {
658
659 if (LiveGetOffload() == 0) {
660 if (GetIfaceOffloading(ldev->dev, 1, 1) == 1) {
661 wd_tv->offload_enabled = true;
662 }
663 } else {
664 if (DisableIfaceOffloading(ldev, 1, 1) != 1) {
665 wd_tv->offload_enabled = true;
666 }
667 }
668 }
669}
670
671/**
672 * \brief enable offload status on devices for this filter
673 *
674 * \param wd_tv pointer to WinDivert thread vars
675 */
676static void WinDivertRestoreOffloading(WinDivertThreadVars *wd_tv)
677{
678 for (LiveDevice *ldev = TAILQ_FIRST(&wd_tv->live_devices); ldev != NULL;
679 ldev = TAILQ_NEXT(ldev, next)) {
680
682 }
683}
684
685/**
686 * \brief Deinit function releases resources at exit.
687 *
688 * \param tv pointer to generic thread vars
689 * \param data pointer to WinDivert-specific thread vars
690 */
691TmEcode ReceiveWinDivertThreadDeinit(ThreadVars *tv, void *data)
692{
693 SCEnter();
694
695 WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)data;
696
697 SCReturnCT(WinDivertCloseHelper(wd_tv), "TmEcode");
698}
699
700/**
701 * \brief ExitStats prints stats to stdout at exit
702 *
703 *
704 * \param tv pointer to generic thread vars
705 * \param data pointer to WinDivert-specific thread vars
706 */
707void ReceiveWinDivertThreadExitStats(ThreadVars *tv, void *data)
708{
709 SCEnter();
710
711 WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)data;
712 WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
713 if (wd_qv == NULL) {
714 SCLogError("queue == NULL");
715 SCReturn;
716 }
717
718 SCMutexLock(&wd_qv->counters_mutex);
719
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);
725
726 SCMutexUnlock(&wd_qv->counters_mutex);
727 SCReturn;
728}
729
730/**
731 * \brief WinDivert verdict module packet entry function
732 */
733TmEcode VerdictWinDivert(ThreadVars *tv, Packet *p, void *data)
734{
735 SCEnter();
736
737 TmEcode ret = TM_ECODE_OK;
738
739 ret = WinDivertVerdictHelper(tv, p);
740 if (ret != TM_ECODE_OK) {
741 SCReturnInt(ret);
742 }
743
745}
746
747/**
748 * \brief internal helper function to do the bulk of verdict work
749 */
750static TmEcode WinDivertVerdictHelper(ThreadVars *tv, Packet *p)
751{
752 SCEnter();
753 WinDivertThreadVars *wd_tv = WinDivertGetThread(p->windivert_v.thread_num);
754
755#ifdef COUNTERS
756 WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
757#endif /* COUNTERS */
758
759 p->windivert_v.verdicted = true;
760
761 /* can't verdict a "fake" packet */
762 if (PKT_IS_PSEUDOPKT(p)) {
764 }
765
766 /* the handle has been closed and we can no longer use it */
767 if (wd_tv->filter_handle == INVALID_HANDLE_VALUE ||
768 wd_tv->filter_handle == NULL) {
770 }
771
772 /* we can't verdict tunnel packets without ensuring all encapsulated
773 * packets are verdicted */
774 if (PacketIsTunnel(p)) {
775 bool finalVerdict = VerdictTunnelPacket(p);
776 if (!finalVerdict) {
778 }
779
780 // the action needs to occur on the root packet.
781 if (p->root != NULL) {
782 p = p->root;
783 }
784 }
785
786 /* DROP simply means we do nothing; the WinDivert driver does the rest.
787 */
789#ifdef COUNTERS
790 SCMutexLock(&wd_qv->counters_mutex);
791 wd_qv->dropped++;
792 SCMutexUnlock(&wd_qv->counters_mutex);
793#endif /* counters */
794
796 }
797
798 bool success = WinDivertSend(wd_tv->filter_handle, GET_PKT_DATA(p),
799 GET_PKT_LEN(p), &p->windivert_v.addr, NULL);
800
801 if (unlikely(!success)) {
802 WinDivertLogError(GetLastError());
804 }
805
806#ifdef COUNTERS
807 SCMutexLock(&wd_qv->counters_mutex);
808 wd_qv->accepted++;
809 SCMutexUnlock(&wd_qv->counters_mutex);
810#endif /* counters */
811
813}
814
815/**
816 * \brief init the verdict thread, which is piggybacked off the receive
817 * thread
818 */
819TmEcode VerdictWinDivertThreadInit(ThreadVars *tv, const void *initdata,
820 void **data)
821{
822 SCEnter();
823
824 WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)initdata;
825 *data = wd_tv;
826
828}
829
830/**
831 * \brief deinit the verdict thread and shut down the WinDivert driver if
832 * it's still up.
833 */
834TmEcode VerdictWinDivertThreadDeinit(ThreadVars *tv, void *data)
835{
836 SCEnter();
837
838 WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)data;
839
840 SCReturnCT(WinDivertCloseHelper(wd_tv), "TmEcode");
841}
842
843/**
844 * \brief decode a raw packet submitted to suricata from the WinDivert
845 * driver
846 *
847 * All WinDivert packets are IPv4/v6, but do not include the network layer
848 * to differentiate the two, so instead we must check the version and go
849 * from there.
850 */
851TmEcode DecodeWinDivert(ThreadVars *tv, Packet *p, void *data)
852{
853 SCEnter();
854
855 IPV4Hdr *ip4h = (IPV4Hdr *)GET_PKT_DATA(p);
856 IPV6Hdr *ip6h = (IPV6Hdr *)GET_PKT_DATA(p);
857 DecodeThreadVars *d_tv = (DecodeThreadVars *)data;
858
860
862
863 if (IPV4_GET_RAW_VER(ip4h) == 4) {
864 SCLogDebug("IPv4 packet");
865 DecodeIPV4(tv, d_tv, p, GET_PKT_DATA(p), GET_PKT_LEN(p));
866 } else if (IPV6_GET_RAW_VER(ip6h) == 6) {
867 SCLogDebug("IPv6 packet");
868 DecodeIPV6(tv, d_tv, p, GET_PKT_DATA(p), GET_PKT_LEN(p));
869 } else {
870 SCLogDebug("packet unsupported by WinDivert, first byte: %02x",
871 *GET_PKT_DATA(p));
872 }
873
874 PacketDecodeFinalize(tv, d_tv, p);
875
877}
878
879TmEcode DecodeWinDivertThreadInit(ThreadVars *tv, const void *initdata,
880 void **data)
881{
882 SCEnter();
883
885 if (d_tv == NULL) {
887 }
888
890
891 *data = d_tv;
892
894}
895
896TmEcode DecodeWinDivertThreadDeinit(ThreadVars *tv, void *data)
897{
898 SCEnter();
899
900 if (data != NULL) {
902 }
903
905}
906
907/**
908 * \brief helper function for use with ThreadDeinit functions
909 */
910static TmEcode WinDivertCloseHelper(WinDivertThreadVars *wd_tv)
911{
912 SCEnter();
913 TmEcode ret = TM_ECODE_OK;
914
915 WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
916 if (wd_qv == NULL) {
917 SCLogDebug("No queue could be found for thread num %" PRId32 "",
918 wd_tv->thread_num);
920 }
921
922 SCMutexLock(&wd_qv->filter_init_mutex);
923
924 /* check if there's nothing to close */
925 if (wd_qv->filter_handle == INVALID_HANDLE_VALUE ||
926 wd_qv->filter_handle == NULL) {
927 goto unlock;
928 }
929
930 if (!WinDivertClose(wd_qv->filter_handle)) {
931 SCLogError("WinDivertClose failed: error %" PRIu32 "", (uint32_t)(GetLastError()));
932 ret = TM_ECODE_FAILED;
933 goto unlock;
934 }
935
936 (void)WinDivertRestoreOffloading(wd_tv);
937
938 wd_qv->filter_handle = NULL;
939
940unlock:
941 SCMutexUnlock(&wd_qv->filter_init_mutex);
942
943 if (ret == TM_ECODE_OK) {
944 SCMutexDestroy(&wd_qv->filter_init_mutex);
945 SCMutexDestroy(&wd_qv->counters_mutex);
946 }
947
948 SCReturnInt(ret);
949}
950
951#ifdef UNITTESTS
952static int SourceWinDivertTestIfaceMatchFilter(void)
953{
954 struct testdata {
955 const char *filter;
956 int if_index;
957 bool expected;
958 };
959
960 struct testdata tests[] = {
961 {"true", 11, true},
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}};
975
976 size_t count = (sizeof(tests) / sizeof(tests[0]));
977
978 for (size_t i = 0; i < count; i++) {
979 struct testdata test = tests[i];
980
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);
985 FAIL;
986 }
987 }
988 PASS;
989}
990#endif
991
992/**
993 * \brief this function registers unit tests for the WinDivert Source
994 */
995void SourceWinDivertRegisterTests(void)
996{
997#ifdef UNITTESTS
998 UtRegisterTest("SourceWinDivertTestIfaceMatchFilter",
999 SourceWinDivertTestIfaceMatchFilter);
1000#endif
1001}
1002
1003#endif /* WINDIVERT */
#define ACTION_DROP
struct HtpBodyChunk_ * next
void StatsSyncCountersIfSignalled(ThreadVars *tv)
Definition counters.c:450
int DecodeIPV4(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint16_t len)
#define IPV4_GET_RAW_VER(ip4h)
Definition decode-ipv4.h:95
int DecodeIPV6(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint16_t len)
#define IPV6_GET_RAW_VER(ip6h)
Definition decode-ipv6.h:62
#define GET_PKT_DIRECT_MAX_SIZE(p)
Definition decode.h:211
#define SET_PKT_LEN(p, len)
Definition decode.h:213
#define GET_PKT_DIRECT_DATA(p)
Definition decode.h:210
#define PKT_SET_SRC(p, src_val)
Definition decode.h:1325
#define GET_PKT_DATA(p)
Definition decode.h:209
#define MAX_PAYLOAD_SIZE
Definition decode.h:701
#define GET_PKT_LEN(p)
Definition decode.h:208
#define PKT_IS_PSEUDOPKT(p)
return 1 if the packet is a pseudo packet
Definition decode.h:1321
@ PKT_SRC_WIRE
Definition decode.h:52
ThreadVars * tv
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
#define PASS
Pass the test.
#define FAIL
Fail a test.
Packet * PacketGetFromQueueOrAlloc(void)
Get a packet. We try to get a packet from the packetpool first, but if that is empty we alloc a packe...
Definition decode.c:293
void DecodeRegisterPerfCounters(DecodeThreadVars *dtv, ThreadVars *tv)
Definition decode.c:628
void PacketDecodeFinalize(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p)
Finalize decoding of a packet.
Definition decode.c:232
DecodeThreadVars * DecodeThreadVarsAlloc(ThreadVars *tv)
Alloc and setup DecodeThreadVars.
Definition decode.c:804
void DecodeThreadVarsFree(ThreadVars *tv, DecodeThreadVars *dtv)
Definition decode.c:822
int PacketCallocExtPkt(Packet *p, int datalen)
Definition decode.c:309
void DecodeUpdatePacketCounters(ThreadVars *tv, const DecodeThreadVars *dtv, const Packet *p)
Definition decode.c:770
bool PacketCheckAction(const Packet *p, const uint8_t a)
Definition packet.c:49
#define TAILQ_INIT(head)
Definition queue.h:262
#define TAILQ_HEAD(name, type)
Definition queue.h:230
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition queue.h:294
#define TAILQ_FIRST(head)
Definition queue.h:250
#define TAILQ_NEXT(elm, field)
Definition queue.h:307
void TmModuleDecodeWinDivertRegister(void)
TmEcode NoWinDivertSupportExit(ThreadVars *, const void *, void **)
void TmModuleVerdictWinDivertRegister(void)
void TmModuleReceiveWinDivertRegister(void)
Structure to hold thread specific data for all decode modules.
Definition decode.h:963
SCTime_t ts
Definition decode.h:555
uint8_t * ext_pkt
Definition decode.h:615
struct Packet_ * root
Definition decode.h:653
Per thread variable structure.
Definition threadvars.h:58
char name[16]
Definition threadvars.h:65
const char * name
Definition tm-modules.h:48
TmEcode(* ThreadDeinit)(ThreadVars *, void *)
Definition tm-modules.h:53
void(* ThreadExitPrintStats)(ThreadVars *, void *)
Definition tm-modules.h:52
TmEcode(* Func)(ThreadVars *, Packet *, void *)
Definition tm-modules.h:56
TmEcode(* PktAcqLoop)(ThreadVars *, void *, void *)
Definition tm-modules.h:58
uint8_t flags
Definition tm-modules.h:80
TmEcode(* ThreadInit)(ThreadVars *, const void *, void **)
Definition tm-modules.h:51
#define BUG_ON(x)
size_t strlcpy(char *dst, const char *src, size_t siz)
volatile uint8_t suricata_ctl_flags
Definition suricata.c:172
#define SURICATA_STOP
Definition suricata.h:94
#define SCMutexDestroy
#define SCMUTEX_INITIALIZER
#define SCMutex
#define SCMutexUnlock(mut)
#define SCMutexInit(mut, mutattrs)
#define SCMutexLock(mut)
#define THV_RUNNING
Definition threadvars.h:55
TmModule tmm_modules[TMM_SIZE]
Definition tm-modules.c:29
#define TM_FLAG_RECEIVE_TM
Definition tm-modules.h:32
#define TM_FLAG_DECODE_TM
Definition tm-modules.h:33
#define TM_FLAG_VERDICT_TM
Definition tm-modules.h:35
@ TMM_RECEIVEWINDIVERT
@ TMM_DECODEWINDIVERT
@ TMM_VERDICTWINDIVERT
@ TM_ECODE_FAILED
@ TM_ECODE_OK
void TmThreadsSetFlag(ThreadVars *tv, uint32_t flag)
Set a thread flag.
Definition tm-threads.c:101
void PacketPoolWait(void)
#define SCEnter(...)
Definition util-debug.h:277
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCReturnInt(x)
Definition util-debug.h:281
#define SCReturnCT(x, type)
Definition util-debug.h:291
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition util-debug.h:255
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition util-debug.h:225
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267
#define SCLogConfig(...)
Definition util-debug.h:229
#define SCReturn
Definition util-debug.h:279
int LiveGetOffload(void)
Definition util-device.c:87
int LiveRegisterDevice(const char *dev)
Add a pcap device for monitoring and create structure.
int GetIfaceOffloading(const char *dev, int csum, int other)
output offloading status of the link
Definition util-ioctl.c:670
int DisableIfaceOffloading(LiveDevice *dev, int csum, int other)
Definition util-ioctl.c:683
void RestoreIfaceOffloading(LiveDevice *dev)
Definition util-ioctl.c:700
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define SCStrdup(s)
Definition util-mem.h:56
#define unlikely(expr)
SCTime_t TimeGet(void)
Definition util-time.c:152
#define SCTIME_SECS(t)
Definition util-time.h:57
#define SCTIME_FROM_TIMEVAL(tv)
Definition util-time.h:79