1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License Version 2 as published
6 // by the Free Software Foundation. You may not use, modify or distribute
7 // this program under any other version of the GNU General Public License.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License along
15 // with this program; if not, write to the Free Software Foundation, Inc.,
16 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 //--------------------------------------------------------------------------
18 // main.cc author Russ Combs <rucombs@cisco.com>
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "main.h"
25
26 #include <thread>
27
28 #include "control/control.h"
29 #include "detection/signature.h"
30 #include "framework/module.h"
31 #include "helpers/process.h"
32 #include "helpers/ring.h"
33 #include "log/messages.h"
34 #include "lua/lua.h"
35 #include "main/analyzer.h"
36 #include "main/analyzer_command.h"
37 #include "main/reload_tracker.h"
38 #include "main/shell.h"
39 #include "main/snort.h"
40 #include "main/snort_config.h"
41 #include "main/snort_debug.h"
42 #include "main/snort_module.h"
43 #include "main/swapper.h"
44 #include "main/thread_config.h"
45 #include "managers/inspector_manager.h"
46 #include "managers/module_manager.h"
47 #include "managers/plugin_manager.h"
48 #include "packet_io/sfdaq.h"
49 #include "packet_io/sfdaq_config.h"
50 #include "packet_io/sfdaq_instance.h"
51 #include "packet_io/trough.h"
52 #include "target_based/host_attributes.h"
53 #include "time/periodic.h"
54 #include "trace/trace_api.h"
55 #include "trace/trace_config.h"
56 #include "trace/trace_logger.h"
57 #include "utils/util.h"
58 #include "utils/safec.h"
59
60 #if defined(UNIT_TEST) || defined(BENCHMARK_TEST)
61 #include "catch/unit_test.h"
62 #endif
63
64 #ifdef PIGLET
65 #include "piglet/piglet.h"
66 #endif
67
68 #ifdef SHELL
69 #include "control/control_mgmt.h"
70 #include "main/ac_shell_cmd.h"
71 #endif
72
73 //-------------------------------------------------------------------------
74
75 using namespace snort;
76
77 static bool exit_requested = false;
78 static int main_exit_code = 0;
79 static bool paused = false;
80 static bool all_pthreads_started = false;
81 static std::queue<AnalyzerCommand*> orphan_commands;
82
83 static std::mutex poke_mutex;
84 static Ring<unsigned>* pig_poke = nullptr;
85
86 const struct timespec main_sleep = { 0, 1000000 }; // 0.001 sec
87
88 static const char* prompt = "o\")~ ";
89
get_prompt()90 const char* get_prompt()
91 { return prompt; }
use_shell(const SnortConfig * sc)92 static bool use_shell(const SnortConfig* sc)
93 {
94 #ifdef SHELL
95 return ( sc->run_flags & RUN_FLAG__SHELL );
96 #else
97 UNUSED(sc);
98 return false;
99 #endif
100 }
101
102 // FIXIT-L X Replace main_poke()/main_read() usage with command objects
main_poke(unsigned id)103 void main_poke(unsigned id)
104 {
105 std::lock_guard<std::mutex> lock(poke_mutex);
106 pig_poke->put(id);
107 }
108
main_read()109 static int main_read()
110 {
111 std::lock_guard<std::mutex> lock(poke_mutex);
112 return pig_poke->get(-1);
113 }
114
115 //-------------------------------------------------------------------------
116 // pig foo
117 //-------------------------------------------------------------------------
118
119 class Pig
120 {
121 public:
122 Pig() = default;
123
set_index(unsigned index)124 void set_index(unsigned index) { idx = index; }
125
126 bool prep(const char* source);
127 void start();
128 void stop();
129
130 bool queue_command(AnalyzerCommand*, bool orphan = false);
131 void reap_commands();
132
133 Analyzer* analyzer = nullptr;
134 bool awaiting_privilege_change = false;
135 bool requires_privileged_start = true;
136
137 private:
138 void reap_command(AnalyzerCommand* ac);
139
140 // we could just let the analyzer own this pointer and delete
141 // immediately after getting the data but that creates memory
142 // count mismatches between main and packet threads. since the
143 // startup swapper has no old config to delete only 32 bytes
144 // after held.
145 Swapper* swapper = nullptr;
146
147 std::thread* athread = nullptr;
148 unsigned idx = (unsigned)-1;
149 };
150
prep(const char * source)151 bool Pig::prep(const char* source)
152 {
153 const SnortConfig* sc = SnortConfig::get_conf();
154 SFDAQInstance *instance = new SFDAQInstance(source, idx, sc->daq_config);
155
156 if (!SFDAQ::init_instance(instance, sc->bpf_filter))
157 {
158 delete instance;
159 return false;
160 }
161 requires_privileged_start = instance->can_start_unprivileged();
162 analyzer = new Analyzer(instance, idx, source, sc->pkt_cnt);
163 analyzer->set_skip_cnt(sc->pkt_skip);
164 #ifdef REG_TEST
165 analyzer->set_pause_after_cnt(sc->pkt_pause_cnt);
166 #endif
167 return true;
168 }
169
start()170 void Pig::start()
171 {
172 static uint16_t run_num = 0;
173 assert(!athread);
174 LogMessage("++ [%u] %s\n", idx, analyzer->get_source());
175
176 swapper = new Swapper(SnortConfig::get_main_conf());
177 athread = new std::thread(std::ref(*analyzer), swapper, ++run_num);
178 }
179
stop()180 void Pig::stop()
181 {
182 assert(analyzer);
183 assert(athread);
184
185 delete swapper;
186 swapper = nullptr;
187
188 athread->join();
189 delete athread;
190 athread = nullptr;
191
192 LogMessage("-- [%u] %s\n", idx, analyzer->get_source());
193
194 // Reap all analyzer commands, completed or not.
195 // FIXIT-L X Add concept of finalizing commands differently based on whether they were
196 // completed or not when we have commands that care about that.
197 while (!analyzer->completed_work_queue.empty())
198 {
199 reap_command(analyzer->completed_work_queue.front());
200 analyzer->completed_work_queue.pop();
201 }
202 while (!analyzer->pending_work_queue.empty())
203 {
204 reap_command(analyzer->pending_work_queue.front());
205 analyzer->pending_work_queue.pop();
206 }
207 delete analyzer;
208 analyzer = nullptr;
209 }
210
queue_command(AnalyzerCommand * ac,bool orphan)211 bool Pig::queue_command(AnalyzerCommand* ac, bool orphan)
212 {
213 if (!analyzer || !athread)
214 {
215 if (orphan)
216 orphan_commands.push(ac);
217 return false;
218 }
219
220 #ifdef DEBUG_MSGS
221 unsigned ac_ref_count = ac->get();
222 debug_logf(snort_trace, TRACE_MAIN, nullptr, "[%u] Queuing command %s for execution (refcount %u)\n",
223 idx, ac->stringify(), ac_ref_count);
224 #else
225 ac->get();
226 #endif
227 analyzer->execute(ac);
228 return true;
229 }
230
reap_command(AnalyzerCommand * ac)231 void Pig::reap_command(AnalyzerCommand* ac)
232 {
233 unsigned ac_ref_count = ac->put();
234 if (ac_ref_count == 0)
235 {
236 debug_logf(snort_trace, TRACE_MAIN, nullptr, "[%u] Destroying completed command %s\n",
237 idx, ac->stringify());
238 delete ac;
239 }
240 #ifdef DEBUG_MSGS
241 else
242 debug_logf(snort_trace, TRACE_MAIN, nullptr, "[%u] Reaped ongoing command %s (refcount %u)\n",
243 idx, ac->stringify(), ac_ref_count);
244 #endif
245 }
246
reap_commands()247 void Pig::reap_commands()
248 {
249 if (!analyzer)
250 return;
251 size_t commands_to_reap;
252 do
253 {
254 AnalyzerCommand* ac = nullptr;
255 analyzer->completed_work_queue_mutex.lock();
256 commands_to_reap = analyzer->completed_work_queue.size();
257 if (commands_to_reap)
258 {
259 ac = analyzer->completed_work_queue.front();
260 analyzer->completed_work_queue.pop();
261 }
262 analyzer->completed_work_queue_mutex.unlock();
263 if (ac)
264 reap_command(ac);
265 } while (commands_to_reap > 1);
266 }
267
268
269 static bool* pigs_started = nullptr;
270 static Pig* pigs = nullptr;
271 static unsigned max_pigs = 0;
272
get_lazy_pig(unsigned max)273 static Pig* get_lazy_pig(unsigned max)
274 {
275 for ( unsigned i = 0; i < max; ++i )
276 if ( !pigs[i].analyzer )
277 return pigs + i;
278
279 assert(false);
280 return nullptr;
281 }
282
283 //-------------------------------------------------------------------------
284 // main commands
285 //-------------------------------------------------------------------------
286
get_command(AnalyzerCommand * ac,ControlConn * ctrlcon)287 static AnalyzerCommand* get_command(AnalyzerCommand* ac, ControlConn* ctrlcon)
288 {
289 #ifndef SHELL
290 UNUSED(ctrlcon);
291 #else
292 if (ctrlcon)
293 return ( new ACShellCmd(ctrlcon, ac) );
294 else
295 #endif
296 return ac;
297 }
298
send_response(ControlConn * ctrlcon,const char * response)299 static void send_response(ControlConn* ctrlcon, const char* response)
300 {
301 if (ctrlcon)
302 ctrlcon->respond("%s", response);
303 else
304 LogMessage("%s", response);
305 }
306
main_broadcast_command(AnalyzerCommand * ac,ControlConn * ctrlcon)307 void snort::main_broadcast_command(AnalyzerCommand* ac, ControlConn* ctrlcon)
308 {
309 unsigned dispatched = 0;
310
311 ac = get_command(ac, ctrlcon);
312 debug_logf(snort_trace, TRACE_MAIN, nullptr, "Broadcasting %s command\n", ac->stringify());
313
314 for (unsigned idx = 0; idx < max_pigs; ++idx)
315 {
316 if (pigs[idx].queue_command(ac))
317 dispatched++;
318 }
319
320 if (!dispatched)
321 orphan_commands.push(ac);
322 }
323
324 #ifdef REG_TEST
main_unicast_command(AnalyzerCommand * ac,unsigned target,ControlConn * ctrlcon)325 void snort::main_unicast_command(AnalyzerCommand* ac, unsigned target, ControlConn* ctrlcon)
326 {
327 assert(target < max_pigs);
328 ac = get_command(ac, ctrlcon);
329 if (!pigs[target].queue_command(ac))
330 orphan_commands.push(ac);
331 }
332 #endif
333
main_dump_stats(lua_State * L)334 int main_dump_stats(lua_State* L)
335 {
336 ControlConn* ctrlcon = ControlConn::query_from_lua(L);
337 send_response(ctrlcon, "== dumping stats\n");
338 main_broadcast_command(new ACGetStats(ctrlcon), ctrlcon);
339 return 0;
340 }
341
main_reset_stats(lua_State * L)342 int main_reset_stats(lua_State* L)
343 {
344 ControlConn* ctrlcon = ControlConn::query_from_lua(L);
345 int type = luaL_optint(L, 1, 0);
346 ctrlcon->respond("== clearing stats\n");
347 main_broadcast_command(new ACResetStats(static_cast<clear_counter_type_t>(type)), ctrlcon);
348 return 0;
349 }
350
main_rotate_stats(lua_State * L)351 int main_rotate_stats(lua_State* L)
352 {
353 ControlConn* ctrlcon = ControlConn::query_from_lua(L);
354 send_response(ctrlcon, "== rotating stats\n");
355 main_broadcast_command(new ACRotate(), ctrlcon);
356 return 0;
357 }
358
main_reload_config(lua_State * L)359 int main_reload_config(lua_State* L)
360 {
361 ControlConn* ctrlcon = ControlConn::query_from_lua(L);
362 if ( !ReloadTracker::start(ctrlcon) )
363 {
364 send_response(ctrlcon, "== reload pending; retry\n");
365 return 0;
366 }
367 const char* fname = nullptr;
368 const char* plugin_path = nullptr;
369
370 if ( L )
371 {
372 Lua::ManageStack(L, 1);
373 fname = luaL_checkstring(L, 1);
374 if (lua_gettop(L) > 1)
375 {
376 plugin_path = luaL_checkstring(L, 2);
377 std::ostringstream plugin_path_msg;
378 plugin_path_msg << "-- reload plugin_path: " << plugin_path << "\n";
379 send_response(ctrlcon, plugin_path_msg.str().c_str());
380 }
381 }
382
383 send_response(ctrlcon, ".. reloading configuration\n");
384 ReloadTracker::update(ctrlcon,"start loading ...");
385 const SnortConfig* old = SnortConfig::get_conf();
386 SnortConfig* sc = Snort::get_reload_config(fname, plugin_path, old);
387
388 if ( !sc )
389 {
390 if (get_reload_errors())
391 {
392 std::string response_message = "== reload failed - restart required - ";
393 response_message += get_reload_errors_description() + "\n";
394 ReloadTracker::failed(ctrlcon, "restart required");
395 send_response(ctrlcon, response_message.c_str());
396 reset_reload_errors();
397 }
398 else
399 {
400 ReloadTracker::failed(ctrlcon, "bad config");
401 send_response(ctrlcon, "== reload failed - bad config\n");
402 }
403
404
405 HostAttributesManager::load_failure_cleanup();
406 return 0;
407 }
408
409 int32_t num_hosts = HostAttributesManager::get_num_host_entries();
410 if ( num_hosts >= 0 )
411 LogMessage( "host attribute table: %d hosts loaded\n", num_hosts);
412 else
413 LogMessage("No host attribute table loaded\n");
414
415 if ( !old or !old->trace_config or !old->trace_config->initialized )
416 {
417 LogMessage("== WARNING: Trace module was not configured during "
418 "initial startup. Ignoring the new trace configuration.\n");
419 sc->trace_config->clear();
420 }
421
422 PluginManager::reload_so_plugins_cleanup(sc);
423 sc->update_reload_id();
424 SnortConfig::set_conf(sc);
425 TraceApi::thread_reinit(sc->trace_config);
426 proc_stats.conf_reloads++;
427
428 ReloadTracker::update(ctrlcon, "start swapping configuration ...");
429 send_response(ctrlcon, ".. swapping configuration\n");
430 main_broadcast_command(new ACSwap(new Swapper(old, sc), ctrlcon), ctrlcon);
431
432 return 0;
433 }
434
main_reload_policy(lua_State * L)435 int main_reload_policy(lua_State* L)
436 {
437 ControlConn* ctrlcon = ControlConn::query_from_lua(L);
438 if ( !ReloadTracker::start(ctrlcon) )
439 {
440 send_response(ctrlcon, "== reload pending; retry\n");
441 return 0;
442 }
443 const char* fname = nullptr;
444
445 if ( L )
446 {
447 Lua::ManageStack(L, 1);
448 fname = luaL_checkstring(L, 1);
449 }
450
451 if ( fname and *fname )
452 send_response(ctrlcon, ".. reloading policy\n");
453 else
454 {
455 ReloadTracker::failed(ctrlcon, "filename required");
456 send_response(ctrlcon, "== filename required\n");
457 return 0;
458 }
459
460 SnortConfig* old = SnortConfig::get_main_conf();
461 SnortConfig* sc = Snort::get_updated_policy(old, fname, nullptr);
462
463 if ( !sc )
464 {
465 ReloadTracker::failed(ctrlcon, "failed to update policy");
466 send_response(ctrlcon, "== reload failed\n");
467 return 0;
468 }
469 sc->update_reload_id();
470 SnortConfig::set_conf(sc);
471 proc_stats.policy_reloads++;
472
473 ReloadTracker::update(ctrlcon, "start swapping configuration ...");
474 send_response(ctrlcon, ".. swapping policy\n");
475 main_broadcast_command(new ACSwap(new Swapper(old, sc), ctrlcon), ctrlcon);
476
477 return 0;
478 }
479
main_reload_module(lua_State * L)480 int main_reload_module(lua_State* L)
481 {
482 ControlConn* ctrlcon = ControlConn::query_from_lua(L);
483 if ( !ReloadTracker::start(ctrlcon) )
484 {
485 send_response(ctrlcon, "== reload pending; retry\n");
486 return 0;
487 }
488 const char* fname = nullptr;
489
490 if ( L )
491 {
492 Lua::ManageStack(L, 1);
493 fname = luaL_checkstring(L, 1);
494 }
495
496 if ( fname and *fname )
497 send_response(ctrlcon, ".. reloading module\n");
498 else
499 {
500 ReloadTracker::failed(ctrlcon, "module name required");
501 send_response(ctrlcon, "== module name required\n");
502 return 0;
503 }
504
505 SnortConfig* old = SnortConfig::get_main_conf();
506 SnortConfig* sc = Snort::get_updated_module(old, fname);
507
508 if ( !sc )
509 {
510 ReloadTracker::failed(ctrlcon, "failed to update module");
511 send_response(ctrlcon, "== reload failed\n");
512 return 0;
513 }
514 sc->update_reload_id();
515 SnortConfig::set_conf(sc);
516 proc_stats.policy_reloads++;
517
518 ReloadTracker::update(ctrlcon, "start swapping configuration ...");
519 send_response(ctrlcon, ".. swapping module\n");
520 main_broadcast_command(new ACSwap(new Swapper(old, sc), ctrlcon), ctrlcon);
521
522 return 0;
523 }
524
main_reload_daq(lua_State * L)525 int main_reload_daq(lua_State* L)
526 {
527 ControlConn* ctrlcon = ControlConn::query_from_lua(L);
528 send_response(ctrlcon, ".. reloading daq module\n");
529 main_broadcast_command(new ACDAQSwap(), ctrlcon);
530 proc_stats.daq_reloads++;
531
532 return 0;
533 }
534
main_reload_hosts(lua_State * L)535 int main_reload_hosts(lua_State* L)
536 {
537 ControlConn* ctrlcon = ControlConn::query_from_lua(L);
538 if ( !ReloadTracker::start(ctrlcon) )
539 {
540 send_response(ctrlcon, "== reload pending; retry\n");
541 return 0;
542 }
543
544 SnortConfig* sc = SnortConfig::get_main_conf();
545 const char* fname;
546
547 if ( L )
548 {
549 Lua::ManageStack(L, 1);
550 fname = luaL_optstring(L, 1, sc->attribute_hosts_file.c_str());
551 }
552 else
553 fname = sc->attribute_hosts_file.c_str();
554
555 if ( fname and *fname )
556 {
557 std::string msg = "Reloading Host attribute table from ";
558 msg += fname;
559 ReloadTracker::update(ctrlcon, msg.c_str());
560 send_response(ctrlcon, ".. reloading hosts table\n");
561 }
562 else
563 {
564 ReloadTracker::failed(ctrlcon, "host attribute table filename required.");
565 send_response(ctrlcon, "== filename required\n");
566 return 0;
567 }
568
569 if ( !HostAttributesManager::load_hosts_file(sc, fname) )
570 {
571 ReloadTracker::failed(ctrlcon, "failed to load host table.");
572 send_response(ctrlcon, "== reload failed\n");
573 return 0;
574 }
575
576 proc_stats.attribute_table_reloads++;
577 int32_t num_hosts = HostAttributesManager::get_num_host_entries();
578 assert( num_hosts >= 0 );
579 LogMessage("Host attribute table: %d hosts loaded successfully.\n", num_hosts);
580
581 ReloadTracker::update(ctrlcon, "start swapping configuration ...");
582 send_response(ctrlcon, ".. swapping hosts table\n");
583 main_broadcast_command(new ACHostAttributesSwap(ctrlcon), ctrlcon);
584
585 return 0;
586 }
587
main_delete_inspector(lua_State * L)588 int main_delete_inspector(lua_State* L)
589 {
590 ControlConn* ctrlcon = ControlConn::query_from_lua(L);
591 if ( !ReloadTracker::start(ctrlcon) )
592 {
593 send_response(ctrlcon, "== delete pending; retry\n");
594 return 0;
595 }
596 const char* iname = nullptr;
597
598 if ( L )
599 {
600 Lua::ManageStack(L, 1);
601 iname = luaL_checkstring(L, 1);
602 }
603
604 if ( iname and *iname )
605 send_response(ctrlcon, ".. deleting inspector\n");
606 else
607 {
608 ReloadTracker::failed(ctrlcon, "inspector name required.");
609 send_response(ctrlcon, "== inspector name required\n");
610 return 0;
611 }
612
613 SnortConfig* old = SnortConfig::get_main_conf();
614 SnortConfig* sc = Snort::get_updated_policy(old, nullptr, iname);
615
616 if ( !sc )
617 {
618 ReloadTracker::failed(ctrlcon, "failed to update policy");
619 send_response(ctrlcon, "== reload failed\n");
620 return 0;
621 }
622 SnortConfig::set_conf(sc);
623 proc_stats.inspector_deletions++;
624
625 ReloadTracker::update(ctrlcon, "start swapping configuration ...");
626 send_response(ctrlcon, ".. deleted inspector\n");
627 main_broadcast_command(new ACSwap(new Swapper(old, sc), ctrlcon), ctrlcon);
628
629 return 0;
630 }
631
main_process(lua_State * L)632 int main_process(lua_State* L)
633 {
634 ControlConn* ctrlcon = ControlConn::query_from_lua(L);
635 const char* f = lua_tostring(L, 1);
636 if ( !f )
637 {
638 send_response(ctrlcon, "== pcap filename required\n");
639 return 0;
640 }
641 send_response(ctrlcon, "== queuing pcap\n");
642 Trough::add_source(Trough::SOURCE_LIST, f);
643 return 0;
644 }
645
main_pause(lua_State * L)646 int main_pause(lua_State* L)
647 {
648 ControlConn* ctrlcon = ControlConn::query_from_lua(L);
649 send_response(ctrlcon, "== pausing\n");
650 main_broadcast_command(new ACPause(), ctrlcon);
651 paused = true;
652 return 0;
653 }
654
main_resume(lua_State * L)655 int main_resume(lua_State* L)
656 {
657 ControlConn* ctrlcon = ControlConn::query_from_lua(L);
658 uint64_t pkt_num = 0;
659
660 #ifdef REG_TEST
661 int target = -1;
662 #endif
663
664 if (L)
665 {
666 const int num_of_args = lua_gettop(L);
667 if (num_of_args)
668 {
669 pkt_num = lua_tointeger(L, 1);
670 if (pkt_num < 1)
671 {
672 send_response(ctrlcon, "Invalid usage of resume(n), n should be a number > 0\n");
673 return 0;
674 }
675 #ifdef REG_TEST
676 if (num_of_args > 1)
677 {
678 target = lua_tointeger(L, 2);
679 if (target < 0 or unsigned(target) >= max_pigs)
680 {
681 send_response(ctrlcon,
682 "Invalid usage of resume(n,m), m should be a number >= 0 and less than number of threads\n");
683 return 0;
684 }
685 }
686 #endif
687 }
688 }
689 send_response(ctrlcon, "== resuming\n");
690
691 #ifdef REG_TEST
692 if (target >= 0)
693 main_unicast_command(new ACResume(pkt_num), target, ctrlcon);
694 else
695 main_broadcast_command(new ACResume(pkt_num), ctrlcon);
696 #else
697 main_broadcast_command(new ACResume(pkt_num), ctrlcon);
698 #endif
699 paused = false;
700 return 0;
701 }
702
703 #ifdef SHELL
main_detach(lua_State * L)704 int main_detach(lua_State* L)
705 {
706 ControlConn* ctrlcon = ControlConn::query_from_lua(L);
707 send_response(ctrlcon, "== detaching\n");
708 ctrlcon->shutdown();
709 return 0;
710 }
711
main_dump_plugins(lua_State *)712 int main_dump_plugins(lua_State*)
713 {
714 ModuleManager::dump_modules();
715 PluginManager::dump_plugins();
716 return 0;
717 }
718 #endif
719
main_quit(lua_State * L)720 int main_quit(lua_State* L)
721 {
722 ControlConn* ctrlcon = ControlConn::query_from_lua(L);
723 send_response(ctrlcon, "== stopping\n");
724 main_broadcast_command(new ACStop(), ctrlcon);
725 exit_requested = true;
726 return 0;
727 }
728
main_help(lua_State * L)729 int main_help(lua_State* L)
730 {
731 ControlConn* ctrlcon = ControlConn::query_from_lua(L);
732 std::list<Module*> modules = ModuleManager::get_all_modules();
733 for (const auto& m : modules)
734 {
735 const Command* cmd = m->get_commands();
736 if (!cmd)
737 continue;
738 std::string prefix;
739 if (strcmp(m->get_name(), "snort"))
740 {
741 prefix = m->get_name();
742 prefix += '.';
743 }
744 while (cmd->name)
745 {
746 std::string info = prefix;
747 info += cmd->name;
748 info += cmd->get_arg_list();
749 info += ": ";
750 info += cmd->help;
751 info += "\n";
752 send_response(ctrlcon, info.c_str());
753 ++cmd;
754 }
755 }
756 return 0;
757 }
758
759 //-------------------------------------------------------------------------
760 // housekeeping foo
761 //-------------------------------------------------------------------------
762
signal_check()763 static int signal_check()
764 {
765 PigSignal s = get_pending_signal();
766
767 if ( s == PIG_SIG_NONE or s >= PIG_SIG_MAX )
768 return 0;
769
770 LogMessage("** caught %s signal\n", get_signal_name(s));
771
772 switch ( s )
773 {
774 case PIG_SIG_QUIT:
775 case PIG_SIG_TERM:
776 main_quit();
777 break;
778
779 case PIG_SIG_INT:
780 if ( paused )
781 main_resume(nullptr);
782 else
783 main_quit();
784 break;
785
786 case PIG_SIG_RELOAD_CONFIG:
787 main_reload_config();
788 break;
789
790 case PIG_SIG_RELOAD_HOSTS:
791 main_reload_hosts();
792 break;
793
794 case PIG_SIG_DUMP_STATS:
795 main_dump_stats();
796 break;
797
798 case PIG_SIG_ROTATE_STATS:
799 main_rotate_stats();
800 break;
801 default:
802 break;
803 }
804 proc_stats.signals++;
805 return 1;
806 }
807
reap_commands()808 static void reap_commands()
809 {
810 for (unsigned idx = 0; idx < max_pigs; ++idx)
811 pigs[idx].reap_commands();
812
813 while (!orphan_commands.empty())
814 {
815 AnalyzerCommand* ac = orphan_commands.front();
816 orphan_commands.pop();
817 debug_logf(snort_trace, TRACE_MAIN, nullptr, "Destroying orphan command %s\n", ac->stringify());
818 delete ac;
819 }
820 }
821
822 // FIXIT-L return true if something was done to avoid sleeping
house_keeping()823 static bool house_keeping()
824 {
825 if (all_pthreads_started)
826 signal_check();
827
828 reap_commands();
829
830 Periodic::check();
831
832 InspectorManager::empty_trash();
833
834 return false;
835 }
836
service_check()837 static void service_check()
838 {
839 #ifdef SHELL
840 if (all_pthreads_started && ControlMgmt::service_users() )
841 return;
842 #endif
843
844 if ( house_keeping() )
845 return;
846
847 nanosleep(&main_sleep, nullptr);
848 }
849
850 //-------------------------------------------------------------------------
851 // main foo
852 //-------------------------------------------------------------------------
853
just_validate(const SnortConfig * sc)854 static bool just_validate(const SnortConfig* sc)
855 {
856 if ( sc->test_mode() )
857 return true;
858
859 if ( use_shell(sc) )
860 return false;
861
862 if ( sc->daq_config->module_configs.empty() )
863 {
864 if ( sc->read_mode() && !Trough::get_queue_size() )
865 return true;
866
867 if ( !sc->read_mode() && !SFDAQ::get_input_spec(sc->daq_config, 0) )
868 return true;
869 }
870
871 return false;
872 }
873
set_mode()874 static bool set_mode()
875 {
876 #ifdef PIGLET
877 if ( Piglet::piglet_mode() )
878 {
879 main_exit_code = Piglet::main();
880 return false;
881 }
882 #endif
883 #if defined(UNIT_TEST) || defined(BENCHMARK_TEST)
884 // FIXIT-M X we should move this out of set_mode and not do Snort bring up/teardown at all
885 if ( catch_enabled() )
886 {
887 main_exit_code = catch_test();
888 return false;
889 }
890 #endif
891
892 unsigned warnings = get_parse_warnings();
893
894 if ( unsigned k = get_parse_errors() )
895 FatalError("see prior %u errors (%u warnings)\n", k, warnings);
896
897 SnortConfig* sc = SnortConfig::get_main_conf();
898
899 if ( sc->conf_error_out() )
900 {
901 if ( warnings )
902 FatalError("see prior %u warnings\n", warnings);
903 }
904
905 if ( sc->dump_msg_map() )
906 {
907 dump_msg_map(sc);
908 return false;
909 }
910
911 if ( sc->dump_rule_deps() )
912 {
913 dump_rule_deps(sc);
914 return false;
915 }
916
917 if ( sc->dump_rule_meta() )
918 {
919 dump_rule_meta(sc);
920 return false;
921 }
922
923 if ( sc->dump_rule_state() )
924 {
925 dump_rule_state(sc);
926 return false;
927 }
928
929 if ( just_validate(sc) )
930 {
931 LogMessage("\nSnort successfully validated the configuration (with %u warnings).\n",
932 warnings);
933
934 // force test mode to exit w/o stats
935 sc->run_flags |= RUN_FLAG__TEST;
936 return false;
937 }
938
939 if ( sc->run_flags & RUN_FLAG__PAUSE )
940 {
941 LogMessage("Paused; resume to start packet processing\n");
942 paused = true;
943 }
944 else
945 LogMessage("Commencing packet processing\n");
946
947 return true;
948 }
949
handle(Pig & pig,unsigned & swine,unsigned & pending_privileges)950 static void handle(Pig& pig, unsigned& swine, unsigned& pending_privileges)
951 {
952 switch (pig.analyzer->get_state())
953 {
954 case Analyzer::State::NEW:
955 pig.start();
956 break;
957
958 case Analyzer::State::INITIALIZED:
959 if (pig.requires_privileged_start && pending_privileges &&
960 !Snort::has_dropped_privileges())
961 {
962 if (!pig.awaiting_privilege_change)
963 {
964 pig.awaiting_privilege_change = true;
965 pending_privileges--;
966 }
967 if (pending_privileges)
968 break;
969 // FIXIT-L Make this call and the one below exit more gracefully upon error
970 if (!Snort::drop_privileges())
971 FatalError("Failed to drop privileges!\n");
972
973 Snort::do_pidfile();
974 main_broadcast_command(new ACStart());
975 }
976 else
977 {
978 Snort::do_pidfile();
979 pig.queue_command(new ACStart(), true);
980 }
981 break;
982
983 case Analyzer::State::STARTED:
984 if (!pig.requires_privileged_start && pending_privileges &&
985 !Snort::has_dropped_privileges())
986 {
987 if (!pig.awaiting_privilege_change)
988 {
989 pig.awaiting_privilege_change = true;
990 pending_privileges--;
991 }
992 if (pending_privileges)
993 break;
994
995 if (!Snort::drop_privileges())
996 FatalError("Failed to drop privileges!\n");
997
998 Snort::do_pidfile();
999 main_broadcast_command(new ACRun(paused));
1000 }
1001 else
1002 {
1003 Snort::do_pidfile();
1004 pig.queue_command(new ACRun(paused), true);
1005 }
1006 break;
1007
1008 case Analyzer::State::STOPPED:
1009 pig.stop();
1010 --swine;
1011 break;
1012
1013 default:
1014 break;
1015 }
1016 }
1017
main_loop()1018 static void main_loop()
1019 {
1020 unsigned swine = 0, pending_privileges = 0;
1021
1022 if (SnortConfig::get_conf()->change_privileges())
1023 pending_privileges = max_pigs;
1024
1025 // Preemptively prep all pigs in live traffic mode
1026 if (!SnortConfig::get_conf()->read_mode())
1027 {
1028 for (unsigned i = 0; i < max_pigs; i++)
1029 {
1030 if (pigs[i].prep(SFDAQ::get_input_spec(SnortConfig::get_conf()->daq_config, i)))
1031 swine++;
1032 }
1033 }
1034
1035 // Iterate over the drove, spawn them as allowed, and handle their deaths.
1036 // FIXIT-L X - If an exit has been requested, we might want to have some mechanism
1037 // for forcing inconsiderate pigs to die in timely fashion.
1038 while ( swine or paused or (Trough::has_next() and !exit_requested) )
1039 {
1040 const char* src;
1041 int idx = main_read();
1042
1043 if ( idx >= 0 )
1044 {
1045 Pig& pig = pigs[idx];
1046
1047 if ( pig.analyzer )
1048 {
1049 handle(pig, swine, pending_privileges);
1050 if (!pigs_started[idx] && pig.analyzer && (pig.analyzer->get_state() ==
1051 Analyzer::State::STARTED))
1052 pigs_started[idx] = true;
1053 }
1054 else if ( pending_privileges )
1055 pending_privileges--;
1056
1057 if ( !swine and exit_requested )
1058 break;
1059
1060 continue;
1061 }
1062
1063 if (!all_pthreads_started)
1064 {
1065 all_pthreads_started = true;
1066 const unsigned num_threads = (!Trough::has_next()) ? swine : max_pigs;
1067 for (unsigned i = 0; i < num_threads; i++)
1068 all_pthreads_started &= pigs_started[i];
1069 if (all_pthreads_started)
1070 {
1071 #ifdef REG_TEST
1072 LogMessage("All pthreads started\n");
1073 #endif
1074 #ifdef SHELL
1075 if (use_shell(SnortConfig::get_conf()))
1076 {
1077 LogMessage("Entering command shell\n");
1078 ControlMgmt::add_control(STDOUT_FILENO, true);
1079 }
1080 #endif
1081 }
1082 }
1083
1084 if ( !exit_requested and (swine < max_pigs) and (src = Trough::get_next()) )
1085 {
1086 Pig* pig = get_lazy_pig(max_pigs);
1087 if (pig->prep(src))
1088 ++swine;
1089 continue;
1090 }
1091 service_check();
1092 }
1093 }
1094
snort_main()1095 static void snort_main()
1096 {
1097 #ifdef SHELL
1098 ControlMgmt::socket_init(SnortConfig::get_conf());
1099 #endif
1100
1101 SnortConfig::get_conf()->thread_config->implement_thread_affinity(
1102 STHREAD_TYPE_MAIN, get_instance_id());
1103
1104 max_pigs = ThreadConfig::get_instance_max();
1105 assert(max_pigs > 0);
1106
1107 // maximum number of state change notifications per pig
1108 constexpr unsigned max_grunts = static_cast<unsigned>(Analyzer::State::NUM_STATES);
1109
1110 pig_poke = new Ring<unsigned>((max_pigs*max_grunts)+1);
1111 pigs = new Pig[max_pigs];
1112 pigs_started = new bool[max_pigs];
1113
1114 for (unsigned idx = 0; idx < max_pigs; idx++)
1115 {
1116 Pig& pig = pigs[idx];
1117 pig.set_index(idx);
1118 pigs_started[idx] = false;
1119 }
1120
1121 main_loop();
1122
1123 delete pig_poke;
1124 delete[] pigs;
1125 pigs = nullptr;
1126 delete[] pigs_started;
1127 pigs_started = nullptr;
1128
1129 #ifdef SHELL
1130 ControlMgmt::socket_term();
1131 #endif
1132 }
1133
main(int argc,char * argv[])1134 int main(int argc, char* argv[])
1135 {
1136 set_mem_constraint_handler_s(log_safec_error);
1137 set_str_constraint_handler_s(log_safec_error);
1138
1139 const char* s = getenv("SNORT_PROMPT");
1140
1141 if ( s )
1142 prompt = s;
1143
1144 set_thread_type(STHREAD_TYPE_MAIN);
1145
1146 Snort::setup(argc, argv);
1147
1148 if ( set_mode() )
1149 snort_main();
1150
1151 Snort::cleanup();
1152
1153 return main_exit_code;
1154 }
1155
1156