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.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 
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 *);
351 TmEcode VerdictWinDivertThreadInit(ThreadVars *, const void *, void **);
352 TmEcode VerdictWinDivertThreadDeinit(ThreadVars *, void *);
353 
354 /* Decode functions */
355 TmEcode DecodeWinDivert(ThreadVars *, Packet *, void *);
356 TmEcode DecodeWinDivertThreadInit(ThreadVars *, const void *, void **);
357 TmEcode DecodeWinDivertThreadDeinit(ThreadVars *, void *);
358 
359 /* internal helper functions */
360 static TmEcode WinDivertRecvHelper(ThreadVars *tv, WinDivertThreadVars *);
361 static TmEcode WinDivertVerdictHelper(ThreadVars *tv, Packet *p);
362 static TmEcode WinDivertCloseHelper(WinDivertThreadVars *);
363 
364 static TmEcode WinDivertCollectFilterDevices(WinDivertThreadVars *,
365  WinDivertQueueVars *);
366 static bool WinDivertIfaceMatchFilter(const char *filter_string, int if_index);
367 static void WinDivertDisableOffloading(WinDivertThreadVars *);
368 static void WinDivertRestoreOffloading(WinDivertThreadVars *);
369 
371 {
373 
374  tm_ptr->name = "ReceiveWinDivert";
375  tm_ptr->ThreadInit = ReceiveWinDivertThreadInit;
376  tm_ptr->PktAcqLoop = ReceiveWinDivertLoop;
377  tm_ptr->ThreadExitPrintStats = ReceiveWinDivertThreadExitStats;
378  tm_ptr->ThreadDeinit = ReceiveWinDivertThreadDeinit;
379  tm_ptr->flags = TM_FLAG_RECEIVE_TM;
380 }
381 
383 {
385 
386  tm_ptr->name = "VerdictWinDivert";
387  tm_ptr->ThreadInit = VerdictWinDivertThreadInit;
388  tm_ptr->Func = VerdictWinDivert;
389  tm_ptr->ThreadDeinit = VerdictWinDivertThreadDeinit;
390 }
391 
393 {
395 
396  tm_ptr->name = "DecodeWinDivert";
397  tm_ptr->ThreadInit = DecodeWinDivertThreadInit;
398  tm_ptr->Func = DecodeWinDivert;
399  tm_ptr->ThreadDeinit = DecodeWinDivertThreadDeinit;
400  tm_ptr->flags = TM_FLAG_DECODE_TM;
401 }
402 
403 /**
404  * \brief Main WinDivert packet receive pump
405  */
406 TmEcode ReceiveWinDivertLoop(ThreadVars *tv, void *data, void *slot)
407 {
408  SCEnter();
409 
410  WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)data;
411  wd_tv->slot = ((TmSlot *)slot)->slot_next;
412 
413  // Indicate that the thread is actually running its application level code (i.e., it can poll
414  // packets)
416 
417  while (true) {
420  }
421 
422  if (unlikely(WinDivertRecvHelper(tv, wd_tv) != TM_ECODE_OK)) {
424  }
425 
427  }
428 
430 }
431 
432 static TmEcode WinDivertRecvHelper(ThreadVars *tv, WinDivertThreadVars *wd_tv)
433 {
434  SCEnter();
435 
436 #ifdef COUNTERS
437  WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
438 #endif /* COUNTERS */
439 
440  /* make sure we have at least one packet in the packet pool, to prevent us
441  * from alloc'ing packets at line rate
442  */
443  PacketPoolWait();
444 
445  /* obtain a packet buffer */
447  if (unlikely(p == NULL)) {
448  SCLogDebug(
449  "PacketGetFromQueueOrAlloc() - failed to obtain Packet buffer");
451  }
453 
454  /* receive packet, depending on offload status. MTU is used as an estimator
455  * for direct data alloc size, and this is meaningless if large segments are
456  * coalesced before they reach WinDivert */
457  bool success = false;
458  uint32_t pktlen = 0;
459  if (wd_tv->offload_enabled) {
460  /* allocate external, if not already */
462 
463  success =
464  WinDivertRecv(wd_tv->filter_handle, p->ext_pkt,
465  MAX_PAYLOAD_SIZE, &p->windivert_v.addr, &pktlen);
466  } else {
467  success = WinDivertRecv(wd_tv->filter_handle, GET_PKT_DIRECT_DATA(p),
469  &p->windivert_v.addr, &pktlen);
470  }
471  SET_PKT_LEN(p, pktlen);
472 
473  if (!success) {
474 #ifdef COUNTERS
475  SCMutexLock(&wd_qv->counters_mutex);
476  wd_qv->errs++;
477  SCMutexUnlock(&wd_qv->counters_mutex);
478 #endif /* COUNTERS */
479 
480  /* ensure packet length is zero to trigger an error in packet decoding
481  */
482  SET_PKT_LEN(p, 0);
483 
484  SCLogInfo("WinDivertRecv failed: error %" PRIu32 "",
485  (uint32_t)(GetLastError()));
487  }
488  SCLogDebug("Packet received, length %" PRId32 "", GET_PKT_LEN(p));
489 
490  p->ts = WinDivertTimestampToTimeval(wd_tv, p->windivert_v.addr.Timestamp);
491  p->windivert_v.thread_num = wd_tv->thread_num;
492 
493 #ifdef COUNTERS
494  SCMutexLock(&wd_qv->counters_mutex);
495  wd_qv->pkts++;
496  wd_qv->bytes += GET_PKT_LEN(p);
497  SCMutexUnlock(&wd_qv->counters_mutex);
498 #endif /* COUNTERS */
499 
500  /* Do the packet processing by calling TmThreadsSlotProcessPkt, this will,
501  * depending on the running mode, pass the packet to the treatment functions
502  * or push it to a packet pool. So processing time can vary.
503  */
504  if (TmThreadsSlotProcessPkt(tv, wd_tv->slot, p) != TM_ECODE_OK) {
506  }
507 
509 }
510 
511 /**
512  * \brief Init function for ReceiveWinDivert
513  *
514  * ReceiveWinDivertThreadInit sets up receiving packets via WinDivert.
515  *
516  * \param tv pointer to generic thread vars
517  * \param initdata pointer to the interface passed from the user
518  * \param data out-pointer to the WinDivert-specific thread vars
519  */
520 TmEcode ReceiveWinDivertThreadInit(ThreadVars *tv, const void *initdata,
521  void **data)
522 {
523  SCEnter();
524  TmEcode ret = TM_ECODE_OK;
525 
526  WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)initdata;
527 
528  if (wd_tv == NULL) {
529  SCLogError(SC_ERR_INVALID_ARGUMENT, "initdata == NULL");
531  }
532 
533  WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
534 
535  if (wd_qv == NULL) {
536  SCLogError(SC_ERR_INVALID_ARGUMENT, "queue == NULL");
538  }
539 
540  SCMutexLock(&wd_qv->filter_init_mutex);
541  /* does the queue already have an active handle? */
542  if (wd_qv->filter_handle != NULL &&
543  wd_qv->filter_handle != INVALID_HANDLE_VALUE) {
544  goto unlock;
545  }
546 
547  TAILQ_INIT(&wd_tv->live_devices);
548 
549  if (WinDivertCollectFilterDevices(wd_tv, wd_qv) == TM_ECODE_OK) {
550  WinDivertDisableOffloading(wd_tv);
551  } else {
553  "Failed to obtain network devices for WinDivert filter");
554  }
555 
556  /* we open now so that we can immediately start handling packets,
557  * instead of losing however many would occur between registering the
558  * queue and starting a receive thread. */
559  wd_qv->filter_handle = WinDivertOpen(wd_qv->filter_str, wd_qv->layer,
560  wd_qv->priority, wd_qv->flags);
561  if (wd_qv->filter_handle == INVALID_HANDLE_VALUE) {
562  WinDivertLogError(GetLastError());
563  ret = TM_ECODE_FAILED;
564  goto unlock;
565  }
566 
567 unlock:
568  if (ret == 0) { /* success */
569  wd_tv->filter_handle = wd_qv->filter_handle;
570 
571  /* set our return context */
572  *data = wd_tv;
573  }
574 
575  SCMutexUnlock(&wd_qv->filter_init_mutex);
576  SCReturnInt(ret);
577 }
578 
579 /**
580  * \brief collect all devices covered by this filter in the thread vars'
581  * live devices list
582  *
583  * \param wd_tv pointer to WinDivert thread vars
584  * \param wd_qv pointer to WinDivert queue vars
585  */
586 static TmEcode WinDivertCollectFilterDevices(WinDivertThreadVars *wd_tv,
587  WinDivertQueueVars *wd_qv)
588 {
589  SCEnter();
590  TmEcode ret = TM_ECODE_OK;
591 
592  IP_ADAPTER_ADDRESSES *if_info_list;
593  DWORD err = (DWORD)Win32GetAdaptersAddresses(&if_info_list);
594  if (err != NO_ERROR) {
595  ret = TM_ECODE_FAILED;
596  goto release;
597  }
598 
599  for (IP_ADAPTER_ADDRESSES *if_info = if_info_list; if_info != NULL;
600  if_info = if_info->Next) {
601 
602  if (WinDivertIfaceMatchFilter(wd_qv->filter_str, if_info->IfIndex)) {
603  SCLogConfig("Found adapter %s matching WinDivert filter %s",
604  if_info->AdapterName, wd_qv->filter_str);
605 
606  LiveDevice *new_ldev = SCCalloc(1, sizeof(LiveDevice));
607  if (new_ldev == NULL) {
608  ret = TM_ECODE_FAILED;
609  goto release;
610  }
611  new_ldev->dev = SCStrdup(if_info->AdapterName);
612  if (new_ldev->dev == NULL) {
613  ret = TM_ECODE_FAILED;
614  goto release;
615  }
616  TAILQ_INSERT_TAIL(&wd_tv->live_devices, new_ldev, next);
617  } else {
618  SCLogDebug("Adapter %s does not match WinDivert filter %s",
619  if_info->AdapterName, wd_qv->filter_str);
620  }
621  }
622 
623 release:
624  SCFree(if_info_list);
625 
626  SCReturnInt(ret);
627 }
628 
629 /**
630  * \brief test if the specified interface index matches the filter
631  */
632 static bool WinDivertIfaceMatchFilter(const char *filter_string, int if_index)
633 {
634  bool match = false;
635 
636  WINDIVERT_ADDRESS if_addr = {};
637  if_addr.IfIdx = if_index;
638 
639  uint8_t dummy[4] = {4, 4, 4, 4};
640 
641  match = WinDivertHelperEvalFilter(filter_string, WINDIVERT_LAYER_NETWORK,
642  dummy, sizeof(dummy), &if_addr);
643  if (!match) {
644  int err = GetLastError();
645  if (err != 0) {
647  "Failed to evaluate filter: 0x%" PRIx32, err);
648  }
649  }
650 
651  return match;
652 }
653 
654 /**
655  * \brief disable offload status on devices for this filter
656  *
657  * \param wd_tv pointer to WinDivert thread vars
658  */
659 static void WinDivertDisableOffloading(WinDivertThreadVars *wd_tv)
660 {
661  for (LiveDevice *ldev = TAILQ_FIRST(&wd_tv->live_devices); ldev != NULL;
662  ldev = TAILQ_NEXT(ldev, next)) {
663 
664  if (LiveGetOffload() == 0) {
665  if (GetIfaceOffloading(ldev->dev, 1, 1) == 1) {
666  wd_tv->offload_enabled = true;
667  }
668  } else {
669  if (DisableIfaceOffloading(ldev, 1, 1) != 1) {
670  wd_tv->offload_enabled = true;
671  }
672  }
673  }
674 }
675 
676 /**
677  * \brief enable offload status on devices for this filter
678  *
679  * \param wd_tv pointer to WinDivert thread vars
680  */
681 static void WinDivertRestoreOffloading(WinDivertThreadVars *wd_tv)
682 {
683  for (LiveDevice *ldev = TAILQ_FIRST(&wd_tv->live_devices); ldev != NULL;
684  ldev = TAILQ_NEXT(ldev, next)) {
685 
687  }
688 }
689 
690 /**
691  * \brief Deinit function releases resources at exit.
692  *
693  * \param tv pointer to generic thread vars
694  * \param data pointer to WinDivert-specific thread vars
695  */
696 TmEcode ReceiveWinDivertThreadDeinit(ThreadVars *tv, void *data)
697 {
698  SCEnter();
699 
700  WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)data;
701 
702  SCReturnCT(WinDivertCloseHelper(wd_tv), "TmEcode");
703 }
704 
705 /**
706  * \brief ExitStats prints stats to stdout at exit
707  *
708  *
709  * \param tv pointer to generic thread vars
710  * \param data pointer to WinDivert-specific thread vars
711  */
712 void ReceiveWinDivertThreadExitStats(ThreadVars *tv, void *data)
713 {
714  SCEnter();
715 
716  WinDivertThreadVars *wd_tv = (WinDivertThreadVars *)data;
717  WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
718  if (wd_qv == NULL) {
719  SCLogError(SC_ERR_INVALID_ARGUMENT, "queue == NULL");
720  SCReturn;
721  }
722 
723  SCMutexLock(&wd_qv->counters_mutex);
724 
725  SCLogInfo("(%s) Packets %" PRIu32 ", Bytes %" PRIu64 ", Errors %" PRIu32 "",
726  tv->name, wd_qv->pkts, wd_qv->bytes, wd_qv->errs);
727  SCLogInfo("(%s) Verdict: Accepted %" PRIu32 ", Dropped %" PRIu32
728  ", Replaced %" PRIu32 "",
729  tv->name, wd_qv->accepted, wd_qv->dropped, wd_qv->replaced);
730 
731  SCMutexUnlock(&wd_qv->counters_mutex);
732  SCReturn;
733 }
734 
735 /**
736  * \brief WinDivert verdict module packet entry function
737  */
738 TmEcode VerdictWinDivert(ThreadVars *tv, Packet *p, void *data)
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  */
796  if (PacketCheckAction(p, ACTION_DROP)) {
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)
863 {
864  SCEnter();
865 
866  IPV4Hdr *ip4h = (IPV4Hdr *)GET_PKT_DATA(p);
867  IPV6Hdr *ip6h = (IPV6Hdr *)GET_PKT_DATA(p);
868  DecodeThreadVars *d_tv = (DecodeThreadVars *)data;
869 
871 
872  DecodeUpdatePacketCounters(tv, d_tv, p);
873 
874  if (IPV4_GET_RAW_VER(ip4h) == 4) {
875  SCLogDebug("IPv4 packet");
876  DecodeIPV4(tv, d_tv, p, GET_PKT_DATA(p), GET_PKT_LEN(p));
877  } else if (IPV6_GET_RAW_VER(ip6h) == 6) {
878  SCLogDebug("IPv6 packet");
879  DecodeIPV6(tv, d_tv, p, GET_PKT_DATA(p), GET_PKT_LEN(p));
880  } else {
881  SCLogDebug("packet unsupported by WinDivert, first byte: %02x",
882  *GET_PKT_DATA(p));
883  }
884 
885  PacketDecodeFinalize(tv, d_tv, p);
886 
888 }
889 
890 TmEcode DecodeWinDivertThreadInit(ThreadVars *tv, const void *initdata,
891  void **data)
892 {
893  SCEnter();
894 
896  if (d_tv == NULL) {
898  }
899 
901 
902  *data = d_tv;
903 
905 }
906 
907 TmEcode DecodeWinDivertThreadDeinit(ThreadVars *tv, void *data)
908 {
909  SCEnter();
910 
911  if (data != NULL) {
912  DecodeThreadVarsFree(tv, data);
913  }
914 
916 }
917 
918 /**
919  * \brief helper function for use with ThreadDeinit functions
920  */
921 static TmEcode WinDivertCloseHelper(WinDivertThreadVars *wd_tv)
922 {
923  SCEnter();
924  TmEcode ret = TM_ECODE_OK;
925 
926  WinDivertQueueVars *wd_qv = WinDivertGetQueue(wd_tv->thread_num);
927  if (wd_qv == NULL) {
928  SCLogDebug("No queue could be found for thread num %" PRId32 "",
929  wd_tv->thread_num);
931  }
932 
933  SCMutexLock(&wd_qv->filter_init_mutex);
934 
935  /* check if there's nothing to close */
936  if (wd_qv->filter_handle == INVALID_HANDLE_VALUE ||
937  wd_qv->filter_handle == NULL) {
938  goto unlock;
939  }
940 
941  if (!WinDivertClose(wd_qv->filter_handle)) {
942  SCLogError(SC_ERR_FATAL, "WinDivertClose failed: error %" PRIu32 "",
943  (uint32_t)(GetLastError()));
944  ret = TM_ECODE_FAILED;
945  goto unlock;
946  }
947 
948  (void)WinDivertRestoreOffloading(wd_tv);
949 
950  wd_qv->filter_handle = NULL;
951 
952 unlock:
953  SCMutexUnlock(&wd_qv->filter_init_mutex);
954 
955  if (ret == TM_ECODE_OK) {
956  SCMutexDestroy(&wd_qv->filter_init_mutex);
957  SCMutexDestroy(&wd_qv->counters_mutex);
958  }
959 
960  SCReturnInt(ret);
961 }
962 
963 #ifdef UNITTESTS
964 static int SourceWinDivertTestIfaceMatchFilter(void)
965 {
966  struct testdata {
967  const char *filter;
968  int if_index;
969  bool expected;
970  };
971 
972  struct testdata tests[] = {
973  {"true", 11, true},
974  {"ifIdx=11", 11, true},
975  {"ifIdx==11", 11, true},
976  {"ifIdx!=11", 1, true},
977  {"ifIdx!=11", 11, false},
978  {"ifIdx=3", 4, false},
979  {"ifIdx=11 || ifIdx=5", 5, true},
980  {"ifIdx=11 || ifIdx=4", 5, false},
981  {"ifIdx<3 || ifIdx>7", 8, true},
982  {"ifIdx<3 || ifIdx>7", 5, false},
983  {"ifIdx>3 or ifIdx<7", 5, true},
984  {"ifIdx>3 && ifIdx<7", 5, true},
985  {"ifIdx>3 && ifIdx<7", 1, false},
986  {"(ifIdx > 3 && ifIdx < 7) or ifIdx == 11", 11, true}};
987 
988  size_t count = (sizeof(tests) / sizeof(tests[0]));
989 
990  for (size_t i = 0; i < count; i++) {
991  struct testdata test = tests[i];
992 
993  bool actual = WinDivertIfaceMatchFilter(test.filter, test.if_index);
994  if (actual != test.expected) {
995  printf("WinDivertIfaceMatchFilter(\"%s\", %d) == %d, expected %d\n",
996  test.filter, test.if_index, actual, test.expected);
997  FAIL;
998  }
999  }
1000  PASS;
1001 }
1002 #endif
1003 
1004 /**
1005  * \brief this function registers unit tests for the WinDivert Source
1006  */
1007 void SourceWinDivertRegisterTests()
1008 {
1009 #ifdef UNITTESTS
1010  UtRegisterTest("SourceWinDivertTestIfaceMatchFilter",
1011  SourceWinDivertTestIfaceMatchFilter);
1012 #endif
1013 }
1014 
1015 #endif /* WINDIVERT */
PacketCheckAction
bool PacketCheckAction(const Packet *p, const uint8_t a)
Definition: packet.c:48
util-byte.h
tm-threads.h
ts
uint64_t ts
Definition: source-erf-file.c:55
LiveRegisterDevice
int LiveRegisterDevice(const char *dev)
Add a pcap device for monitoring and create structure.
Definition: util-device.c:126
IPV6_GET_RAW_VER
#define IPV6_GET_RAW_VER(ip6h)
Definition: decode-ipv6.h:62
SC_ERR_WINDIVERT_NOSUPPORT
@ SC_ERR_WINDIVERT_NOSUPPORT
Definition: util-error.h:346
ThreadVars_::name
char name[16]
Definition: threadvars.h:64
TAILQ_INIT
#define TAILQ_INIT(head)
Definition: queue.h:262
TmModuleDecodeWinDivertRegister
void TmModuleDecodeWinDivertRegister(void)
Definition: source-windivert.c:74
PKT_IS_PSEUDOPKT
#define PKT_IS_PSEUDOPKT(p)
return 1 if the packet is a pseudo packet
Definition: decode.h:1058
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
CaptureStats_
Definition: decode.h:747
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:296
TmThreadsSetFlag
void TmThreadsSetFlag(ThreadVars *tv, uint32_t flag)
Set a thread flag.
Definition: tm-threads.c:98
LiveGetOffload
int LiveGetOffload(void)
Definition: util-device.c:81
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
SC_ERR_WINDIVERT_GENERIC
@ SC_ERR_WINDIVERT_GENERIC
Definition: util-error.h:345
LiveDevice_
Definition: util-device.h:39
source-windivert-prototypes.h
THV_RUNNING
#define THV_RUNNING
Definition: threadvars.h:54
SURICATA_STOP
#define SURICATA_STOP
Definition: suricata.h:89
NoWinDivertSupportExit
TmEcode NoWinDivertSupportExit(ThreadVars *, const void *, void **)
Definition: source-windivert.c:81
SCMutexLock
#define SCMutexLock(mut)
Definition: threads-debug.h:117
TmModuleVerdictWinDivertRegister
void TmModuleVerdictWinDivertRegister(void)
Definition: source-windivert.c:68
util-privs.h
StatsSyncCountersIfSignalled
#define StatsSyncCountersIfSignalled(tv)
Definition: counters.h:140
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:147
TM_ECODE_FAILED
@ TM_ECODE_FAILED
Definition: tm-threads-common.h:85
GET_PKT_DIRECT_MAX_SIZE
#define GET_PKT_DIRECT_MAX_SIZE(p)
Definition: decode.h:221
util-unittest.h
TMM_DECODEWINDIVERT
@ TMM_DECODEWINDIVERT
Definition: tm-threads-common.h:70
TmModule_::PktAcqLoop
TmEcode(* PktAcqLoop)(ThreadVars *, void *, void *)
Definition: tm-modules.h:54
TM_ECODE_OK
@ TM_ECODE_OK
Definition: tm-threads-common.h:84
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:49
SC_ERR_WINDIVERT_INVALID_FILTER
@ SC_ERR_WINDIVERT_INVALID_FILTER
Definition: util-error.h:347
PKT_SET_SRC
#define PKT_SET_SRC(p, src_val)
Definition: decode.h:1061
DecodeRegisterPerfCounters
void DecodeRegisterPerfCounters(DecodeThreadVars *dtv, ThreadVars *tv)
Definition: decode.c:528
SET_PKT_LEN
#define SET_PKT_LEN(p, len)
Definition: decode.h:223
util-device.h
util-debug.h
PKT_SRC_WIRE
@ PKT_SRC_WIRE
Definition: decode.h:54
TAILQ_FIRST
#define TAILQ_FIRST(head)
Definition: queue.h:250
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
util-error.h
SC_ERR_WINDIVERT_TOOLONG_FILTER
@ SC_ERR_WINDIVERT_TOOLONG_FILTER
Definition: util-error.h:348
SCMutexUnlock
#define SCMutexUnlock(mut)
Definition: threads-debug.h:119
CaptureStatsSetup
void CaptureStatsSetup(ThreadVars *tv, CaptureStats *s)
Definition: decode.c:836
SCEnter
#define SCEnter(...)
Definition: util-debug.h:298
GET_PKT_DATA
#define GET_PKT_DATA(p)
Definition: decode.h:219
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:57
TmModule_::Func
TmEcode(* Func)(ThreadVars *, Packet *, void *)
Definition: tm-modules.h:52
RestoreIfaceOffloading
void RestoreIfaceOffloading(LiveDevice *dev)
Definition: util-ioctl.c:724
LiveDevice_::dev
char * dev
Definition: util-device.h:40
SC_ERR_INVALID_ARGUMENT
@ SC_ERR_INVALID_ARGUMENT
Definition: util-error.h:43
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:289
SC_ERR_SYSCALL
@ SC_ERR_SYSCALL
Definition: util-error.h:80
PacketCallocExtPkt
int PacketCallocExtPkt(Packet *p, int datalen)
Definition: decode.c:224
PacketPoolWait
void PacketPoolWait(void)
Definition: tmqh-packetpool.c:69
SCReturn
#define SCReturn
Definition: util-debug.h:300
IPV6Hdr_
Definition: decode-ipv6.h:32
Packet_
Definition: decode.h:428
TM_FLAG_DECODE_TM
#define TM_FLAG_DECODE_TM
Definition: tm-modules.h:32
tmm_modules
TmModule tmm_modules[TMM_SIZE]
Definition: tm-modules.c:33
DecodeIPV6
int DecodeIPV6(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint16_t len)
Definition: decode-ipv6.c:564
GET_PKT_LEN
#define GET_PKT_LEN(p)
Definition: decode.h:218
IPV4_GET_RAW_VER
#define IPV4_GET_RAW_VER(ip4h)
Definition: decode-ipv4.h:95
TmSlot_
Definition: tm-threads.h:53
TmEcode
TmEcode
Definition: tm-threads-common.h:83
source-windivert.h
GetIfaceOffloading
int GetIfaceOffloading(const char *dev, int csum, int other)
output offloading status of the link
Definition: util-ioctl.c:694
IS_TUNNEL_PKT
#define IS_TUNNEL_PKT(p)
Definition: decode.h:796
queue.h
DisableIfaceOffloading
int DisableIfaceOffloading(LiveDevice *dev, int csum, int other)
Definition: util-ioctl.c:707
TmModule_::name
const char * name
Definition: tm-modules.h:44
runmodes.h
SCLogInfo
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:215
SCMutexInit
#define SCMutexInit(mut, mutattrs)
Definition: threads-debug.h:116
TM_FLAG_RECEIVE_TM
#define TM_FLAG_RECEIVE_TM
Definition: tm-modules.h:31
TmModule_
Definition: tm-modules.h:43
IPV4Hdr_
Definition: decode-ipv4.h:72
DecodeThreadVarsFree
void DecodeThreadVarsFree(ThreadVars *tv, DecodeThreadVars *dtv)
Definition: decode.c:708
Packet_::ts
struct timeval ts
Definition: decode.h:471
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:583
SCLogError
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:255
TmModule_::ThreadInit
TmEcode(* ThreadInit)(ThreadVars *, const void *, void **)
Definition: tm-modules.h:47
SCStrdup
#define SCStrdup(s)
Definition: util-mem.h:56
CaptureStatsUpdate
void CaptureStatsUpdate(ThreadVars *tv, CaptureStats *s, const Packet *p)
Definition: decode.c:823
TMM_RECEIVEWINDIVERT
@ TMM_RECEIVEWINDIVERT
Definition: tm-threads-common.h:68
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:32
TmModule_::ThreadExitPrintStats
void(* ThreadExitPrintStats)(ThreadVars *, void *)
Definition: tm-modules.h:48
SCLogConfig
struct SCLogConfig_ SCLogConfig
Holds the config state used by the logging api.
Packet_::root
struct Packet_ * root
Definition: decode.h:618
SCLogWarning
#define SCLogWarning(err_code,...)
Macro used to log WARNING messages.
Definition: util-debug.h:242
SCFree
#define SCFree(p)
Definition: util-mem.h:61
DecodeThreadVars_
Structure to hold thread specific data for all decode modules.
Definition: decode.h:665
MAX_PAYLOAD_SIZE
#define MAX_PAYLOAD_SIZE
Definition: decode.h:659
SC_ERR_FATAL
@ SC_ERR_FATAL
Definition: util-error.h:203
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:689
TAILQ_HEAD
#define TAILQ_HEAD(name, type)
Definition: queue.h:230
GET_PKT_DIRECT_DATA
#define GET_PKT_DIRECT_DATA(p)
Definition: decode.h:220
TimeGet
void TimeGet(struct timeval *tv)
Definition: util-time.c:155
SCReturnCT
#define SCReturnCT(x, type)
Definition: util-debug.h:312
suricata.h
TMM_VERDICTWINDIVERT
@ TMM_VERDICTWINDIVERT
Definition: tm-threads-common.h:69
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:302
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:208
TmModule_::flags
uint8_t flags
Definition: tm-modules.h:70
DecodeUpdatePacketCounters
void DecodeUpdatePacketCounters(ThreadVars *tv, const DecodeThreadVars *dtv, const Packet *p)
Definition: decode.c:655
suricata_ctl_flags
volatile uint8_t suricata_ctl_flags
Definition: suricata.c:163
TmModuleReceiveWinDivertRegister
void TmModuleReceiveWinDivertRegister(void)
Definition: source-windivert.c:61