suricata
main.c
Go to the documentation of this file.
1 /* Copyright (C) 2025 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 #include "suricata.h"
19 #include "detect.h"
20 #include "detect-engine.h"
21 #include "runmodes.h"
22 #include "conf.h"
23 #include "pcap.h"
24 #include "runmode-lib.h"
25 #include "tm-threads.h"
26 #include "threadvars.h"
27 #include "action-globals.h"
28 #include "packet.h"
29 #include "util-device.h"
30 
31 #include <getopt.h>
32 #include <unistd.h>
33 
34 /* Support up to 16 interfaces */
35 #define MAX_INTERFACES 16
36 
37 static int worker_id = 1;
38 
39 /* Interfaces parsed from command line, used by the runmode setup
40  * callback. */
41 static char *g_interfaces[MAX_INTERFACES];
42 static int g_interface_count = 0;
43 
44 /* ThreadVars created during runmode setup, before thread sealing. */
45 static ThreadVars *g_worker_tvs[MAX_INTERFACES];
46 
47 /**
48  * Struct to pass arguments into a worker thread.
49  */
50 struct WorkerArgs {
51  ThreadVars *tv;
52  char *interface;
53  char *device_name;
54 };
55 
56 /**
57  * Release packet callback.
58  *
59  * If there is any cleanup that needs to be done when Suricata is done
60  * with a packet, this is the place to do it.
61  *
62  * Important: If using a custom release function, you must also
63  * release or free the packet.
64  *
65  * Optionally this is where you would handle IPS like functionality
66  * such as forwarding the packet, or triggering some other mechanism
67  * to forward the packet.
68  */
69 static void ReleasePacket(Packet *p)
70 {
72  SCLogNotice("Dropping packet!");
73  }
74 
75  /* As we overode the default release function, we must release or
76  * free the packet. */
78 }
79 
80 /**
81  * Suricata worker thread in library mode.
82  * The functions should be wrapped in an API layer.
83  */
84 static void *SimpleWorker(void *arg)
85 {
86  struct WorkerArgs *args = arg;
87  ThreadVars *tv = args->tv;
88  int exit_code = EXIT_SUCCESS;
89 
90  /* Start worker. */
91  if (SCRunModeLibSpawnWorker(tv) != 0) {
92  pthread_exit((void *)(intptr_t)EXIT_FAILURE);
93  }
94 
95  /* Open live capture on interface. */
96  char errbuf[PCAP_ERRBUF_SIZE];
97  pcap_t *fp = pcap_open_live(args->interface, 65535, 1, 1000, errbuf);
98  if (fp == NULL) {
99  SCLogError("Failed to open interface: %s", errbuf);
100  exit_code = EXIT_FAILURE;
101  goto done;
102  }
103 
104  LiveDevice *device = LiveGetDevice(args->device_name);
105  assert(device != NULL);
106 
107  int datalink = pcap_datalink(fp);
108  struct pcap_pkthdr *pkthdr;
109  const u_char *packet;
110  int pcap_rc;
111 
112  while (1) {
113  /* Have we been asked to stop? */
115  goto done;
116  }
117 
118  pcap_rc = pcap_next_ex(fp, &pkthdr, &packet);
119  if (pcap_rc == 0) {
120  /* Timeout - no packet available, continue waiting */
121  continue;
122  } else if (pcap_rc == -1) {
123  /* Error occurred */
124  SCLogError("pcap_next_ex failed on %s: %s", args->interface, pcap_geterr(fp));
125  exit_code = EXIT_FAILURE;
126  goto done;
127  } else if (pcap_rc == -2) {
128  /* End of file (shouldn't happen in live capture) */
129  SCLogNotice("End of capture on %s", args->interface);
130  exit_code = EXIT_FAILURE;
131  goto done;
132  }
133 
135  if (unlikely(p == NULL)) {
136  /* Memory allocation error: backoff and continue instead of stopping */
137  usleep(1000); /* brief sleep to avoid tight spin */
138  continue;
139  }
140 
141  /* Setup the packet, these will become functions to avoid
142  * internal Packet access. */
144  SCPacketSetTime(p, SCTIME_FROM_TIMEVAL(&pkthdr->ts));
145  SCPacketSetDatalink(p, datalink);
146  SCPacketSetLiveDevice(p, device);
147  SCPacketSetReleasePacket(p, ReleasePacket);
148 
149  if (PacketSetData(p, packet, pkthdr->len) == -1) {
151  /* Bad packet or setup error; log and continue */
152  SCLogDebug("PacketSetData failed on %s", args->interface);
153  continue;
154  }
155 
156  if (TmThreadsSlotProcessPkt(tv, tv->tm_slots, p) != TM_ECODE_OK) {
158  /* Processing failure for this packet; continue capture */
159  SCLogDebug("TmThreadsSlotProcessPkt failed on %s", args->interface);
160  continue;
161  }
162 
163  LiveDevicePktsIncr(device);
164  }
165 
166 done:
167  if (fp != NULL) {
168  pcap_close(fp);
169  }
170 
171  /* Signal main loop to shutdown. */
172  EngineStop();
173 
174  /* Cleanup.
175  *
176  * Note that there is some thread synchronization between this
177  * function and SuricataShutdown such that they must be run
178  * concurrently at this time before either will exit. */
180 
181  SCLogNotice("Worker thread exiting");
182  pthread_exit((void *)(intptr_t)exit_code);
183 }
184 
185 static uint8_t RateFilterCallback(const Packet *p, const uint32_t sid, const uint32_t gid,
186  const uint32_t rev, uint8_t original_action, uint8_t new_action, void *arg)
187 {
188  /* Don't change the action. */
189  return new_action;
190 }
191 
192 /**
193  * Application runmode setup that creates ThreadVars before thread
194  * sealing. This follows the pattern used by other runmodes where
195  * threads are registered during the runmode callback.
196  */
197 static int AppRunModeSetup(void)
198 {
199  for (int i = 0; i < g_interface_count; i++) {
200  g_worker_tvs[i] = SCRunModeLibCreateThreadVars(worker_id++);
201  if (!g_worker_tvs[i]) {
202  SCLogError("Failed to create ThreadVars for interface %s", g_interfaces[i]);
203  return -1;
204  }
205  }
206 
207  return 0;
208 }
209 
210 int main(int argc, char **argv)
211 {
212  int opt;
213 
214  /* Parse command line options using getopt */
215  while ((opt = getopt(argc, argv, "i:")) != -1) {
216  switch (opt) {
217  case 'i':
218  if (g_interface_count >= MAX_INTERFACES) {
219  fprintf(stderr, "ERROR: Maximum %d interfaces supported\n", MAX_INTERFACES);
220  exit(EXIT_FAILURE);
221  }
222  g_interfaces[g_interface_count++] = optarg;
223  break;
224  default:
225  fprintf(stderr, "Usage: %s -i interface [-i interface2 ...] [suricata_options]\n",
226  argv[0]);
227  fprintf(stderr, " -i interface Network interface to capture from (can be "
228  "specified multiple times)\n");
229  exit(EXIT_FAILURE);
230  }
231  }
232 
233  if (g_interface_count == 0) {
234  fprintf(stderr, "ERROR: At least one interface (-i) is required\n");
235  fprintf(stderr, "Usage: %s -i interface [-i interface2 ...] [suricata_options]\n", argv[0]);
236  exit(EXIT_FAILURE);
237  }
238 
239  SuricataPreInit(argv[0]);
240 
241  /* Pass through the arguments after -- to Suricata. */
242  char *suricata_argv[argc - optind + 2];
243  int suricata_argc = 0;
244  suricata_argv[suricata_argc++] = argv[0];
245  while (optind < argc) {
246  suricata_argv[suricata_argc++] = argv[optind++];
247  }
248  suricata_argv[suricata_argc] = NULL;
249  optind = 1;
250 
251  /* Log the command line arguments being passed to Suricata */
252  if (suricata_argc > 1) {
253  fprintf(stderr, "Passing command line arguments to Suricata:");
254  for (int i = 1; i < suricata_argc; i++) {
255  fprintf(stderr, " %s", suricata_argv[i]);
256  }
257  fprintf(stderr, "\n");
258  }
259 
260  SCParseCommandLine(suricata_argc, suricata_argv);
261 
262  /* Set the runmode to library mode. Perhaps in the future this
263  * should be done in some library bootstrap function. */
265 
266  /* Validate/finalize the runmode. */
267  if (SCFinalizeRunMode() != TM_ECODE_OK) {
268  exit(EXIT_FAILURE);
269  }
270 
271  /* Load configuration file, could be done earlier but must be done
272  * before SuricataInit, but even then its still optional as you
273  * may be programmatically configuration Suricata. */
274  if (SCLoadYamlConfig() != TM_ECODE_OK) {
275  exit(EXIT_FAILURE);
276  }
277 
278  /* Enable default signal handlers including SIGHUP for log file rotation,
279  * and SIGUSR2 for reloading rules. This should be done with care by a
280  * library user as the application may already have signal handlers
281  * loaded. */
283 
284  /* Register our own library run mode. At this time, the ThreadVars
285  * for each capture thread need to be created in the provided
286  * callback to meet thread synchronization requirements. */
288  RUNMODE_LIB, "live", "Live capture application run mode", AppRunModeSetup, NULL);
289 
290  if (!SCConfSetFromString("runmode=live", 1)) {
291  exit(EXIT_FAILURE);
292  }
293 
294  /* Force logging to the current directory. */
295  SCConfSetFromString("default-log-dir=.", 1);
296 
297  /* Register a LiveDevice for each interface */
298  for (int i = 0; i < g_interface_count; i++) {
299  if (LiveRegisterDevice(g_interfaces[i]) < 0) {
300  FatalError("LiveRegisterDevice failed for %s", g_interfaces[i]);
301  }
302  SCLogNotice("Registered device %s", g_interfaces[i]);
303  }
304 
305  /* SuricataInit will call our AppRunModeSetup, when it returns our
306  * ThreadVars will be ready. */
307  SuricataInit();
308 
309  SCDetectEngineRegisterRateFilterCallback(RateFilterCallback, NULL);
310 
311  /* Spawn our worker threads, one for each interface. */
312  pthread_t workers[MAX_INTERFACES];
313  struct WorkerArgs worker_args[MAX_INTERFACES];
314 
315  for (int i = 0; i < g_interface_count; i++) {
316  worker_args[i].tv = g_worker_tvs[i];
317  worker_args[i].interface = g_interfaces[i];
318  worker_args[i].device_name = g_interfaces[i];
319 
320  if (pthread_create(&workers[i], NULL, SimpleWorker, &worker_args[i]) != 0) {
321  FatalError("Failed to create worker thread for interface %s", g_interfaces[i]);
322  }
323 
324  SCLogNotice("Started worker thread for interface %s", g_interfaces[i]);
325  }
326 
328 
329  /* Run the main loop, this just waits for the worker threads to
330  * call EngineStop signalling Suricata that it is done capturing
331  * from the interfaces. */
333 
334  /* Shutdown engine. */
335  SCLogNotice("Shutting down");
336 
337  /* Note that there is some thread synchronization between this
338  * function and SCTmThreadsSlotPacketLoopFinish that require them
339  * to be run concurrently at this time. */
341 
342  /* Ensure our capture workers have fully exited before teardown. */
343  int exit_status = EXIT_SUCCESS;
344  for (int i = 0; i < g_interface_count; i++) {
345  void *worker_status;
346  pthread_join(workers[i], &worker_status);
347  if ((intptr_t)worker_status != EXIT_SUCCESS) {
348  exit_status = EXIT_FAILURE;
349  }
350  }
351 
352  GlobalsDestroy();
353 
354  return exit_status;
355 }
PacketCheckAction
bool PacketCheckAction(const Packet *p, const uint8_t a)
Definition: packet.c:50
SCTmThreadsSlotPacketLoopFinish
bool SCTmThreadsSlotPacketLoopFinish(ThreadVars *tv)
Definition: tm-threads.c:275
tm-threads.h
SuricataMainLoop
void SuricataMainLoop(void)
Definition: suricata.c:2971
WorkerArgs::tv
ThreadVars * tv
Definition: main.c:40
detect-engine.h
LiveRegisterDevice
int LiveRegisterDevice(const char *dev)
Add a pcap device for monitoring and create structure.
Definition: util-device.c:132
SCEnableDefaultSignalHandlers
void SCEnableDefaultSignalHandlers(void)
Enable default signal handlers.
Definition: suricata.c:299
PacketFreeOrRelease
void PacketFreeOrRelease(Packet *p)
Return a packet to where it was allocated.
Definition: decode.c:282
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:282
SCPacketSetLiveDevice
void SCPacketSetLiveDevice(Packet *p, LiveDevice *device)
Set a packets live device.
Definition: packet.c:186
SuricataInit
void SuricataInit(void)
Definition: suricata.c:3059
action-globals.h
SuricataPostInit
void SuricataPostInit(void)
Definition: suricata.c:3163
LiveDevice_
Definition: util-device-private.h:32
SURICATA_STOP
#define SURICATA_STOP
Definition: suricata.h:94
SCParseCommandLine
TmEcode SCParseCommandLine(int argc, char **argv)
Definition: suricata.c:1389
WorkerArgs
Definition: main.c:39
SuricataShutdown
void SuricataShutdown(void)
Definition: suricata.c:3152
TmqhOutputPacketpool
void TmqhOutputPacketpool(ThreadVars *t, Packet *p)
Definition: tmqh-packetpool.c:305
RUNMODE_LIB
@ RUNMODE_LIB
Definition: runmodes.h:40
TM_ECODE_OK
@ TM_ECODE_OK
Definition: tm-threads-common.h:81
SCPacketSetSource
void SCPacketSetSource(Packet *p, enum PktSrcEnum source)
Set packet source.
Definition: packet.c:201
SCRunmodeSet
void SCRunmodeSet(SCRunMode run_mode)
Set the current run mode.
Definition: suricata.c:294
MAX_INTERFACES
#define MAX_INTERFACES
Definition: main.c:35
util-device.h
PKT_SRC_WIRE
@ PKT_SRC_WIRE
Definition: decode.h:52
main
int main(int argc, char **argv)
Definition: main.c:20
ThreadVars_::tm_slots
struct TmSlot_ * tm_slots
Definition: threadvars.h:95
LiveGetDevice
LiveDevice * LiveGetDevice(const char *name)
Get a pointer to the device at idx.
Definition: util-device.c:268
WorkerArgs::device_name
char * device_name
Definition: main.c:53
detect.h
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:58
SCTIME_FROM_TIMEVAL
#define SCTIME_FROM_TIMEVAL(tv)
Definition: util-time.h:79
SCRunModeLibSpawnWorker
int SCRunModeLibSpawnWorker(void *td)
start the "fake" worker.
Definition: runmode-lib.c:65
SCDetectEngineRegisterRateFilterCallback
void SCDetectEngineRegisterRateFilterCallback(SCDetectRateFilterFunc fn, void *arg)
Register a callback when a rate_filter has been applied to an alert.
Definition: detect-engine.c:5157
Packet_
Definition: decode.h:505
conf.h
RunModeRegisterNewRunMode
void RunModeRegisterNewRunMode(enum SCRunModes runmode, const char *name, const char *description, int(*RunModeFunc)(void), int(*RunModeIsIPSEnabled)(void))
Registers a new runmode.
Definition: runmodes.c:482
SCPacketSetTime
void SCPacketSetTime(Packet *p, SCTime_t ts)
Set the timestamp for a packet.
Definition: packet.c:196
runmodes.h
SCConfSetFromString
int SCConfSetFromString(const char *input, int final)
Set a configuration parameter from a string.
Definition: conf.c:265
packet.h
ACTION_DROP
#define ACTION_DROP
Definition: action-globals.h:30
SCPacketSetReleasePacket
void SCPacketSetReleasePacket(Packet *p, void(*ReleasePacket)(Packet *p))
Set a packet release function.
Definition: packet.c:181
FatalError
#define FatalError(...)
Definition: util-debug.h:517
EngineStop
void EngineStop(void)
make sure threads can stop the engine by calling this function. Purpose: pcap file mode needs to be a...
Definition: suricata.c:480
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:33
threadvars.h
SCLoadYamlConfig
TmEcode SCLoadYamlConfig(void)
Definition: suricata.c:1032
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:274
SCRunModeLibCreateThreadVars
ThreadVars * SCRunModeLibCreateThreadVars(int worker_id)
Create ThreadVars for use by a user provided thread.
Definition: runmode-lib.c:29
runmode-lib.h
PacketSetData
int PacketSetData(Packet *p, const uint8_t *pktdata, uint32_t pktlen)
Set data for Packet and set length when zero copy is used.
Definition: decode.c:863
suricata.h
SCPacketSetDatalink
void SCPacketSetDatalink(Packet *p, int datalink)
Set a packets data link type.
Definition: packet.c:191
SCLogNotice
#define SCLogNotice(...)
Macro used to log NOTICE messages.
Definition: util-debug.h:250
SuricataPreInit
void SuricataPreInit(const char *progname)
Definition: suricata.c:3050
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:299
SCFinalizeRunMode
int SCFinalizeRunMode(void)
Definition: suricata.c:2498
GlobalsDestroy
void GlobalsDestroy(void)
Definition: suricata.c:400
suricata_ctl_flags
volatile uint8_t suricata_ctl_flags
Definition: suricata.c:176
WorkerArgs::interface
char * interface
Definition: main.c:52
LiveDevicePktsIncr
void LiveDevicePktsIncr(LiveDevice *dev)
Definition: util-device.c:637