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 
32 #include "util-byte.h"
33 #include "util-debug.h"
34 #include "util-device.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";
72 }
73 
75 {
76  tmm_modules[TMM_DECODEWINDIVERT].name = "DecodeWinDivert";
79 }
80 
81 TmEcode NoWinDivertSupportExit(ThreadVars *tv, const void *initdata,
82  void **data)
83 {
84  SCLogError(
86  "Error creating thread %s: you do not have support for WinDivert "
87  "enabled; please recompile with --enable-windivert",
88  tv->name);
89  exit(EXIT_FAILURE);
90 }
91 
92 #else /* implied we do have WinDivert support */
93 
94 typedef struct WinDivertThreadVars_ {
95  WinDivertHandle filter_handle;
96 
97  int thread_num;
98  CaptureStats stats;
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 #define ERROR_INVALID_IMAGE_HASH 577L
134 #define ERROR_DATA_NOT_ACCEPTED 592L
135 
136 /**
137  * \brief return an error description for Win32 error values commonly returned
138  * by WinDivert
139  */
140 static const char *WinDivertGetErrorString(DWORD error_code)
141 {
142  switch (error_code) {
143  // WinDivertOpen errors
144  case ERROR_FILE_NOT_FOUND:
145  return "The driver files WinDivert32.sys or WinDivert64.sys were "
146  "not found.";
147  case ERROR_ACCESS_DENIED:
148  return "Suricata must be run with Administrator privileges.";
149  case ERROR_INVALID_PARAMETER:
150  return "The WinDivert packet filter string is invalid.";
151  case ERROR_INVALID_IMAGE_HASH:
152  return "The WinDivert32.sys or WinDivert64.sys driver does not "
153  "have a valid digital signature, or your copy of Windows is "
154  "not up-to-date. Windows 7 and Server 2008 users need to "
155  "run Windows Update or install the following patch from "
156  "Microsoft: http://support.microsoft.com/kb/2949927";
157  case ERROR_DRIVER_BLOCKED:
158  return "This error occurs for various reasons, including: "
159  "attempting to load the 32-bit WinDivert.sys driver on a "
160  "64-bit system (or vice versa); the WinDivert.sys driver is "
161  "blocked by security software; or you are using a "
162  "virtualization environment that does not support "
163  "drivers.";
164  case EPT_S_NOT_REGISTERED:
165  return "This error occurs when the Base Filtering Engine service "
166  "has been disabled.";
167  case ERROR_PROC_NOT_FOUND:
168  return "The error may occur for Windows Vista users. The "
169  "solution is to install the following patch from Microsoft: "
170  "http://support.microsoft.com/kb/2761494.";
171 
172  // WinDivertSend errors
173  case ERROR_HOST_UNREACHABLE:
174  return "This error occurs when an impostor packet (with "
175  "pAddr->Impostor set to 1) is injected and the ip.TTL or "
176  "ipv6.HopLimit field goes to zero. This is a defense of "
177  "last resort against infinite loops caused by impostor "
178  "packets.";
179  case ERROR_DATA_NOT_ACCEPTED:
180  return "This error is returned when the user application attempts "
181  "to inject a malformed packet. It may also be returned for "
182  "valid inbound packets, and the Windows TCP/IP stack "
183  "rejects the packet for some reason.";
184  case ERROR_RETRY:
185  return "The underlying cause of this error is unknown. However, "
186  "this error usually occurs when certain kinds of "
187  "anti-virus/firewall/security software is installed, and "
188  "the error message usually resolves once the offending "
189  "program is uninstalled. This suggests a software "
190  "compatibility problem.";
191  default:
192  return "";
193  }
194 }
195 
196 /**
197  * \brief logs a WinDivert error at Error level.
198  */
199 #define WinDivertLogError(err_code) \
200  do { \
201  const char *win_err_str = Win32GetErrorString((err_code), NULL); \
202  SCLogError(SC_ERR_WINDIVERT_GENERIC, \
203  "WinDivertOpen failed, error %" PRId32 " (0x%08" PRIx32 \
204  "): %s %s", \
205  (uint32_t)(err_code), (uint32_t)(err_code), win_err_str, \
206  WinDivertGetErrorString(err_code)); \
207  LocalFree((LPVOID)win_err_str); \
208  } while (0);
209 
210 /**
211  * \brief initializes QueryPerformanceCounter values so we can get
212  * absolute time from WinDivert timestamps.
213  */
214 static void WinDivertInitQPCValues(WinDivertThreadVars *wd_tv)
215 {
216  struct timeval now;
217 
218  TimeGet(&now);
219  (void)QueryPerformanceCounter((LARGE_INTEGER *)&wd_tv->qpc_start_count);
220 
221  wd_tv->qpc_start_time =
222  (uint64_t)now.tv_sec * (1000 * 1000) + (uint64_t)now.tv_usec;
223 
224  (void)QueryPerformanceFrequency((LARGE_INTEGER *)&wd_tv->qpc_freq_usec);
225  /* \bug: clock drift? */
226  wd_tv->qpc_freq_usec /= 1000 * 1000;
227 }
228 
229 /**
230  * \brief gets a timeval from a WinDivert timestamp
231  */
232 static struct timeval WinDivertTimestampToTimeval(WinDivertThreadVars *wd_tv,
233  INT64 timestamp_count)
234 {
235  struct timeval ts;
236 
237  int64_t qpc_delta = (int64_t)timestamp_count - wd_tv->qpc_start_count;
238  int64_t unix_usec =
239  wd_tv->qpc_start_time + (qpc_delta / wd_tv->qpc_freq_usec);
240 
241  ts.tv_sec = (long)(unix_usec / (1000 * 1000));
242  ts.tv_usec = (long)(unix_usec - (int64_t)ts.tv_sec * (1000 * 1000));
243 
244  return ts;
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(
272  "Invalid filter \"%s\" supplied to WinDivert: %s at position "
273  "%" PRId32 "",
274  filter_str, error_str, error_pos);
276  }
277 
278  /* initialize the queue */
279  SCMutexLock(&g_wd_init_lock);
280 
281  if (g_wd_num >= WINDIVERT_MAX_QUEUE) {
283  "Too many WinDivert queues specified %" PRId32 "", g_wd_num);
284  ret = -1;
285  goto unlock;
286  }
287  if (g_wd_num == 0) {
288  /* on first registration, zero-initialize all array structs */
289  memset(&g_wd_tv, 0, sizeof(g_wd_tv));
290  memset(&g_wd_qv, 0, sizeof(g_wd_qv));
291  }
292 
293  /* init thread vars */
294  WinDivertThreadVars *wd_tv = &g_wd_tv[g_wd_num];
295  wd_tv->thread_num = g_wd_num;
296 
297  /* init queue vars */
298  WinDivertQueueVars *wd_qv = &g_wd_qv[g_wd_num];
299  wd_qv->queue_num = g_wd_num;
300 
301  WinDivertInitQPCValues(wd_tv);
302 
303  /* copy filter to persistent storage */
304  size_t filter_len = strlen(filter_str);
305  size_t copy_len =
306  strlcpy(wd_qv->filter_str, filter_str, sizeof(wd_qv->filter_str));
307  if (filter_len > copy_len) {
309  "Queue length exceeds storage by %" PRId32 " bytes",
310  (int32_t)(filter_len - copy_len));
311  ret = -1;
312  goto unlock;
313  }
314 
315  wd_qv->layer = layer;
316  wd_qv->priority =
317  g_wd_num; /* priority set in the order filters are defined */
318  wd_qv->flags = 0; /* normal inline function */
319 
320  SCMutexInit(&wd_qv->filter_init_mutex, NULL);
321  SCMutexInit(&wd_qv->counters_mutex, NULL);
322 
323  g_wd_num++;
324 
325 unlock:
326  SCMutexUnlock(&g_wd_init_lock);
327 
328  if (ret == 0) {
329  // stringify queue index to use as thread name descriptor
330  char wd_num_str[6];
331  wd_num_str[sizeof(wd_num_str) - 1] = 0;
332  snprintf(wd_num_str, sizeof(wd_num_str), "%" PRId16 "", g_wd_num);
333 
334  LiveRegisterDevice(wd_num_str);
335 
336  SCLogDebug("Queue %" PRId16 " registered", wd_qv->queue_num);
337  }
338 
339  return ret;
340 }
341 
342 /* forward declarations of internal functions */
343 /* Receive functions */
344 TmEcode ReceiveWinDivertLoop(ThreadVars *, void *, void *);
345 TmEcode ReceiveWinDivertThreadInit(ThreadVars *, const void *, void **);
346 TmEcode ReceiveWinDivertThreadDeinit(ThreadVars *, void *);
347 void ReceiveWinDivertThreadExitStats(ThreadVars *, void *);
348 
349 /* Verdict functions */
350 TmEcode VerdictWinDivert(ThreadVars *, Packet *, void *, PacketQueue *,
351  PacketQueue *);
352 TmEcode VerdictWinDivertThreadInit(ThreadVars *, const void *, void **);
353 TmEcode VerdictWinDivertThreadDeinit(ThreadVars *, void *);
354 
355 /* Decode functions */
356 TmEcode DecodeWinDivert(ThreadVars *, Packet *, void *, PacketQueue *,
357  PacketQueue *);
358 TmEcode DecodeWinDivertThreadInit(ThreadVars *, const void *, void **);
359 TmEcode DecodeWinDivertThreadDeinit(ThreadVars *, void *);
360 
361 /* internal helper functions */
362 static TmEcode WinDivertRecvHelper(ThreadVars *tv, WinDivertThreadVars *);
363 static TmEcode WinDivertVerdictHelper(ThreadVars *tv, Packet *p);
364 static TmEcode WinDivertCloseHelper(WinDivertThreadVars *);
365 
366 static TmEcode WinDivertCollectFilterDevices(WinDivertThreadVars *,
367  WinDivertQueueVars *);
368 static bool WinDivertIfaceMatchFilter(const char *filter_string, int if_index);
369 static void WinDivertDisableOffloading(WinDivertThreadVars *);
370 static void WinDivertRestoreOffloading(WinDivertThreadVars *);
371 
373 {
375 
376  tm_ptr->name = "ReceiveWinDivert";
377  tm_ptr->ThreadInit = ReceiveWinDivertThreadInit;
378  tm_ptr->PktAcqLoop = ReceiveWinDivertLoop;
379  tm_ptr->ThreadExitPrintStats = ReceiveWinDivertThreadExitStats;
380  tm_ptr->ThreadDeinit = ReceiveWinDivertThreadDeinit;
381  tm_ptr->flags = TM_FLAG_RECEIVE_TM;
382 }
383 
385 {
387 
388  tm_ptr->name = "VerdictWinDivert";
389  tm_ptr->ThreadInit = VerdictWinDivertThreadInit;
390  tm_ptr->Func = VerdictWinDivert;
391  tm_ptr->ThreadDeinit = VerdictWinDivertThreadDeinit;
392 }
393 
395 {
397 
398  tm_ptr->name = "DecodeWinDivert";
399  tm_ptr->ThreadInit = DecodeWinDivertThreadInit;
400  tm_ptr->Func = DecodeWinDivert;
401  tm_ptr->ThreadDeinit = DecodeWinDivertThreadDeinit;
402  tm_ptr->flags = TM_FLAG_DECODE_TM;
403 }
404 
405 /**
406  * \brief Main WinDivert packet receive pump
407  */
408 TmEcode ReceiveWinDivertLoop(ThreadVars *tv, void *data, void *slot)
409 {
410  SCEnter();
411 
412  WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)data;
413  wd_tv->slot = ((TmSlot *)slot)->slot_next;
414 
415  while (true) {
418  }
419 
420  if (unlikely(WinDivertRecvHelper(tv, wd_tv) != TM_ECODE_OK)) {
422  }
423 
425  }
426 
428 }
429 
430 static TmEcode WinDivertRecvHelper(ThreadVars *tv, WinDivertThreadVars *wd_tv)
431 {
432  SCEnter();
433 
434 #ifdef COUNTERS
435  WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
436 #endif /* COUNTERS */
437 
438  /* make sure we have at least one packet in the packet pool, to prevent us
439  * from alloc'ing packets at line rate
440  */
441  PacketPoolWait();
442 
443  /* obtain a packet buffer */
445  if (unlikely(p == NULL)) {
446  SCLogDebug(
447  "PacketGetFromQueueOrAlloc() - failed to obtain Packet buffer");
449  }
451 
452  /* receive packet, depending on offload status. MTU is used as an estimator
453  * for direct data alloc size, and this is meaningless if large segments are
454  * coalesced before they reach WinDivert */
455  bool success = false;
456  uint32_t pktlen = 0;
457  if (wd_tv->offload_enabled) {
458  /* allocate external, if not already */
460 
461  success =
462  WinDivertRecv(wd_tv->filter_handle, p->ext_pkt,
463  MAX_PAYLOAD_SIZE, &p->windivert_v.addr, &pktlen);
464  } else {
465  success = WinDivertRecv(wd_tv->filter_handle, GET_PKT_DIRECT_DATA(p),
467  &p->windivert_v.addr, &pktlen);
468  }
469  SET_PKT_LEN(p, pktlen);
470 
471  if (!success) {
472 #ifdef COUNTERS
473  SCMutexLock(&wd_qv->counters_mutex);
474  wd_qv->errs++;
475  SCMutexUnlock(&wd_qv->counters_mutex);
476 #endif /* COUNTERS */
477 
478  /* ensure packet length is zero to trigger an error in packet decoding
479  */
480  SET_PKT_LEN(p, 0);
481 
482  SCLogInfo("WinDivertRecv failed: error %" PRIu32 "",
483  (uint32_t)(GetLastError()));
485  }
486  SCLogDebug("Packet received, length %" PRId32 "", GET_PKT_LEN(p));
487 
488  p->ts = WinDivertTimestampToTimeval(wd_tv, p->windivert_v.addr.Timestamp);
489  p->windivert_v.thread_num = wd_tv->thread_num;
490 
491 #ifdef COUNTERS
492  SCMutexLock(&wd_qv->counters_mutex);
493  wd_qv->pkts++;
494  wd_qv->bytes += GET_PKT_LEN(p);
495  SCMutexUnlock(&wd_qv->counters_mutex);
496 #endif /* COUNTERS */
497 
498  /* Do the packet processing by calling TmThreadsSlotProcessPkt, this will,
499  * depending on the running mode, pass the packet to the treatment functions
500  * or push it to a packet pool. So processing time can vary.
501  */
502  if (TmThreadsSlotProcessPkt(tv, wd_tv->slot, p) != TM_ECODE_OK) {
503  TmqhOutputPacketpool(tv, p);
505  }
506 
508 }
509 
510 /**
511  * \brief Init function for ReceiveWinDivert
512  *
513  * ReceiveWinDivertThreadInit sets up receiving packets via WinDivert.
514  *
515  * \param tv pointer to generic thread vars
516  * \param initdata pointer to the interface passed from the user
517  * \param data out-pointer to the WinDivert-specific thread vars
518  */
519 TmEcode ReceiveWinDivertThreadInit(ThreadVars *tv, const void *initdata,
520  void **data)
521 {
522  SCEnter();
523  TmEcode ret = TM_ECODE_OK;
524 
525  WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)initdata;
526 
527  if (wd_tv == NULL) {
528  SCLogError(SC_ERR_INVALID_ARGUMENT, "initdata == NULL");
530  }
531 
532  WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
533 
534  if (wd_qv == NULL) {
535  SCLogError(SC_ERR_INVALID_ARGUMENT, "queue == NULL");
537  }
538 
539  SCMutexLock(&wd_qv->filter_init_mutex);
540  /* does the queue already have an active handle? */
541  if (wd_qv->filter_handle != NULL &&
542  wd_qv->filter_handle != INVALID_HANDLE_VALUE) {
543  goto unlock;
544  }
545 
546  TAILQ_INIT(&wd_tv->live_devices);
547 
548  if (WinDivertCollectFilterDevices(wd_tv, wd_qv) == TM_ECODE_OK) {
549  WinDivertDisableOffloading(wd_tv);
550  } else {
552  "Failed to obtain network devices for WinDivert filter");
553  }
554 
555  /* we open now so that we can immediately start handling packets,
556  * instead of losing however many would occur between registering the
557  * queue and starting a receive thread. */
558  wd_qv->filter_handle = WinDivertOpen(wd_qv->filter_str, wd_qv->layer,
559  wd_qv->priority, wd_qv->flags);
560  if (wd_qv->filter_handle == INVALID_HANDLE_VALUE) {
561  WinDivertLogError(GetLastError());
562  ret = TM_ECODE_FAILED;
563  goto unlock;
564  }
565 
566 unlock:
567  if (ret == 0) { /* success */
568  wd_tv->filter_handle = wd_qv->filter_handle;
569 
570  /* set our return context */
571  *data = wd_tv;
572  }
573 
574  SCMutexUnlock(&wd_qv->filter_init_mutex);
575  SCReturnInt(ret);
576 }
577 
578 /**
579  * \brief collect all devices covered by this filter in the thread vars'
580  * live devices list
581  *
582  * \param wd_tv pointer to WinDivert thread vars
583  * \param wd_qv pointer to WinDivert queue vars
584  */
585 static TmEcode WinDivertCollectFilterDevices(WinDivertThreadVars *wd_tv,
586  WinDivertQueueVars *wd_qv)
587 {
588  SCEnter();
589  TmEcode ret = TM_ECODE_OK;
590 
591  IP_ADAPTER_ADDRESSES *if_info_list;
592  DWORD err = (DWORD)Win32GetAdaptersAddresses(&if_info_list);
593  if (err != NO_ERROR) {
594  ret = TM_ECODE_FAILED;
595  goto release;
596  }
597 
598  for (IP_ADAPTER_ADDRESSES *if_info = if_info_list; if_info != NULL;
599  if_info = if_info->Next) {
600 
601  if (WinDivertIfaceMatchFilter(wd_qv->filter_str, if_info->IfIndex)) {
602  SCLogConfig("Found adapter %s matching WinDivert filter %s",
603  if_info->AdapterName, wd_qv->filter_str);
604 
605  LiveDevice *new_ldev = SCCalloc(1, sizeof(LiveDevice));
606  if (new_ldev == NULL) {
607  ret = TM_ECODE_FAILED;
608  goto release;
609  }
610  new_ldev->dev = SCStrdup(if_info->AdapterName);
611  if (new_ldev->dev == NULL) {
612  ret = TM_ECODE_FAILED;
613  goto release;
614  }
615  TAILQ_INSERT_TAIL(&wd_tv->live_devices, new_ldev, next);
616  } else {
617  SCLogDebug("Adapter %s does not match WinDivert filter %s",
618  if_info->AdapterName, wd_qv->filter_str);
619  }
620  }
621 
622 release:
623  SCFree(if_info_list);
624 
625  SCReturnInt(ret);
626 }
627 
628 /**
629  * \brief test if the specified interface index matches the filter
630  */
631 static bool WinDivertIfaceMatchFilter(const char *filter_string, int if_index)
632 {
633  bool match = false;
634 
635  WINDIVERT_ADDRESS if_addr = {};
636  if_addr.IfIdx = if_index;
637 
638  uint8_t dummy[4] = {4, 4, 4, 4};
639 
640  match = WinDivertHelperEvalFilter(filter_string, WINDIVERT_LAYER_NETWORK,
641  dummy, sizeof(dummy), &if_addr);
642  if (!match) {
643  int err = GetLastError();
644  if (err != 0) {
646  "Failed to evaluate filter: 0x%" PRIx32, err);
647  }
648  }
649 
650  return match;
651 }
652 
653 /**
654  * \brief disable offload status on devices for this filter
655  *
656  * \param wd_tv pointer to WinDivert thread vars
657  */
658 static void WinDivertDisableOffloading(WinDivertThreadVars *wd_tv)
659 {
660  for (LiveDevice *ldev = TAILQ_FIRST(&wd_tv->live_devices); ldev != NULL;
661  ldev = TAILQ_NEXT(ldev, next)) {
662 
663  if (LiveGetOffload() == 0) {
664  if (GetIfaceOffloading(ldev->dev, 1, 1) == 1) {
665  wd_tv->offload_enabled = true;
666  }
667  } else {
668  if (DisableIfaceOffloading(ldev, 1, 1) != 1) {
669  wd_tv->offload_enabled = true;
670  }
671  }
672  }
673 }
674 
675 /**
676  * \brief enable offload status on devices for this filter
677  *
678  * \param wd_tv pointer to WinDivert thread vars
679  */
680 static void WinDivertRestoreOffloading(WinDivertThreadVars *wd_tv)
681 {
682  for (LiveDevice *ldev = TAILQ_FIRST(&wd_tv->live_devices); ldev != NULL;
683  ldev = TAILQ_NEXT(ldev, next)) {
684 
686  }
687 }
688 
689 /**
690  * \brief Deinit function releases resources at exit.
691  *
692  * \param tv pointer to generic thread vars
693  * \param data pointer to WinDivert-specific thread vars
694  */
695 TmEcode ReceiveWinDivertThreadDeinit(ThreadVars *tv, void *data)
696 {
697  SCEnter();
698 
699  WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)data;
700 
701  SCReturnCT(WinDivertCloseHelper(wd_tv), "TmEcode");
702 }
703 
704 /**
705  * \brief ExitStats prints stats to stdout at exit
706  *
707  *
708  * \param tv pointer to generic thread vars
709  * \param data pointer to WinDivert-specific thread vars
710  */
711 void ReceiveWinDivertThreadExitStats(ThreadVars *tv, void *data)
712 {
713  SCEnter();
714 
715  WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)data;
716  WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
717  if (wd_qv == NULL) {
718  SCLogError(SC_ERR_INVALID_ARGUMENT, "queue == NULL");
719  SCReturn;
720  }
721 
722  SCMutexLock(&wd_qv->counters_mutex);
723 
724  SCLogInfo("(%s) Packets %" PRIu32 ", Bytes %" PRIu64 ", Errors %" PRIu32 "",
725  tv->name, wd_qv->pkts, wd_qv->bytes, wd_qv->errs);
726  SCLogInfo("(%s) Verdict: Accepted %" PRIu32 ", Dropped %" PRIu32
727  ", Replaced %" PRIu32 "",
728  tv->name, wd_qv->accepted, wd_qv->dropped, wd_qv->replaced);
729 
730  SCMutexUnlock(&wd_qv->counters_mutex);
731  SCReturn;
732 }
733 
734 /**
735  * \brief WinDivert verdict module packet entry function
736  */
737 TmEcode VerdictWinDivert(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq,
738  PacketQueue *postpq)
739 {
740  SCEnter();
741 
742  TmEcode ret = TM_ECODE_OK;
743 
744  ret = WinDivertVerdictHelper(tv, p);
745  if (ret != TM_ECODE_OK) {
746  SCReturnInt(ret);
747  }
748 
750 }
751 
752 /**
753  * \brief internal helper function to do the bulk of verdict work
754  */
755 static TmEcode WinDivertVerdictHelper(ThreadVars *tv, Packet *p)
756 {
757  SCEnter();
758  WinDivertThreadVars *wd_tv = WinDivertGetThread(p->windivert_v.thread_num);
759 
760  /* update counters */
761  CaptureStatsUpdate(tv, &wd_tv->stats, p);
762 
763 #ifdef COUNTERS
764  WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
765 #endif /* COUNTERS */
766 
767  p->windivert_v.verdicted = true;
768 
769  /* can't verdict a "fake" packet */
770  if (PKT_IS_PSEUDOPKT(p)) {
772  }
773 
774  /* the handle has been closed and we can no longer use it */
775  if (wd_tv->filter_handle == INVALID_HANDLE_VALUE ||
776  wd_tv->filter_handle == NULL) {
778  }
779 
780  /* we can't verdict tunnel packets without ensuring all encapsulated
781  * packets are verdicted */
782  if (IS_TUNNEL_PKT(p)) {
783  bool finalVerdict = VerdictTunnelPacket(p);
784  if (!finalVerdict) {
786  }
787 
788  // the action needs to occur on the root packet.
789  if (p->root != NULL) {
790  p = p->root;
791  }
792  }
793 
794  /* DROP simply means we do nothing; the WinDivert driver does the rest.
795  */
797 #ifdef COUNTERS
798  SCMutexLock(&wd_qv->counters_mutex);
799  wd_qv->dropped++;
800  SCMutexUnlock(&wd_qv->counters_mutex);
801 #endif /* counters */
802 
804  }
805 
806  bool success = WinDivertSend(wd_tv->filter_handle, GET_PKT_DATA(p),
807  GET_PKT_LEN(p), &p->windivert_v.addr, NULL);
808 
809  if (unlikely(!success)) {
810  WinDivertLogError(GetLastError());
812  }
813 
814 #ifdef COUNTERS
815  SCMutexLock(&wd_qv->counters_mutex);
816  wd_qv->accepted++;
817  SCMutexUnlock(&wd_qv->counters_mutex);
818 #endif /* counters */
819 
821 }
822 
823 /**
824  * \brief init the verdict thread, which is piggybacked off the receive
825  * thread
826  */
827 TmEcode VerdictWinDivertThreadInit(ThreadVars *tv, const void *initdata,
828  void **data)
829 {
830  SCEnter();
831 
832  WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)initdata;
833 
834  CaptureStatsSetup(tv, &wd_tv->stats);
835 
836  *data = wd_tv;
837 
839 }
840 
841 /**
842  * \brief deinit the verdict thread and shut down the WinDivert driver if
843  * it's still up.
844  */
845 TmEcode VerdictWinDivertThreadDeinit(ThreadVars *tv, void *data)
846 {
847  SCEnter();
848 
849  WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)data;
850 
851  SCReturnCT(WinDivertCloseHelper(wd_tv), "TmEcode");
852 }
853 
854 /**
855  * \brief decode a raw packet submitted to suricata from the WinDivert
856  * driver
857  *
858  * All WinDivert packets are IPv4/v6, but do not include the network layer
859  * to differentiate the two, so instead we must check the version and go
860  * from there.
861  */
862 TmEcode DecodeWinDivert(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq,
863  PacketQueue *postpq)
864 {
865  SCEnter();
866 
867  IPV4Hdr *ip4h = (IPV4Hdr *)GET_PKT_DATA(p);
868  IPV6Hdr *ip6h = (IPV6Hdr *)GET_PKT_DATA(p);
869  DecodeThreadVars *d_tv = (DecodeThreadVars *)data;
870 
871  /* XXX HACK: flow timeout can call us for injected pseudo packets
872  * see bug:
873  * https://redmine.openinfosecfoundation.org/issues/1107
874  */
875  if (PKT_IS_PSEUDOPKT(p))
877 
878  DecodeUpdatePacketCounters(tv, d_tv, p);
879 
880  if (IPV4_GET_RAW_VER(ip4h) == 4) {
881  SCLogDebug("IPv4 packet");
882  DecodeIPV4(tv, d_tv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
883  } else if (IPV6_GET_RAW_VER(ip6h) == 6) {
884  SCLogDebug("IPv6 packet");
885  DecodeIPV6(tv, d_tv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
886  } else {
887  SCLogDebug("packet unsupported by WinDivert, first byte: %02x",
888  *GET_PKT_DATA(p));
889  }
890 
891  PacketDecodeFinalize(tv, d_tv, p);
892 
894 }
895 
896 TmEcode DecodeWinDivertThreadInit(ThreadVars *tv, const void *initdata,
897  void **data)
898 {
899  SCEnter();
900 
902  if (d_tv == NULL) {
904  }
905 
906  DecodeRegisterPerfCounters(d_tv, tv);
907 
908  *data = d_tv;
909 
911 }
912 
913 TmEcode DecodeWinDivertThreadDeinit(ThreadVars *tv, void *data)
914 {
915  SCEnter();
916 
917  if (data != NULL) {
918  DecodeThreadVarsFree(tv, data);
919  }
920 
922 }
923 
924 /**
925  * \brief helper function for use with ThreadDeinit functions
926  */
927 static TmEcode WinDivertCloseHelper(WinDivertThreadVars *wd_tv)
928 {
929  SCEnter();
930  TmEcode ret = TM_ECODE_OK;
931 
932  WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
933  if (wd_qv == NULL) {
934  SCLogDebug("No queue could be found for thread num %" PRId32 "",
935  wd_tv->thread_num);
937  }
938 
939  SCMutexLock(&wd_qv->filter_init_mutex);
940 
941  /* check if there's nothing to close */
942  if (wd_qv->filter_handle == INVALID_HANDLE_VALUE ||
943  wd_qv->filter_handle == NULL) {
944  goto unlock;
945  }
946 
947  if (!WinDivertClose(wd_qv->filter_handle)) {
948  SCLogError(SC_ERR_FATAL, "WinDivertClose failed: error %" PRIu32 "",
949  (uint32_t)(GetLastError()));
950  ret = TM_ECODE_FAILED;
951  goto unlock;
952  }
953 
954  (void)WinDivertRestoreOffloading(wd_tv);
955 
956  wd_qv->filter_handle = NULL;
957 
958 unlock:
959  SCMutexUnlock(&wd_qv->filter_init_mutex);
960 
961  if (ret == TM_ECODE_OK) {
962  SCMutexDestroy(&wd_qv->filter_init_mutex);
963  SCMutexDestroy(&wd_qv->counters_mutex);
964  }
965 
966  SCReturnInt(ret);
967 }
968 
969 #ifdef UNITTESTS
970 static int SourceWinDivertTestIfaceMatchFilter(void)
971 {
972  struct testdata {
973  const char *filter;
974  int if_index;
975  bool expected;
976  };
977 
978  struct testdata tests[] = {
979  {"true", 11, true},
980  {"ifIdx=11", 11, true},
981  {"ifIdx==11", 11, true},
982  {"ifIdx!=11", 1, true},
983  {"ifIdx!=11", 11, false},
984  {"ifIdx=3", 4, false},
985  {"ifIdx=11 || ifIdx=5", 5, true},
986  {"ifIdx=11 || ifIdx=4", 5, false},
987  {"ifIdx<3 || ifIdx>7", 8, true},
988  {"ifIdx<3 || ifIdx>7", 5, false},
989  {"ifIdx>3 or ifIdx<7", 5, true},
990  {"ifIdx>3 && ifIdx<7", 5, true},
991  {"ifIdx>3 && ifIdx<7", 1, false},
992  {"(ifIdx > 3 && ifIdx < 7) or ifIdx == 11", 11, true}};
993 
994  size_t count = (sizeof(tests) / sizeof(tests[0]));
995 
996  for (size_t i = 0; i < count; i++) {
997  struct testdata test = tests[i];
998 
999  bool actual = WinDivertIfaceMatchFilter(test.filter, test.if_index);
1000  if (actual != test.expected) {
1001  printf("WinDivertIfaceMatchFilter(\"%s\", %d) == %d, expected %d\n",
1002  test.filter, test.if_index, actual, test.expected);
1003  FAIL;
1004  }
1005  }
1006  PASS;
1007 }
1008 #endif
1009 
1010 /**
1011  * \brief this function registers unit tests for the WinDivert Source
1012  */
1013 void SourceWinDivertRegisterTests()
1014 {
1015 #ifdef UNITTESTS
1016  UtRegisterTest("SourceWinDivertTestIfaceMatchFilter",
1017  SourceWinDivertTestIfaceMatchFilter);
1018 #endif
1019 }
1020 
1021 #endif /* WINDIVERT */
#define SCMutex
#define TM_FLAG_DECODE_TM
Definition: tm-modules.h:32
#define GET_PKT_DIRECT_MAX_SIZE(p)
Definition: decode.h:228
DecodeThreadVars * DecodeThreadVarsAlloc(ThreadVars *tv)
Alloc and setup DecodeThreadVars.
Definition: decode.c:614
#define SCLogDebug(...)
Definition: util-debug.h:335
void CaptureStatsUpdate(ThreadVars *tv, CaptureStats *s, const Packet *p)
Definition: decode.c:706
#define TAILQ_FIRST(head)
Definition: queue.h:339
int LiveRegisterDevice(const char *dev)
Add a pcap device for monitoring and create structure.
Definition: util-device.c:124
struct HtpBodyChunk_ * next
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: util-strlcpyu.c:43
uint8_t flags
Definition: tm-modules.h:70
#define PACKET_TEST_ACTION(p, a)
Definition: decode.h:858
void TmModuleVerdictWinDivertRegister(void)
TmEcode NoWinDivertSupportExit(ThreadVars *, const void *, void **)
#define SET_PKT_LEN(p, len)
Definition: decode.h:230
void PacketDecodeFinalize(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p)
Finalize decoding of a packet.
Definition: decode.c:115
#define PASS
Pass the test.
#define unlikely(expr)
Definition: util-optimize.h:35
TmEcode(* Func)(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *)
Definition: tm-modules.h:52
void DecodeRegisterPerfCounters(DecodeThreadVars *dtv, ThreadVars *tv)
Definition: decode.c:474
#define IS_TUNNEL_PKT(p)
Definition: decode.h:882
volatile uint8_t suricata_ctl_flags
Definition: suricata.c:201
uint8_t * ext_pkt
Definition: decode.h:551
#define TAILQ_HEAD(name, type)
Definition: queue.h:321
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:177
TmEcode(* PktAcqLoop)(ThreadVars *, void *, void *)
Definition: tm-modules.h:54
#define SCMutexLock(mut)
#define IPV4_GET_RAW_VER(ip4h)
Definition: decode-ipv4.h:94
#define SCReturnCT(x, type)
Definition: util-debug.h:351
int GetIfaceOffloading(const char *dev, int csum, int other)
output offloading status of the link
Definition: util-ioctl.c:694
#define PKT_SET_SRC(p, src_val)
Definition: decode.h:1136
char * dev
Definition: util-device.h:41
#define TM_FLAG_RECEIVE_TM
Definition: tm-modules.h:31
#define SURICATA_STOP
Definition: suricata.h:95
#define SCCalloc(nm, a)
Definition: util-mem.h:253
#define SCMutexUnlock(mut)
void TmqhOutputPacketpool(ThreadVars *t, Packet *p)
#define SCMUTEX_INITIALIZER
#define IPV6_GET_RAW_VER(ip6h)
Definition: decode-ipv6.h:62
void TimeGet(struct timeval *tv)
Definition: util-time.c:146
#define TAILQ_INIT(head)
Definition: queue.h:370
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:294
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Structure to hold thread specific data for all decode modules.
Definition: decode.h:633
#define GET_PKT_DIRECT_DATA(p)
Definition: decode.h:227
TmEcode(* ThreadDeinit)(ThreadVars *, void *)
Definition: tm-modules.h:49
#define SCEnter(...)
Definition: util-debug.h:337
#define SCMutexInit(mut, mutattrs)
#define SCReturnInt(x)
Definition: util-debug.h:341
void(* ThreadExitPrintStats)(ThreadVars *, void *)
Definition: tm-modules.h:48
#define SCLogWarning(err_code,...)
Macro used to log WARNING messages.
Definition: util-debug.h:281
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:385
void PacketPoolWait(void)
#define FAIL
Definition: util-unittest.h:60
const char * name
Definition: tm-modules.h:44
int DecodeIPV6(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint16_t len, PacketQueue *pq)
Definition: decode-ipv6.c:585
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:254
#define SCFree(a)
Definition: util-mem.h:322
int DecodeIPV4(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint16_t len, PacketQueue *pq)
Definition: decode-ipv4.c:532
TmModule tmm_modules[TMM_SIZE]
Definition: tm-modules.h:73
int DisableIfaceOffloading(LiveDevice *dev, int csum, int other)
Definition: util-ioctl.c:707
int PacketCallocExtPkt(Packet *p, int datalen)
Definition: decode.c:192
uint64_t ts
#define MAX_PAYLOAD_SIZE
Definition: decode.h:617
#define StatsSyncCountersIfSignalled(tv)
Definition: counters.h:137
TmEcode(* ThreadInit)(ThreadVars *, const void *, void **)
Definition: tm-modules.h:47
#define GET_PKT_DATA(p)
Definition: decode.h:226
void DecodeUpdatePacketCounters(ThreadVars *tv, const DecodeThreadVars *dtv, const Packet *p)
Definition: decode.c:580
#define SCStrdup(a)
Definition: util-mem.h:268
char name[16]
Definition: threadvars.h:59
#define PKT_IS_PSEUDOPKT(p)
return 1 if the packet is a pseudo packet
Definition: decode.h:1133
#define TAILQ_NEXT(elm, field)
Definition: queue.h:341
void CaptureStatsSetup(ThreadVars *tv, CaptureStats *s)
Definition: decode.c:719
#define SCReturn
Definition: util-debug.h:339
Per thread variable structure.
Definition: threadvars.h:57
struct timeval ts
Definition: decode.h:452
#define GET_PKT_LEN(p)
Definition: decode.h:225
void RestoreIfaceOffloading(LiveDevice *dev)
Definition: util-ioctl.c:724
#define ACTION_DROP
struct SCLogConfig_ SCLogConfig
Holds the config state used by the logging api.
void DecodeThreadVarsFree(ThreadVars *tv, DecodeThreadVars *dtv)
Definition: decode.c:633
int LiveGetOffload(void)
Definition: util-device.c:79
void TmModuleReceiveWinDivertRegister(void)
void TmModuleDecodeWinDivertRegister(void)
struct Packet_ * root
Definition: decode.h:578
#define SCMutexDestroy