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 
59 TmEcode NoWinDivertSupportExit(ThreadVars *, const void *, void **);
60 
62 {
63  tmm_modules[TMM_RECEIVEWINDIVERT].name = "ReceiveWinDivert";
66 }
67 
69 {
70  tmm_modules[TMM_VERDICTWINDIVERT].name = "VerdictWinDivert";
73 }
74 
76 {
77  tmm_modules[TMM_DECODEWINDIVERT].name = "DecodeWinDivert";
80 }
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 
95 typedef 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
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;
114 static SCMutex g_wd_init_lock = SCMUTEX_INITIALIZER;
115 
116 void *WinDivertGetThread(int n)
117 {
118  if (n >= g_wd_num) {
119  return NULL;
120  }
121  return (void *)&g_wd_tv[n];
122 }
123 
124 void *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  */
144 static 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  */
216 static 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  */
231 static 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  */
256 int 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 
321 unlock:
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 */
340 TmEcode ReceiveWinDivertLoop(ThreadVars *, void *, void *);
341 TmEcode ReceiveWinDivertThreadInit(ThreadVars *, const void *, void **);
342 TmEcode ReceiveWinDivertThreadDeinit(ThreadVars *, void *);
343 void ReceiveWinDivertThreadExitStats(ThreadVars *, void *);
344 
345 /* Verdict functions */
346 TmEcode VerdictWinDivert(ThreadVars *, Packet *, void *);
347 TmEcode VerdictWinDivertThreadInit(ThreadVars *, const void *, void **);
348 TmEcode VerdictWinDivertThreadDeinit(ThreadVars *, void *);
349 
350 /* Decode functions */
351 TmEcode DecodeWinDivert(ThreadVars *, Packet *, void *);
352 TmEcode DecodeWinDivertThreadInit(ThreadVars *, const void *, void **);
353 TmEcode DecodeWinDivertThreadDeinit(ThreadVars *, void *);
354 
355 /* internal helper functions */
356 static TmEcode WinDivertRecvHelper(ThreadVars *tv, WinDivertThreadVars *);
357 static TmEcode WinDivertVerdictHelper(ThreadVars *tv, Packet *p);
358 static TmEcode WinDivertCloseHelper(WinDivertThreadVars *);
359 
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 *);
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  */
403 TmEcode 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 
429 static 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  */
440  PacketPoolWait();
441 
442  /* obtain a packet buffer */
444  if (unlikely(p == NULL)) {
445  SCLogDebug(
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  */
517 TmEcode 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 
563 unlock:
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  */
582 static 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 
619 release:
620  SCFree(if_info_list);
621 
622  SCReturnInt(ret);
623 }
624 
625 /**
626  * \brief test if the specified interface index matches the filter
627  */
628 static 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  */
654 static 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  */
676 static 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  */
691 TmEcode 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  */
707 void 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  */
733 TmEcode 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  */
750 static 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  */
788  if (PacketCheckAction(p, ACTION_DROP)) {
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  */
819 TmEcode 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  */
834 TmEcode 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  */
851 TmEcode 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 
861  DecodeUpdatePacketCounters(tv, d_tv, p);
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 
879 TmEcode 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 
896 TmEcode DecodeWinDivertThreadDeinit(ThreadVars *tv, void *data)
897 {
898  SCEnter();
899 
900  if (data != NULL) {
901  DecodeThreadVarsFree(tv, data);
902  }
903 
905 }
906 
907 /**
908  * \brief helper function for use with ThreadDeinit functions
909  */
910 static 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 
940 unlock:
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
952 static 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  */
995 void SourceWinDivertRegisterTests(void)
996 {
997 #ifdef UNITTESTS
998  UtRegisterTest("SourceWinDivertTestIfaceMatchFilter",
999  SourceWinDivertTestIfaceMatchFilter);
1000 #endif
1001 }
1002 
1003 #endif /* WINDIVERT */
PacketCheckAction
bool PacketCheckAction(const Packet *p, const uint8_t a)
Definition: packet.c:49
util-device-private.h
util-byte.h
tm-threads.h
win32-syscall.h
LiveRegisterDevice
int LiveRegisterDevice(const char *dev)
Add a pcap device for monitoring and create structure.
Definition: util-device.c:132
IPV6_GET_RAW_VER
#define IPV6_GET_RAW_VER(ip6h)
Definition: decode-ipv6.h:62
ThreadVars_::name
char name[16]
Definition: threadvars.h:65
TAILQ_INIT
#define TAILQ_INIT(head)
Definition: queue.h:262
TmModuleDecodeWinDivertRegister
void TmModuleDecodeWinDivertRegister(void)
Definition: source-windivert.c:75
TM_FLAG_VERDICT_TM
#define TM_FLAG_VERDICT_TM
Definition: tm-modules.h:35
PKT_IS_PSEUDOPKT
#define PKT_IS_PSEUDOPKT(p)
return 1 if the packet is a pseudo packet
Definition: decode.h:1319
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:275
TmThreadsSetFlag
void TmThreadsSetFlag(ThreadVars *tv, uint32_t flag)
Set a thread flag.
Definition: tm-threads.c:101
LiveGetOffload
int LiveGetOffload(void)
Definition: util-device.c:87
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
action-globals.h
LiveDevice_
Definition: util-device-private.h:32
source-windivert-prototypes.h
THV_RUNNING
#define THV_RUNNING
Definition: threadvars.h:55
SURICATA_STOP
#define SURICATA_STOP
Definition: suricata.h:94
NoWinDivertSupportExit
TmEcode NoWinDivertSupportExit(ThreadVars *, const void *, void **)
Definition: source-windivert.c:82
SCMutexLock
#define SCMutexLock(mut)
Definition: threads-debug.h:117
TmModuleVerdictWinDivertRegister
void TmModuleVerdictWinDivertRegister(void)
Definition: source-windivert.c:68
util-privs.h
SCMUTEX_INITIALIZER
#define SCMUTEX_INITIALIZER
Definition: threads-debug.h:121
TAILQ_INSERT_TAIL
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:294
PacketDecodeFinalize
void PacketDecodeFinalize(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p)
Finalize decoding of a packet.
Definition: decode.c:232
TM_ECODE_FAILED
@ TM_ECODE_FAILED
Definition: tm-threads-common.h:82
GET_PKT_DIRECT_MAX_SIZE
#define GET_PKT_DIRECT_MAX_SIZE(p)
Definition: decode.h:211
util-unittest.h
TMM_DECODEWINDIVERT
@ TMM_DECODEWINDIVERT
Definition: tm-threads-common.h:66
TmModule_::PktAcqLoop
TmEcode(* PktAcqLoop)(ThreadVars *, void *, void *)
Definition: tm-modules.h:58
TM_ECODE_OK
@ TM_ECODE_OK
Definition: tm-threads-common.h:81
strlcpy
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: util-strlcpyu.c:43
TmModule_::ThreadDeinit
TmEcode(* ThreadDeinit)(ThreadVars *, void *)
Definition: tm-modules.h:53
PKT_SET_SRC
#define PKT_SET_SRC(p, src_val)
Definition: decode.h:1323
DecodeRegisterPerfCounters
void DecodeRegisterPerfCounters(DecodeThreadVars *dtv, ThreadVars *tv)
Definition: decode.c:628
SET_PKT_LEN
#define SET_PKT_LEN(p, len)
Definition: decode.h:213
util-debug.h
PKT_SRC_WIRE
@ PKT_SRC_WIRE
Definition: decode.h:52
TAILQ_FIRST
#define TAILQ_FIRST(head)
Definition: queue.h:250
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
util-error.h
Packet_::ts
SCTime_t ts
Definition: decode.h:555
SCMutexUnlock
#define SCMutexUnlock(mut)
Definition: threads-debug.h:119
SCEnter
#define SCEnter(...)
Definition: util-debug.h:277
GET_PKT_DATA
#define GET_PKT_DATA(p)
Definition: decode.h:209
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:58
SCTIME_FROM_TIMEVAL
#define SCTIME_FROM_TIMEVAL(tv)
Definition: util-time.h:79
TmModule_::Func
TmEcode(* Func)(ThreadVars *, Packet *, void *)
Definition: tm-modules.h:56
RestoreIfaceOffloading
void RestoreIfaceOffloading(LiveDevice *dev)
Definition: util-ioctl.c:700
LiveDevice_::dev
char * dev
Definition: util-device-private.h:33
SCLogWarning
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition: util-debug.h:255
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:317
PacketCallocExtPkt
int PacketCallocExtPkt(Packet *p, int datalen)
Definition: decode.c:309
PacketPoolWait
void PacketPoolWait(void)
Definition: tmqh-packetpool.c:80
SCReturn
#define SCReturn
Definition: util-debug.h:279
IPV6Hdr_
Definition: decode-ipv6.h:32
Packet_
Definition: decode.h:501
TM_FLAG_DECODE_TM
#define TM_FLAG_DECODE_TM
Definition: tm-modules.h:33
tmm_modules
TmModule tmm_modules[TMM_SIZE]
Definition: tm-modules.c:29
DecodeIPV6
int DecodeIPV6(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint16_t len)
Definition: decode-ipv6.c:560
GET_PKT_LEN
#define GET_PKT_LEN(p)
Definition: decode.h:208
TimeGet
SCTime_t TimeGet(void)
Definition: util-time.c:152
IPV4_GET_RAW_VER
#define IPV4_GET_RAW_VER(ip4h)
Definition: decode-ipv4.h:95
TmSlot_
Definition: tm-threads.h:53
SCTime_t
Definition: util-time.h:40
TmEcode
TmEcode
Definition: tm-threads-common.h:80
source-windivert.h
GetIfaceOffloading
int GetIfaceOffloading(const char *dev, int csum, int other)
output offloading status of the link
Definition: util-ioctl.c:670
queue.h
DisableIfaceOffloading
int DisableIfaceOffloading(LiveDevice *dev, int csum, int other)
Definition: util-ioctl.c:683
TmModule_::name
const char * name
Definition: tm-modules.h:48
runmodes.h
SCLogInfo
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:225
SCMutexInit
#define SCMutexInit(mut, mutattrs)
Definition: threads-debug.h:116
TM_FLAG_RECEIVE_TM
#define TM_FLAG_RECEIVE_TM
Definition: tm-modules.h:32
TmModule_
Definition: tm-modules.h:47
IPV4Hdr_
Definition: decode-ipv4.h:72
DecodeThreadVarsFree
void DecodeThreadVarsFree(ThreadVars *tv, DecodeThreadVars *dtv)
Definition: decode.c:820
suricata-common.h
packet.h
TAILQ_NEXT
#define TAILQ_NEXT(elm, field)
Definition: queue.h:307
ACTION_DROP
#define ACTION_DROP
Definition: action-globals.h:30
Packet_::ext_pkt
uint8_t * ext_pkt
Definition: decode.h:615
SCTIME_SECS
#define SCTIME_SECS(t)
Definition: util-time.h:57
TmModule_::ThreadInit
TmEcode(* ThreadInit)(ThreadVars *, const void *, void **)
Definition: tm-modules.h:51
SCStrdup
#define SCStrdup(s)
Definition: util-mem.h:56
TMM_RECEIVEWINDIVERT
@ TMM_RECEIVEWINDIVERT
Definition: tm-threads-common.h:64
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:32
TmModule_::ThreadExitPrintStats
void(* ThreadExitPrintStats)(ThreadVars *, void *)
Definition: tm-modules.h:52
SCLogConfig
struct SCLogConfig_ SCLogConfig
Holds the config state used by the logging api.
Packet_::root
struct Packet_ * root
Definition: decode.h:653
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:267
SCFree
#define SCFree(p)
Definition: util-mem.h:61
DecodeThreadVars_
Structure to hold thread specific data for all decode modules.
Definition: decode.h:963
MAX_PAYLOAD_SIZE
#define MAX_PAYLOAD_SIZE
Definition: decode.h:701
util-ioctl.h
FAIL
#define FAIL
Fail a test.
Definition: util-unittest.h:60
DecodeThreadVarsAlloc
DecodeThreadVars * DecodeThreadVarsAlloc(ThreadVars *tv)
Alloc and setup DecodeThreadVars.
Definition: decode.c:802
TAILQ_HEAD
#define TAILQ_HEAD(name, type)
Definition: queue.h:230
GET_PKT_DIRECT_DATA
#define GET_PKT_DIRECT_DATA(p)
Definition: decode.h:210
SCReturnCT
#define SCReturnCT(x, type)
Definition: util-debug.h:291
suricata.h
StatsSyncCountersIfSignalled
void StatsSyncCountersIfSignalled(ThreadVars *tv)
Definition: counters.c:450
TMM_VERDICTWINDIVERT
@ TMM_VERDICTWINDIVERT
Definition: tm-threads-common.h:65
DecodeIPV4
int DecodeIPV4(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint16_t len)
Definition: decode-ipv4.c:520
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SCReturnInt
#define SCReturnInt(x)
Definition: util-debug.h:281
SCMutexDestroy
#define SCMutexDestroy
Definition: threads-debug.h:120
SCMutex
#define SCMutex
Definition: threads-debug.h:114
PacketGetFromQueueOrAlloc
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
TmModule_::flags
uint8_t flags
Definition: tm-modules.h:80
DecodeUpdatePacketCounters
void DecodeUpdatePacketCounters(ThreadVars *tv, const DecodeThreadVars *dtv, const Packet *p)
Definition: decode.c:768
suricata_ctl_flags
volatile uint8_t suricata_ctl_flags
Definition: suricata.c:172
TmModuleReceiveWinDivertRegister
void TmModuleReceiveWinDivertRegister(void)
Definition: source-windivert.c:61