#include <pthread.h>
#include <sched.h>
+#include "control/control.h"
#include "control/control_mgmt.h"
+#include "main/analyzer_command.h"
#include "main/snort_config.h"
#include "main/thread_config.h"
+#include "time/periodic.h"
#include "utils/snort_pcre.h"
#include "utils/util.h"
+#define LOG_BYTE_LIMIT 4096
+
namespace BatchedLogger
{
std::atomic<bool> BatchedLogManager::running(false);
std::atomic<uint64_t> BatchQueue::overwrite_count(0);
+class BatchedLoggerPeriodicFlush : public snort::AnalyzerCommand
+{
+public:
+ BatchedLoggerPeriodicFlush(ControlConn* ctrl = nullptr)
+ : AnalyzerCommand(ctrl) {}
+
+ bool execute(Analyzer&, void**) override
+ {
+ BatchedLogManager::flush_thread_buffers();
+ return true;
+ }
+
+ const char* stringify() override { return "BATCHED_LOGGER_PERIODIC_FLUSH"; }
+};
+
void LogBuffer::append(FILE* fh, bool use_syslog, const char* msg, size_t len)
{
if (size + len >= LOG_BUFFER_THRESHOLD)
{
if (!running)
return;
+
+ stop_periodic_flush();
flush_thread_buffers();
+
running = false;
queue.push({});
void BatchedLogManager::log(FILE* fh, bool use_syslog, const char* msg, size_t len)
{
+ assert(snort::SnortConfig::get_conf()->use_log_buffered());
+
buffer.append(fh, use_syslog, msg, len);
auto now = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - buffer.last_flush_time);
#endif
}
-#if 0
void BatchedLogManager::log(FILE* fh, bool use_syslog, const char* format, va_list& ap)
{
- static char temp[1024];
+ assert(snort::SnortConfig::get_conf()->use_log_buffered());
+
+ thread_local char temp[LOG_BYTE_LIMIT];
int len = vsnprintf(temp, sizeof(temp), format, ap);
+ assert(len <= LOG_BYTE_LIMIT);
+ len = std::min(len, LOG_BYTE_LIMIT);
+
buffer.append(fh, use_syslog, temp, len);
auto now = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - buffer.last_flush_time);
#endif
}
-#endif
+
+static void s_batched_logger_flush_handler(void*)
+{
+ BatchedLogManager::flush_thread_buffers();
+ main_broadcast_command(new BatchedLoggerPeriodicFlush());
+}
+
+void BatchedLogManager::start_periodic_flush()
+{
+ Periodic::register_handler(s_batched_logger_flush_handler, nullptr, 0, 250);
+}
+
+void BatchedLogManager::stop_periodic_flush()
+{
+ Periodic::unregister_handler(s_batched_logger_flush_handler);
+}
+
void BatchedLogManager::flush_thread_buffers()
{
buffer.flush();
queue.push(std::move(batch));
}
+static bool is_packet_tracer_message(const char* data, size_t size)
+{
+ return memmem(data, size, "PktTracerDbg", 12) != nullptr;
+}
+
void BatchedLogManager::print_batch(const LogBatch& batch)
{
static bool stop_trace = false;
}
if (stop_trace)
return;
-
- if (!s_filter.filter.empty())
+
+ // Only apply filters to PacketTracer content
+ if (!s_filter.filter.empty() && is_packet_tracer_message(batch.data.data(), batch.size))
{
int rc = pcre2_match(
s_filter.re, reinterpret_cast<PCRE2_SPTR>(batch.data.data()),
}
}
#ifdef SHELL
- if (stop_trace)
+ if (stop_trace && is_packet_tracer_message(batch.data.data(), batch.size))
if (!ControlMgmt::send_command_to_socket("packet_tracer.disable()\n"))
fprintf(stderr, "Batched_logger: Failed to send command to control socket\n");
#endif
void BatchedLogManager::init()
{
+ if (running)
+ return;
+
running = true;
snort::ThreadConfig* thread_config = snort::SnortConfig::get_conf()->thread_config;
sched_param sch_params;
sch_params.sched_priority = 1;
pthread_setschedparam(writer_thread.native_handle(), SCHED_OTHER, &sch_params);
+
+ start_periodic_flush();
}
} // namespace BatchedLogger
static void init();
static void shutdown();
static void log(FILE* fh, bool use_syslog, const char* msg, size_t len);
- //static void log(FILE* fh, bool use_syslog, const char* format, va_list& ap);
+ static void log(FILE* fh, bool use_syslog, const char* format, va_list& ap);
static void flush_thread_buffers();
static void push_batch(LogBatch&& batch);
static void set_filter(const std::string& filter);
+ static void start_periodic_flush();
+ static void stop_periodic_flush();
private:
static thread_local LogBuffer buffer;
#include <cassert>
#include <cstring>
+#include "log/batched_logger.h"
#include "main/snort_config.h"
#include "main/thread.h"
#include "parser/parser.h"
static void WriteLogMessage(FILE* fh, bool prefer_fh, const char* format, va_list& ap)
{
- if ( prefer_fh or !SnortConfig::log_syslog() )
- {
- vfprintf(fh, format, ap);
- return;
- }
- char buf[STD_BUF+1];
- vsnprintf(buf, STD_BUF, format, ap);
- buf[STD_BUF] = '\0';
- syslog(LOG_DAEMON | LOG_NOTICE, "%s", buf);
+ const SnortConfig* sc = SnortConfig::get_conf();
+
+ if ( sc && sc->use_log_buffered() )
+ {
+ bool syslog_option = ( prefer_fh or !SnortConfig::log_syslog() ) ? false : true;
+ BatchedLogger::BatchedLogManager::log(fh, syslog_option, format, ap);
+ }
+ else
+ {
+ if ( prefer_fh or !SnortConfig::log_syslog() )
+ {
+ vfprintf(fh, format, ap);
+ return;
+ }
+ char buf[STD_BUF+1];
+ vsnprintf(buf, STD_BUF, format, ap);
+ buf[STD_BUF] = '\0';
+ syslog(LOG_DAEMON | LOG_NOTICE, "%s", buf);
+ }
}
// print an info message to stdout or syslog
#include "host_tracker/host_cache_module.h"
#include "js_norm/js_norm_module.h"
#include "latency/latency_module.h"
+#include "log/batched_logger.h"
#include "log/messages.h"
#include "lua/lua.h"
#include "managers/module_manager.h"
{ "obfuscate", Parameter::PT_BOOL, nullptr, "false",
"obfuscate the logged IP addresses (same as -O)" },
+ { "log_buffered", Parameter::PT_BOOL, nullptr, "false",
+ "enable buffered logging for all output" },
+
#ifdef REG_TEST
{ "wide_hex_dump", Parameter::PT_BOOL, nullptr, "true",
#else
else if ( v.is("obfuscate") )
v.update_mask(sc->output_flags, OUTPUT_FLAG__OBFUSCATE);
+
+ else if ( v.is("log_buffered") )
+ {
+ if ( v.get_bool() )
+ BatchedLogger::BatchedLogManager::init();
+ else
+ BatchedLogger::BatchedLogManager::shutdown();
+
+ v.update_mask(sc->output_flags, OUTPUT_FLAG__LOG_BUFFERED);
+ }
return true;
}
#include "catch/snort_catch.h"
#endif
-#include "log/batched_logger.h"
#include "snort_config.h"
#include "thread_config.h"
init(argc, argv);
const SnortConfig* sc = SnortConfig::get_conf();
- BatchedLogger::BatchedLogManager::init();
if ( sc->daemon_mode() )
daemonize();
OUTPUT_FLAG__WIDE_HEX = 0x00000800,
OUTPUT_FLAG__ALERT_REFS = 0x00001000,
+ OUTPUT_FLAG__LOG_BUFFERED = 0x00002000,
};
enum LoggingFlag
bool obfuscate() const
{ return output_flags & OUTPUT_FLAG__OBFUSCATE; }
+ bool use_log_buffered() const
+ { return output_flags & OUTPUT_FLAG__LOG_BUFFERED; }
+
bool output_app_data() const
{ return output_flags & OUTPUT_FLAG__APP_DATA; }
const char* drop_reason = p->active->get_drop_reason();
if (drop_reason)
PacketTracer::log("Verdict Reason: %s, %s\n", drop_reason, p->active->get_action_string() );
- if (s_pkt_trace->buff_len < max_buff_size - 1)
- s_pkt_trace->buffer[s_pkt_trace->buff_len++] = '\n';
- if (s_pkt_trace->log_fh && s_pkt_trace->log_fh != stdout)
+ if(!snort::SnortConfig::get_conf()->use_log_buffered())
+ LogMessage(s_pkt_trace->log_fh, "%s\n", s_pkt_trace->buffer);
+ else
{
- fprintf(s_pkt_trace->log_fh, "%.*s", s_pkt_trace->buff_len, s_pkt_trace->buffer);
- fflush(s_pkt_trace->log_fh);
+ if (s_pkt_trace->buff_len < max_buff_size - 1)
+ s_pkt_trace->buffer[s_pkt_trace->buff_len++] = '\n';
+ if (s_pkt_trace->log_fh && s_pkt_trace->log_fh != stdout)
+ {
+ fprintf(s_pkt_trace->log_fh, "%.*s", s_pkt_trace->buff_len, s_pkt_trace->buffer);
+ fflush(s_pkt_trace->log_fh);
+ }
+ else
+ BatchedLogger::BatchedLogManager::log(s_pkt_trace->log_fh, SnortConfig::log_syslog(),
+ s_pkt_trace->buffer, s_pkt_trace->buff_len);
}
- else
- BatchedLogger::BatchedLogManager::log(s_pkt_trace->log_fh, SnortConfig::log_syslog(),
- s_pkt_trace->buffer, s_pkt_trace->buff_len);
}
s_pkt_trace->reset(false);
constraints.set_bits |= PacketConstraints::SetBits::DST_PORT;
std::string filter_str = regexstr ? regexstr : "";
- if (!filter_str.empty()) filter_str = (stop_after_match ? "Y" : "N") + filter_str;
+ if (!filter_str.empty())
+ {
+ if (!snort::SnortConfig::get_conf()->use_log_buffered())
+ {
+ LogMessage("WARNING: Regex filtering requires log_buffered to be enabled.\n");
+ LogMessage(" Continuing with packet tracer but ignoring regex filter.\n");
+ LogMessage(" To enable regex filtering, add 'log_buffered = true' to output configuration.\n");
+
+ filter_str.clear();
+ }
+ else
+ filter_str = (stop_after_match ? "Y" : "N") + filter_str;
+ }
+
BatchedLogger::BatchedLogManager::set_filter(filter_str);
main_broadcast_command(new PacketTracerDebug(&constraints), ControlConn::query_from_lua(L));
return 0;
}
}
+void Periodic::unregister_handler(PeriodicHook hook)
+{
+ s_periodic_handlers.remove_if([hook](const PeriodicHookNode& node) {
+ return node.hook == hook;
+ });
+}
+
void Periodic::unregister_all()
{ s_periodic_handlers.clear(); }
static void register_handler(PeriodicHook, void*, uint16_t priority, uint32_t);
static void check();
+ static void unregister_handler(PeriodicHook hook);
static void unregister_all();
};