]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #4680: logger: add batched logger to improve performance
authorSteven Baigal (sbaigal) <sbaigal@cisco.com>
Thu, 24 Jul 2025 22:14:33 +0000 (22:14 +0000)
committerSteven Baigal (sbaigal) <sbaigal@cisco.com>
Thu, 24 Jul 2025 22:14:33 +0000 (22:14 +0000)
Merge in SNORT/snort3 from ~SBAIGAL/snort3:newlog to master

Squashed commit of the following:

commit 3234f22b1c8c442884e594566d8973b2df532733
Author: Steven Baigal <sbaigal@cisco.com>
Date:   Wed Jul 23 14:26:25 2025 -0400

    logger: add cpu affinity for log writer thread

commit 109903ad2a7b428e4f99a8b035dad085d8c9e785
Author: Steven Baigal <sbaigal@cisco.com>
Date:   Wed Mar 26 14:56:13 2025 -0400

    logger: add batched logger to improve packet_tracer output performace

src/control/control_mgmt.cc
src/control/control_mgmt.h
src/log/CMakeLists.txt
src/log/batched_logger.cc [new file with mode: 0644]
src/log/batched_logger.h [new file with mode: 0644]
src/log/log.cc
src/main/snort.cc
src/main/test/distill_verdict_stubs.h
src/packet_io/packet_tracer.cc
src/packet_io/packet_tracer.h
src/packet_io/packet_tracer_module.cc

index 2d0638788060c745a2a39d9781320a4602795b94..d59ae19dd34f7cab53d720017c9ab8ce6251574d 100644 (file)
 #include <sys/un.h>
 
 #include <cassert>
+#include <chrono>
 #include <unordered_map>
 
 #include "log/messages.h"
+#include "main.h"
 #include "main/shell.h"
 #include "main/snort_config.h"
 #include "main/thread.h"
@@ -53,7 +55,7 @@ static constexpr unsigned MAX_CONTROL_IDLE_TIME = 5;
 #endif
 
 static int listener = -1;
-static socklen_t sock_addr_size = 0;
+static socklen_t sock_addr_size = 0, my_sock_addr_size = 0;
 static struct sockaddr* sock_addr = nullptr;
 static struct sockaddr_in in_addr;
 static struct sockaddr_un unix_addr;
@@ -486,7 +488,7 @@ int ControlMgmt::socket_init(const SnortConfig* sc)
     // FIXIT-M want to disable time wait
     int on = 1;
     setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-
+    my_sock_addr_size = sock_addr_size;
     if (bind(listener, sock_addr, sock_addr_size) < 0)
         FatalError("Failed to bind control listener: %s\n", get_error(errno));
 
@@ -499,6 +501,82 @@ int ControlMgmt::socket_init(const SnortConfig* sc)
     return 0;
 }
 
+// Helper function to wait for the prompt with a timeout
+static bool wait_for_prompt(int fd, const std::string& prompt, int timeout_ms)
+{
+    char buffer[1024];
+    std::string response;
+    auto start_time = std::chrono::steady_clock::now();
+
+    while (true)
+    {
+        ssize_t bytes = read(fd, buffer, sizeof(buffer) - 1);
+        if (bytes < 0)
+        {
+            if (errno == EAGAIN || errno == EWOULDBLOCK)
+            {
+                // Check if the timeout has been exceeded
+                auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
+                    std::chrono::steady_clock::now() - start_time);
+                if (elapsed.count() >= timeout_ms)
+                {
+                    return false; // Timeout
+                }
+                continue; // Retry
+            }
+            else
+            {
+                ErrorMessage("Failed to read response: %s\n", get_error(errno));
+                return false;
+            }
+        }
+        else if (bytes == 0)
+        {
+            return false; // Connection closed
+        }
+
+        buffer[bytes] = '\0';
+        response += buffer;
+
+        // Check if the prompt is found in the response
+        if (response.find(prompt) != std::string::npos)
+        {
+            return true;
+        }
+    }
+}
+
+bool ControlMgmt::send_command_to_socket(const std::string& command)
+{
+    std::string prompt = get_prompt();
+    if (listener < 0)
+    {
+        ErrorMessage("Listener is not initialized.\n");
+        return false;
+    }
+
+    int fd = socket(sock_addr->sa_family, SOCK_STREAM, 0);
+    if (fd < 0)
+    {
+        ErrorMessage("Failed to create socket: %s\n", get_error(errno));
+        return false;
+    }
+
+    if (connect(fd, sock_addr, my_sock_addr_size) >= 0 && wait_for_prompt(fd, prompt, 30) &&
+        write(fd, command.c_str(), command.size()) >= 0)
+    {
+        // Wait for the prompt again to ensure the command was processed
+        if (wait_for_prompt(fd, prompt, 90))
+        {
+            close(fd);
+            return true;
+        }
+    }
+    close(fd);
+    return false;
+}
+
+
 void ControlMgmt::socket_term()
 {
     clear_controls();
index e6982379ff1349a04fbe881d81d91206ec6e1d9f..4fab5749879002bde86e8c28e1a6d6dfafbc7d16 100644 (file)
@@ -24,6 +24,8 @@
 #ifndef CONTROL_MGMT_H
 #define CONTROL_MGMT_H
 
+#include <string>
+
 class ControlConn;
 struct lua_State;
 
@@ -42,6 +44,7 @@ public:
     static void socket_term();
 
     static ControlConn* find_control(const lua_State*);
+    static bool send_command_to_socket(const std::string& command);
 
     static bool service_users();
 };
index 99de682d2c70e7c5625ff5021ac31effeb07fb81..b1e7087316213ae2e095b31cf84ecf925025b816 100644 (file)
@@ -11,6 +11,8 @@ set (LOG_INCLUDES
 
 add_library ( log OBJECT
     ${LOG_INCLUDES}
+    batched_logger.cc
+    batched_logger.h
     log.cc
     log.h
     log_errors.h
diff --git a/src/log/batched_logger.cc b/src/log/batched_logger.cc
new file mode 100644 (file)
index 0000000..661c77d
--- /dev/null
@@ -0,0 +1,351 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2025-2025 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// batched_logger.cc author Steven Baigal <sbaigal@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "batched_logger.h"
+
+#include <algorithm>
+#include <cstdlib>
+#include <memory>
+#include <pthread.h>
+#include <sched.h>
+
+#include "control/control_mgmt.h"
+#include "main/snort_config.h"
+#include "main/thread_config.h"
+#include "utils/snort_pcre.h"
+#include "utils/util.h"
+
+namespace BatchedLogger
+{
+
+thread_local LogBuffer BatchedLogManager::buffer;
+BatchQueue BatchedLogManager::queue;
+std::thread BatchedLogManager::writer_thread;
+std::atomic<bool> BatchedLogManager::running(false);
+std::atomic<uint64_t> BatchQueue::overwrite_count(0);
+
+void LogBuffer::append(FILE* fh, bool use_syslog, const char* msg, size_t len)
+{
+    if (size + len >= LOG_BUFFER_THRESHOLD)
+        flush();
+    std::memcpy(buffer + size, msg, len);
+    size += len;
+    this->fh = fh;
+    this->use_syslog = use_syslog;
+}
+
+void LogBuffer::flush()
+{
+    if (size == 0)
+        return;
+
+    LogBatch batch;
+    batch.data.assign(buffer, buffer + size);
+    batch.size = size;
+    batch.fh = fh;
+    batch.use_syslog = use_syslog;
+    batch.is_control_message = false;
+    BatchedLogManager::push_batch(std::move(batch));
+    size = 0;
+    last_flush_time = std::chrono::steady_clock::now();
+}
+
+void LogBuffer::send_control_message(const char* msg, size_t len)
+{
+    if (len > LOG_BUFFER_THRESHOLD)
+        return;
+
+    LogBatch batch;
+    batch.data.assign(msg, msg + len);
+    batch.size = len;
+    batch.fh = nullptr;
+    batch.use_syslog = false;
+    batch.is_control_message = true;
+    BatchedLogManager::push_batch(std::move(batch));
+}
+
+void BatchQueue::push(LogBatch&& batch)
+{
+    std::lock_guard<std::mutex> lock(mtx);
+    size_t next_tail = tail + 1;
+
+    if (next_tail >= LOG_QUEUE_SIZE)
+        next_tail = 0;
+    if (next_tail == head)
+    {
+        head++;
+        if (head >= LOG_QUEUE_SIZE)
+            head = 0;
+        overwrite_count++;
+    }
+
+    buffer[tail] = std::move(batch);
+    tail = next_tail;
+    cv.notify_one();
+}
+
+bool BatchQueue::pop(LogBatch& batch)
+{
+    std::unique_lock<std::mutex> lock(mtx);
+
+    if (head == tail)
+        return false;
+
+    batch = std::move(buffer[head]);
+    head++;
+    if (head >= LOG_QUEUE_SIZE)
+        head = 0;
+    return true;
+}
+
+void BatchQueue::wait()
+{
+    std::unique_lock<std::mutex> lock(mtx);
+    cv.wait(lock, [this] { return head != tail; });
+}
+
+bool BatchQueue::empty() const
+{
+    std::lock_guard<std::mutex> lock(mtx);
+    return head == tail;
+}
+
+struct FilterData
+{
+    std::string filter;
+    pcre2_code* re = nullptr;
+    pcre2_match_data* match_data = nullptr;
+    bool stop_trace = false;
+    uint64_t revision = 0;
+
+    void clear()
+    {
+        if (match_data) pcre2_match_data_free(match_data);
+        if (re) pcre2_code_free(re);
+        filter.clear();
+        match_data = nullptr;
+        re = nullptr;
+        stop_trace = false;
+        revision++;
+    }
+};
+
+
+static FilterData s_filter;
+
+static void update_filter(const std::string& pattern_s)
+{
+    s_filter.clear();
+    if (pattern_s.empty())
+        return;
+
+    if (pattern_s[0] == 'Y')
+        s_filter.stop_trace = true;
+
+    std::string pattern = pattern_s.substr(1);
+    int error_code;
+    PCRE2_SIZE error_offset;
+
+    pcre2_code* re = pcre2_compile((PCRE2_SPTR)pattern.c_str(), PCRE2_ZERO_TERMINATED,
+        PCRE2_MULTILINE, &error_code, &error_offset, nullptr);
+
+    if (!re)
+        return;
+
+    pcre2_match_data* match_data = pcre2_match_data_create_from_pattern(re, nullptr);
+    if (!match_data)
+    {
+        pcre2_code_free(re);
+        return;
+    }
+
+    s_filter.filter = std::move(pattern);
+    s_filter.re = re;
+    s_filter.match_data = match_data;
+}
+
+void BatchedLogManager::set_filter(const std::string& pattern)
+{
+    LogBuffer::send_control_message(pattern.c_str(), pattern.size());
+}
+
+void BatchedLogManager::shutdown()
+{
+    if (!running)
+        return;
+    flush_thread_buffers();
+    running = false;
+    queue.push({});
+
+    if (writer_thread.joinable())
+        writer_thread.join();
+
+    if (BatchQueue::get_overwrite_count() > 0)
+        fprintf(stderr, "BatchedLogManager Stats: Ring buffer overwrites = %lu\n",
+            static_cast<unsigned long>(BatchQueue::get_overwrite_count()));
+
+    if (!s_filter.filter.empty())
+        s_filter.clear();
+}
+
+void BatchedLogManager::log(FILE* fh, bool use_syslog, const char* msg, size_t len)
+{
+    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);
+
+    if (buffer.size >= LOG_BUFFER_THRESHOLD || elapsed >= LOG_TIME_THRESHOLD)
+        buffer.flush();
+#ifdef REG_TEST
+    flush_thread_buffers(); // Force flush for regression tests
+#endif
+}
+
+#if 0
+void BatchedLogManager::log(FILE* fh, bool use_syslog, const char* format, va_list& ap)
+{
+    static char temp[1024];
+    int len = vsnprintf(temp, sizeof(temp), format, ap);
+
+    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);
+
+    if (buffer.size >= LOG_BUFFER_THRESHOLD || elapsed >= LOG_TIME_THRESHOLD)
+        buffer.flush();
+
+#ifdef REG_TEST
+    flush_thread_buffers(); // Force flush for regression tests
+#endif
+
+}
+#endif
+void BatchedLogManager::flush_thread_buffers()
+{
+    buffer.flush();
+}
+
+void BatchedLogManager::push_batch(LogBatch&& batch)
+{
+    queue.push(std::move(batch));
+}
+
+void BatchedLogManager::print_batch(const LogBatch& batch)
+{
+    static bool stop_trace = false;
+    static uint64_t revision = 0;
+
+    // reset condition if filter was changed by user
+    if (s_filter.revision != revision)
+    {
+        revision = s_filter.revision;
+        stop_trace = false;
+    }
+    if (stop_trace)
+        return;
+
+    if (!s_filter.filter.empty())
+    {
+        int rc = pcre2_match(
+            s_filter.re, reinterpret_cast<PCRE2_SPTR>(batch.data.data()),
+            batch.size, 0, 0, s_filter.match_data, nullptr);
+
+        if (rc < 0)
+            return; // Skip printing if no match
+        stop_trace = s_filter.stop_trace;
+        revision = s_filter.revision;
+    }
+
+    if (!batch.use_syslog && batch.fh)
+    {
+        if ( snort::SnortConfig::log_quiet() and batch.fh == stdout )
+            return;
+
+        fprintf(batch.fh, "%.*s", static_cast<int>(batch.size), batch.data.data());
+        fflush(batch.fh);
+    }
+    else
+    {
+        const char* data = batch.data.data();
+        size_t len = batch.size;
+        const char* start = data;
+        const char* end = data + len;
+
+        while (start < end)
+        {
+            const char* line_end = start;
+
+            while (line_end < end && *line_end != '\n')
+                ++line_end;
+
+            size_t line_len = line_end - start;
+
+            if (line_len > 0)
+                syslog(LOG_DAEMON | LOG_NOTICE, "%.*s", static_cast<int>(line_len), start);
+
+            start = line_end + 1;
+        }
+    }
+#ifdef SHELL
+    if (stop_trace)
+        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::writer_thread_func()
+{
+    while (running || !queue.empty())
+    {
+        LogBatch batch;
+
+        if (queue.pop(batch))
+        {
+            if (batch.is_control_message)
+                update_filter(std::string(batch.data.data(), batch.size));
+            else
+                print_batch(batch);
+        }
+        else
+            queue.wait();
+    }
+}
+
+void BatchedLogManager::init()
+{
+    running = true;
+
+    snort::ThreadConfig* thread_config = snort::SnortConfig::get_conf()->thread_config;
+    thread_config->implement_named_thread_affinity("BatchedLoggerWriter");
+    writer_thread = std::thread(writer_thread_func);
+    SET_THREAD_NAME(writer_thread.native_handle(), "snort.logger");
+    thread_config->implement_thread_affinity(STHREAD_TYPE_MAIN, snort::ThreadConfig::DEFAULT_THREAD_ID);
+
+    std::atexit(BatchedLogManager::shutdown);
+
+    sched_param sch_params;
+    sch_params.sched_priority = 1;
+    pthread_setschedparam(writer_thread.native_handle(), SCHED_OTHER, &sch_params);
+}
+
+} // namespace BatchedLogger
diff --git a/src/log/batched_logger.h b/src/log/batched_logger.h
new file mode 100644 (file)
index 0000000..a68fd8a
--- /dev/null
@@ -0,0 +1,108 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2025-2025 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// batched_logger.h author Steven Baigal <sbaigal@cisco.com>
+
+#ifndef BATCHED_LOGGER_H
+#define BATCHED_LOGGER_H
+
+#include <atomic>
+#include <condition_variable>
+#include <chrono>
+#include <cstdarg>
+#include <cstdint>
+#include <cstring>
+#include <syslog.h>
+#include <stdio.h>
+#include <vector>
+#include <thread>
+
+namespace BatchedLogger
+{
+
+const size_t LOG_BUFFER_THRESHOLD = 8192;
+const size_t LOG_QUEUE_SIZE = 8192;
+const std::chrono::milliseconds LOG_TIME_THRESHOLD(10);
+
+struct LogBatch
+{
+    std::vector<char> data;
+    size_t size = 0;
+    FILE* fh = nullptr;
+    bool use_syslog = false;
+    bool is_control_message = false;
+};
+
+class LogBuffer
+{
+public:
+    char buffer[LOG_BUFFER_THRESHOLD];
+    size_t size = 0;
+    std::chrono::steady_clock::time_point last_flush_time = std::chrono::steady_clock::now();
+
+    void append(FILE* fh, bool use_syslog, const char* msg, size_t len);
+    void flush();
+    static void send_control_message(const char* msg, size_t len);
+private:
+    FILE* fh = nullptr;
+    bool use_syslog = true;
+};
+
+class BatchQueue
+{
+private:
+    std::vector<LogBatch> buffer;
+    size_t head = 0;
+    size_t tail = 0;
+    mutable std::mutex mtx;
+    std::condition_variable cv;
+    static std::atomic<uint64_t> overwrite_count;
+
+public:
+    BatchQueue() : buffer(LOG_QUEUE_SIZE) {}
+
+    void push(LogBatch&& batch);
+    bool pop(LogBatch& batch);
+    void wait();
+    bool empty() const;
+    static uint64_t get_overwrite_count() { return overwrite_count.load(); }
+};
+
+class BatchedLogManager
+{
+public:
+    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 flush_thread_buffers();
+    static void push_batch(LogBatch&& batch);
+    static void set_filter(const std::string& filter);
+
+private:
+    static thread_local LogBuffer buffer;
+    static BatchQueue queue;
+    static std::thread writer_thread;
+    static std::atomic<bool> running;
+
+    static void writer_thread_func();
+    static void print_batch(const LogBatch& batch);
+};
+
+} // namespace BatchedLogger
+
+#endif // BATCHED_LOGGER_H
index 56ef50476304c6369b61afdf20960f716ddba14b..635e738ad7d8ca40ee5a5b4bb160a2868b24a77e 100644 (file)
@@ -32,6 +32,7 @@
 #include "utils/util.h"
 #include "utils/util_cstring.h"
 
+#include "batched_logger.h"
 #include "log_text.h"
 #include "messages.h"
 
@@ -122,6 +123,7 @@ void OpenLogger()
 
 void CloseLogger()
 {
+    BatchedLogger::BatchedLogManager::shutdown();
     TextLog_Term(text_log);
 }
 
index ad15f0c005d4805db1891c7ad12b3e01d142e8e1..d092f5a738d876c212eb5bc234717a86c3fe99ce 100644 (file)
@@ -93,6 +93,7 @@
 #include "catch/snort_catch.h"
 #endif
 
+#include "log/batched_logger.h"
 #include "snort_config.h"
 #include "thread_config.h"
 
@@ -434,6 +435,7 @@ void Snort::setup(int argc, char* argv[])
 
     init(argc, argv);
     const SnortConfig* sc = SnortConfig::get_conf();
+    BatchedLogger::BatchedLogManager::init();
 
     if ( sc->daemon_mode() )
         daemonize();
@@ -472,9 +474,9 @@ void Snort::cleanup()
     if ( !SnortConfig::get_conf()->test_mode() )  // FIXIT-M ideally the check is in one place
         PrintStatistics();
 
-    CloseLogger();
     ThreadConfig::term();
     clean_exit(0);
+    CloseLogger();
 }
 
 void Snort::reload_failure_cleanup(SnortConfig* sc)
index 3bcd6ffa26e0a69d65bb7d6fb68668231a895e04..b3e736ec3b6d879d6b059d808f3630bdbe156d77 100644 (file)
@@ -130,7 +130,6 @@ eth_t* eth_open(const char*) { return nullptr; }
 eth_t* eth_close(eth_t*) { return nullptr; }
 ssize_t eth_send(eth_t*, const void*, size_t) { return -1; }
 void HostAttributesManager::initialize() { }
-
 void select_default_policy(const _daq_pkt_hdr&, const snort::SnortConfig*) { }
 void select_default_policy(const _daq_flow_stats&, const snort::SnortConfig*) { }
 
index d9a4fe907885d35b59c555189f4d1faf295a7954..acd6d2b07b87a00381cd9b95a541909246264608 100644 (file)
@@ -30,7 +30,9 @@
 #include <sstream>
 
 #include "detection/ips_context.h"
+#include "log/batched_logger.h"
 #include "log/messages.h"
+#include "main/snort_config.h"
 #include "main/thread.h"
 #include "protocols/eth.h"
 #include "protocols/icmp4.h"
@@ -157,7 +159,10 @@ void PacketTracer::dump(Packet* p)
         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() );
-        LogMessage(s_pkt_trace->log_fh, "%s\n", s_pkt_trace->buffer);
+        if (s_pkt_trace->buff_len < max_buff_size - 1)
+            s_pkt_trace->buffer[s_pkt_trace->buff_len++] = '\n';
+        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);
@@ -321,11 +326,13 @@ void PacketTracer::restart_timer()
 // -----------------------------------------------------------------------------
 
 PacketTracer::PacketTracer()
+    : pt_timer(new Stopwatch<SnortClock>),
+      buffer(new char[max_buff_size]()),
+      daq_buffer(new char[max_buff_size]()),
+      debug_session(new char[PT_DEBUG_SESSION_ID_SIZE])
 {
-    pt_timer = new Stopwatch<SnortClock>;
-    buffer = new char[max_buff_size] { };
-    daq_buffer = new char[max_buff_size] { };
-    debug_session = new char[PT_DEBUG_SESSION_ID_SIZE];
+    dbg_str.reserve(256);
+    debugstr.reserve(256);
 
     mutes.resize(global_mutes.val, false);
     open_file();
@@ -359,7 +366,6 @@ void PacketTracer::populate_buf(const char* format, va_list ap, char* buffer, ui
 void PacketTracer::log_va(const char* format, va_list ap, bool daq_log, bool msg_only)
 {
     // FIXIT-L Need to find way to add 'PktTracerDbg' string as part of format string.
-    std::string dbg_str;
     if (shell_enabled and !daq_log) // only add debug string during shell execution
     {
         dbg_str = "PktTracerDbg ";
@@ -395,7 +401,6 @@ void PacketTracer::add_ip_header_info(const Packet& p)
     if (shell_enabled)
     {
         PacketTracer::log("\n");
-
         oss << sipstr << " " << sport << " -> "
             << dipstr << " " << dport << " "
             << std::to_string(to_utype(proto))
index a589393a082268042d49ee4c92f1b2edbb8ce097..eb1422aa42e91169233bb7277cb5aa4844a35f00 100644 (file)
@@ -122,6 +122,8 @@ protected:
     void open_file();
     virtual void dump_to_daq(Packet*);
     void reset(bool);
+private:
+    std::string dbg_str;
 };
 
 struct PacketTracerSuspend
index 3385096587b29e18503f4d82fc9e4394e976529f..164fb8c5b4dadbe7905607f029cb19de71cf7c5a 100644 (file)
@@ -27,6 +27,7 @@
 #include <lua.hpp>
 
 #include "control/control.h"
+#include "log/batched_logger.h"
 #include "log/messages.h"
 #include "main/analyzer_command.h"
 #include "main/snort_config.h"
@@ -60,6 +61,8 @@ static const Parameter enable_packet_tracer_params[] =
     {"dst_ip", Parameter::PT_STRING, nullptr, nullptr, "destination IP address filter"},
     {"dst_port", Parameter::PT_INT, "0:65535", nullptr, "destination port filter"},
     {"tenants", Parameter::PT_STRING, nullptr, nullptr, "tenants filter"},
+    {"regex", Parameter::PT_STRING, nullptr, nullptr, "regex filter"},
+    {"stop_after_match", Parameter::PT_BOOL, nullptr, nullptr, "stop trace after match is found"},
     {nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr}
 };
 
@@ -110,6 +113,8 @@ static int enable(lua_State* L)
     int dport = luaL_optint(L, 5, 0);
 
     const char *tenantsstr = luaL_optstring(L, 6, nullptr);
+    const char *regexstr = luaL_optstring(L, 7, nullptr);
+    bool stop_after_match = luaL_opt(L,lua_toboolean, 8, false);
 
     SfIp sip, dip;
     sip.clear();
@@ -159,6 +164,10 @@ static int enable(lua_State* L)
         constraints.set_bits |= PacketConstraints::SetBits::SRC_PORT;
     if ( dport )
         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;
+    BatchedLogger::BatchedLogManager::set_filter(filter_str);
     main_broadcast_command(new PacketTracerDebug(&constraints), ControlConn::query_from_lua(L));
     return 0;
 }