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