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