]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #4683: packet_io: add trace logs when injecting packets.
authorSteve Chew (stechew) <stechew@cisco.com>
Fri, 9 May 2025 00:26:40 +0000 (00:26 +0000)
committerPriyanka Bangalore Gurudev (prbg) <prbg@cisco.com>
Fri, 9 May 2025 00:26:40 +0000 (00:26 +0000)
Merge in SNORT/snort3 from ~STECHEW/snort3:inject_trace_logs to master

Squashed commit of the following:

commit 4aee3268aab234a62231870a3ff8764b463b7948
Author: Steve Chew <stechew@cisco.com>
Date:   Wed Apr 2 00:50:04 2025 -0400

    packet_io: add trace logs when injecting packets.

src/flow/test/flow_stubs.h
src/framework/base_api.h
src/main/test/distill_verdict_stubs.h
src/network_inspectors/appid/test/appid_discovery_test.cc
src/packet_io/active.cc
src/packet_io/active.h
src/packet_io/packet_tracer.cc
src/packet_io/packet_tracer.h
src/packet_io/test/CMakeLists.txt
src/packet_io/test/active_packet_trace_stubs.h [new file with mode: 0644]
src/packet_io/test/active_packet_trace_test.cc [new file with mode: 0644]

index f873d836974324acd5627936d73e5f7beb5cb857..d929409c3beee2cdccad84034c423e2e77e7b89d 100644 (file)
@@ -57,6 +57,7 @@ void PacketTracer::dump_to_daq(Packet*) { }
 void PacketTracer::reset(bool) { }
 void PacketTracer::pause() { }
 void PacketTracer::unpause() { }
+bool PacketTracer::is_active() { return false; }
 
 namespace layer
 {
index 4306687da2920273a6ac46eef99261a0c5498439..c2d0b94c8151ac866dcda1bb4f73a61bf5c299e4 100644 (file)
@@ -38,7 +38,7 @@
 // depends on includes installed in framework/snort_api.h
 // see framework/plugins.h
 
-#define BASE_API_VERSION 21
+#define BASE_API_VERSION 22
 
 // set the reserved field to this to be future proof
 #define API_RESERVED 0
index 23fa6d3492e527b528153335c98f2bfbf2449436..81a1a4469ad3ab5d4b6e197178d12f4403d61fc2 100644 (file)
@@ -191,6 +191,8 @@ void PacketTracer::log(const char*, ...) { }
 void PacketTracer::dump(Packet*) { }
 void PacketTracer::daq_dump(Packet*) { }
 void PacketTracer::activate(const Packet&) { }
+bool PacketTracer::is_active() { return false; }
+bool PacketTracer::is_daq_activated() { return false; }
 void TraceApi::thread_init(const TraceConfig*) { }
 void TraceApi::thread_term() { }
 void TraceApi::thread_reinit(const TraceConfig*) { }
index 9ec2591a3658f0ca2deb6bfae872b58113cf3da3..7ef0115119cd99c7593c7f8c39edf5c5aea5bc68 100644 (file)
@@ -55,6 +55,7 @@ const char* AppIdApi::get_application_name(AppId, OdpContext&) { return NULL; }
 void PacketTracer::daq_log(const char*, ...) { }
 uint64_t PacketTracer::get_time() { return 0; }
 THREAD_LOCAL PacketTracer* PacketTracer::s_pkt_trace = nullptr;
+bool PacketTracer::is_daq_activated() { return false; }
 
 // Stubs for packet
 Packet::Packet(bool) {}
index 5756b229cfb84b9a9cbbfa97808590a36585dd07..66c9b5eef7dc864069537ddd82402b6a57ce18ff 100644 (file)
@@ -30,6 +30,7 @@
 #include "main/snort_config.h"
 #include "managers/action_manager.h"
 #include "profiler/profiler.h"
+#include "packet_io/packet_tracer.h"
 #include "protocols/tcp.h"
 #include "pub_sub/active_events.h"
 #include "stream/stream.h"
@@ -217,6 +218,49 @@ void Active::thread_term()
 }
 
 //--------------------------------------------------------------------
+static void add_trace_log(EncodeFlags ef, bool direct_inject, bool success,
+    const char* prefix, const char* pkt_type, uint32_t payload_len)
+{
+    if ( not prefix or not pkt_type)
+        return;
+
+    bool forward_direction = ef & ENC_FLAG_FWD;
+    const char *direction = "reverse";
+    const char *inject_type = "injection";
+    const char *status = "failed";
+    if ( forward_direction )
+        direction = "forward";
+
+    if ( success )
+        status = "successful";
+
+    if ( direct_inject )
+        inject_type = "direct injection";
+
+    if ( payload_len )
+        PacketTracer::log("%s: %s %s of %s (length %u) in %s direction\n", prefix, status, inject_type, pkt_type, payload_len, direction);
+    else
+        PacketTracer::log("%s: %s %s of %s in %s direction\n", prefix, status, inject_type, pkt_type, direction);
+}
+
+void Active::add_reset_trace_log(EncodeFlags ef, bool direct_inject, bool success, const char* prefix)
+{
+    add_trace_log(ef, direct_inject, success, prefix, "RST packet", 0);
+}
+void Active::add_fin_trace_log(EncodeFlags ef, bool direct_inject, bool success, const char* prefix)
+{
+    add_trace_log(ef, direct_inject, success, prefix, "FIN packet", 0);
+}
+
+void Active::add_unreach_trace_log(EncodeFlags ef, bool direct_inject, bool success, const char* prefix)
+{
+    add_trace_log(ef, direct_inject, success, prefix, "packet unreachable", 0);
+}
+
+void Active::add_payload_trace_log(EncodeFlags ef, bool direct_inject, bool success, const char* prefix, uint32_t payload_len)
+{
+    add_trace_log(ef, direct_inject, success, prefix, "payload packet", payload_len);
+}
 
 void Active::send_reset(Packet* p, EncodeFlags ef)
 {
@@ -236,10 +280,14 @@ void Active::send_reset(Packet* p, EncodeFlags ef)
             if ( ret != DAQ_SUCCESS )
             {
                 active_counts.failed_direct_injects++;
+                if (PacketTracer::is_active())
+                    add_reset_trace_log(ef, true, false, "send_reset");
                 return;
             }
 
             active_counts.direct_injects++;
+            if (PacketTracer::is_active())
+                add_reset_trace_log(ef, true, true, "send_reset");
         }
         else
         {
@@ -252,14 +300,24 @@ void Active::send_reset(Packet* p, EncodeFlags ef)
             if ( !rej )
             {
                 active_counts.failed_injects++;
+                if (PacketTracer::is_active())
+                    add_reset_trace_log(ef, false, false, "send_reset failed to encode");
                 return;
             }
 
             int ret = s_send(p->daq_msg, !(ef & ENC_FLAG_FWD), rej, len);
             if ( ret )
+            {
                 active_counts.failed_injects++;
+                if (PacketTracer::is_active())
+                    add_reset_trace_log(ef, false, false, "send_reset");
+            }
             else
+            {
                 active_counts.injects++;
+                if (PacketTracer::is_active())
+                    add_reset_trace_log(ef, false, true, "send_reset");
+            }
         }
     }
 }
@@ -277,14 +335,24 @@ void Active::send_unreach(Packet* p, UnreachResponse type)
     if ( !rej )
     {
         active_counts.failed_injects++;
+        if (PacketTracer::is_active())
+            add_unreach_trace_log(flags, false, false, "send_unreach failed to encode");
         return;
     }
 
     int ret = s_send(p->daq_msg, 1, rej, len);
     if ( ret )
+    {
         active_counts.failed_injects++;
+        if (PacketTracer::is_active())
+            add_unreach_trace_log(flags, false, false, "send_unreach");
+    }
     else
+    {
         active_counts.injects++;
+        if (PacketTracer::is_active())
+            add_unreach_trace_log(flags, false, true, "send_unreach");
+    }
 }
 
 uint32_t Active::send_data(
@@ -313,10 +381,14 @@ uint32_t Active::send_data(
             if ( ret != DAQ_SUCCESS )
             {
                 active_counts.failed_direct_injects++;
+                if (PacketTracer::is_active())
+                    add_reset_trace_log(tmp_flags, true, false, "send_data to originator");
                 return 0;
             }
 
             active_counts.direct_injects++;
+            if (PacketTracer::is_active())
+                add_reset_trace_log(tmp_flags, true, true, "send_data to originator");
         }
         else
         {
@@ -327,12 +399,24 @@ uint32_t Active::send_data(
             {
                 ret = s_send(p->daq_msg, !(tmp_flags & ENC_FLAG_FWD), seg, plen);
                 if ( ret )
+                {
                     active_counts.failed_injects++;
+                    if (PacketTracer::is_active())
+                        add_reset_trace_log(tmp_flags, false, false, "send_data to originator");
+                }
                 else
+                {
                     active_counts.injects++;
+                    if (PacketTracer::is_active())
+                        add_reset_trace_log(tmp_flags, false, true, "send_data to originator");
+                }
             }
             else
+            {
                 active_counts.failed_injects++;
+                if (PacketTracer::is_active())
+                    add_reset_trace_log(tmp_flags, false, false, "send_data to originator failed to encode");
+            }
         }
     }
     flags |= ENC_FLAG_SEQ;
@@ -357,11 +441,15 @@ uint32_t Active::send_data(
             if ( ret != DAQ_SUCCESS )
             {
                 active_counts.failed_direct_injects++;
+                if (PacketTracer::is_active())
+                    add_payload_trace_log(flags, true, false, "send_data", blen);
                 return 0;
             }
 
             sent = blen;
             active_counts.direct_injects++;
+            if (PacketTracer::is_active())
+                add_payload_trace_log(flags, true, true, "send_data", blen);
         }
         else
         {
@@ -370,6 +458,7 @@ uint32_t Active::send_data(
             if (maxPayload)
             {
                 uint32_t toSend;
+                bool success = true;
                 do
                 {
                     plen = 0;
@@ -381,12 +470,17 @@ uint32_t Active::send_data(
                     if ( !seg )
                     {
                         active_counts.failed_injects++;
+                        if (PacketTracer::is_active())
+                            add_payload_trace_log(flags, false, false, "send_data failed to encode", blen);
                         return sent;
                     }
 
                     ret = s_send(p->daq_msg, !(flags & ENC_FLAG_FWD), seg, plen);
                     if ( ret )
+                    {
                         active_counts.failed_injects++;
+                        success = false;
+                    }
                     else
                         active_counts.injects++;
 
@@ -394,6 +488,9 @@ uint32_t Active::send_data(
                     buf += toSend;
                 }
                 while (blen -= toSend);
+
+                if (PacketTracer::is_active())
+                    add_payload_trace_log(flags, false, success, "send_data", sent);
             }
         }
     }
@@ -409,14 +506,24 @@ uint32_t Active::send_data(
         if ( !seg )
         {
             active_counts.failed_injects++;
+            if (PacketTracer::is_active())
+                add_fin_trace_log(flags, false, false, "send_data failed to encode");
             return sent;
         }
 
         ret = s_send(p->daq_msg, !(flags & ENC_FLAG_FWD), seg, plen);
         if ( ret )
+        {
             active_counts.failed_injects++;
+            if (PacketTracer::is_active())
+                add_fin_trace_log(flags, false, false, "send_data");
+        }
         else
+        {
             active_counts.injects++;
+            if (PacketTracer::is_active())
+                add_fin_trace_log(flags, false, true, "send_data");
+        }
 
         // Sending a FIN requires that we bump the seq by 1.
         sent++;
@@ -435,10 +542,14 @@ uint32_t Active::send_data(
             if ( ret != DAQ_SUCCESS )
             {
                 active_counts.failed_direct_injects++;
+                if (PacketTracer::is_active())
+                    add_reset_trace_log(flags, true, false, "send_data");
                 return sent;
             }
 
             active_counts.direct_injects++;
+            if (PacketTracer::is_active())
+                add_reset_trace_log(flags, true, true, "send_data");
         }
         else
         {
@@ -449,12 +560,24 @@ uint32_t Active::send_data(
             {
                 ret = s_send(p->daq_msg, !(flags & ENC_FLAG_FWD), seg, plen);
                 if ( ret )
+                {
                     active_counts.failed_injects++;
+                    if (PacketTracer::is_active())
+                        add_reset_trace_log(flags, false, false, "send_data");
+                }
                 else
+                {
                     active_counts.injects++;
+                    if (PacketTracer::is_active())
+                        add_reset_trace_log(flags, false, true, "send_data");
+                }
             }
             else
+            {
                 active_counts.failed_injects++;
+                if (PacketTracer::is_active())
+                    add_reset_trace_log(flags, false, false, "send_data failed to encode");
+            }
         }
     }
 
@@ -477,14 +600,24 @@ void Active::inject_data(
     if ( !seg )
     {
         active_counts.failed_injects++;
+        if (PacketTracer::is_active())
+            add_payload_trace_log(flags, false, false, "inject_data failed to encode", blen);
         return;
     }
 
     int ret = s_send(p->daq_msg, !(flags & ENC_FLAG_FWD), seg, plen);
     if ( ret )
+    {
         active_counts.failed_injects++;
+        if (PacketTracer::is_active())
+            add_payload_trace_log(flags, false, false, "inject_data", blen);
+    }
     else
+    {
         active_counts.injects++;
+        if (PacketTracer::is_active())
+            add_payload_trace_log(flags, false, true, "inject_data", blen);
+    }
 }
 
 //--------------------------------------------------------------------
index 2ea0fca16247b1561189559a043a09557e46e856..d51c1dc388a5b48a022f57acfae107a824c00732 100644 (file)
@@ -187,6 +187,11 @@ public:
     const char* get_drop_reason()
     { return drop_reason; }
 
+    static void add_reset_trace_log(EncodeFlags, bool direct_inject, bool success, const char* prefix);
+    static void add_fin_trace_log(EncodeFlags, bool direct_inject, bool success, const char* prefix);
+    static void add_unreach_trace_log(EncodeFlags, bool direct_inject, bool success, const char* prefix);
+    static void add_payload_trace_log(EncodeFlags, bool direct_inject, bool success, const char* prefix, uint32_t payload_len);
+
 private:
     static bool open(const char*);
     static void close();
index 1d3db53d151d97e4de2b95cbafbfd42886584895..d9a4fe907885d35b59c555189f4d1faf295a7954 100644 (file)
@@ -78,13 +78,11 @@ static constexpr int max_buff_size = 2048;
 // static functions
 // -----------------------------------------------------------------------------
 
-#ifdef _WIN64
 bool PacketTracer::is_active()
 { return s_pkt_trace ? s_pkt_trace->active : false; }
 
 bool PacketTracer::is_daq_activated()
 { return s_pkt_trace ? s_pkt_trace->daq_activated : false; }
-#endif
 
 static std::string stringify_tcp_options(const Packet* const pkt)
 {
index aa89208d94cd380f68ca71d3f125187050d88361..a589393a082268042d49ee4c92f1b2edbb8ce097 100644 (file)
@@ -63,16 +63,8 @@ public:
     static SO_PUBLIC void unpause();
     static SO_PUBLIC bool is_paused();
 
-#ifdef _WIN64
     static SO_PUBLIC bool is_active();
     static SO_PUBLIC bool is_daq_activated();
-#else
-    static bool is_active()
-    { return s_pkt_trace ? s_pkt_trace->active : false; }
-
-    static bool is_daq_activated()
-    { return s_pkt_trace ? s_pkt_trace->daq_activated : false; }
-#endif
 
     static SO_PUBLIC TracerMute get_mute();
 
index 777a08e06bf49b328574ceb13694b15d666a7c14..55a399a408a289aaeec8ba56f6957964f878fd13 100644 (file)
@@ -2,3 +2,7 @@ add_cpputest(sfdaq_counters_test
     SOURCES
         ../sfdaq_module.cc
 )
+
+add_cpputest(active_packet_trace_test
+    SOURCES
+)
diff --git a/src/packet_io/test/active_packet_trace_stubs.h b/src/packet_io/test/active_packet_trace_stubs.h
new file mode 100644 (file)
index 0000000..3b0c558
--- /dev/null
@@ -0,0 +1,245 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-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.
+//--------------------------------------------------------------------------
+// distill_verdict_stubs.h author Ron Dempster <rdempste@cisco.com>
+
+#include "detection/context_switcher.h"
+#include "detection/detection_buf.h"
+#include "detection/detection_engine.h"
+#include "detection/ips_context.h"
+#include "detection/tag.h"
+#include "file_api/file_service.h"
+#include "filters/detection_filter.h"
+#include "filters/rate_filter.h"
+#include "filters/sfrf.h"
+#include "filters/sfthreshold.h"
+#include "flow/flow_control.h"
+#include "flow/ha.h"
+#include "framework/data_bus.h"
+#include "latency/packet_latency.h"
+#include "latency/rule_latency.h"
+#include "log/messages.h"
+#include "managers/action_manager.h"
+#include "managers/codec_manager.h"
+#include "managers/connector_manager.h"
+#include "managers/event_manager.h"
+#include "managers/inspector_manager.h"
+#include "managers/ips_manager.h"
+#include "managers/module_manager.h"
+#include "main.h"
+#include "main/analyzer.h"
+#include "main/oops_handler.h"
+#include "main/policy.h"
+#include "main/snort_config.h"
+#include "main/swapper.h"
+#include "main/thread_config.h"
+#include "memory/memory_cap.h"
+#include "packet_io/active.h"
+#include "packet_io/packet_tracer.h"
+#include "packet_io/sfdaq.h"
+#include "packet_io/sfdaq_instance.h"
+#include "packet_io/sfdaq_module.h"
+#include "profiler/profiler_impl.h"
+#include "profiler/time_profiler_defs.h"
+#include "protocols/packet.h"
+#include "protocols/packet_manager.h"
+#include "side_channel/side_channel.h"
+#include "stream/stream.h"
+#include "target_based/host_attributes.h"
+#include "time/packet_time.h"
+#include "trace/trace_api.h"
+#include "utils/dnet_header.h"
+#include "utils/stats.h"
+
+using namespace snort;
+
+THREAD_LOCAL DAQStats daq_stats;
+THREAD_LOCAL bool RuleContext::enabled = false;
+THREAD_LOCAL bool snort::TimeProfilerStats::enabled;
+THREAD_LOCAL snort::PacketTracer* snort::PacketTracer::s_pkt_trace;
+THREAD_LOCAL class FlowControl* flow_con;
+
+void Flow::trust() { }
+void DeferredTrust::finalize(snort::Active&) { }
+SFDAQInstance* SFDAQ::get_local_instance() { return nullptr; }
+
+void Profiler::start() { }
+void Profiler::stop(uint64_t) { }
+void Profiler::consolidate_stats(snort::ProfilerType) { }
+void Swapper::apply(Analyzer&) { }
+Swapper::~Swapper() = default;
+void OopsHandler::tinit() { }
+void OopsHandler::tterm() { }
+void OopsHandler::set_current_message(DAQ_Msg_h, snort::SFDAQInstance*) { }
+uint16_t get_run_num() { return 0; }
+void set_run_num(uint16_t) { }
+void set_instance_id(unsigned) { }
+void set_thread_type(SThreadType) { }
+void ContextSwitcher::push(snort::IpsContext*) { }
+void ContextSwitcher::stop() { }
+ContextSwitcher::~ContextSwitcher() = default;
+snort::IpsContext* ContextSwitcher::get_context() const { return nullptr; }
+void ContextSwitcher::start() { }
+void InitTag() { }
+void CleanupTag() { }
+void RateFilter_Cleanup() { }
+int sfthreshold_alloc(unsigned int, unsigned int) { return -1; }
+void sfthreshold_reset() { }
+void sfthreshold_free() { }
+void EventTrace_Init() { }
+void EventTrace_Term() { }
+void detection_filter_init(DetectionFilterConfig*) { }
+void detection_filter_term() { }
+void RuleLatency::tterm() { }
+void rule_latency::set_force_enable(bool) { }
+void PacketLatency::tterm() { }
+void packet_latency::set_force_enable(bool) { }
+void ConnectorManager::thread_init() { }
+void ConnectorManager::thread_reinit() { }
+void ConnectorManager::thread_term() { }
+void SideChannelManager::thread_init() { }
+void SideChannelManager::thread_term() { }
+void CodecManager::thread_init() { }
+void CodecManager::thread_term() { }
+void EventManager::open_outputs() { }
+void EventManager::close_outputs() { }
+void EventManager::reload_outputs() { }
+void IpsManager::setup_options(const snort::SnortConfig*) { }
+void IpsManager::clear_options(const snort::SnortConfig*) { }
+void ActionManager::thread_init(const snort::SnortConfig*) { }
+void ActionManager::thread_term() { }
+void ActionManager::thread_reinit(const snort::SnortConfig*) { }
+int SFRF_Alloc(unsigned int) { return -1; }
+void packet_time_update(const struct timeval*) { }
+void main_poke(unsigned) { }
+void set_default_policy(const snort::SnortConfig*) { }
+bool snort_ignore(snort::Packet*) { return false; }
+ip_t* ip_open() { return nullptr; }
+ip_t* ip_close(ip_t*) { return nullptr; }
+ssize_t ip_send(ip_t*, const void*, size_t) { return -1; }
+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*) { }
+
+namespace snort
+{
+static struct timeval s_packet_time = { 0, 0 };
+THREAD_LOCAL PacketCount pc;
+
+void packet_gettimeofday(struct timeval* tv) { *tv = s_packet_time; }
+MemoryContext::MemoryContext(MemoryTracker&) : saved(nullptr) { }
+MemoryContext::~MemoryContext() = default;
+Packet::Packet(bool)
+{
+    memset((char*) this , 0, sizeof(*this));
+    ip_proto_next = IpProtocol::PROTO_NOT_SET;
+    packet_flags = PKT_FROM_CLIENT;
+}
+Packet::~Packet()  = default;
+IpsPolicy* get_ips_policy() { return nullptr; }
+void DataBus::publish(unsigned, unsigned, Packet*, Flow*) { }
+void DataBus::publish(unsigned, unsigned, DataEvent&, Flow*) { }
+void DataBus::publish_to_all_network_policies(unsigned int, unsigned int) { }
+SFDAQInstance::SFDAQInstance(const char*, unsigned, const SFDAQConfig*) { }
+SFDAQInstance::~SFDAQInstance() = default;
+void SFDAQInstance::reload() { }
+bool SFDAQInstance::start() { return false; }
+bool SFDAQInstance::stop() { return false; }
+const char* SFDAQInstance::get_error() { return nullptr; }
+bool SFDAQInstance::interrupt() { return false; }
+int SFDAQInstance::inject(DAQ_Msg_h, int, const uint8_t*, uint32_t) { return -1; }
+DAQ_RecvStatus SFDAQInstance::receive_messages(unsigned) { return DAQ_RSTAT_ERROR; }
+void SFDAQ::set_local_instance(SFDAQInstance*) { }
+const char* SFDAQ::verdict_to_string(DAQ_Verdict) { return nullptr; }
+bool SFDAQ::forwarding_packet(const DAQ_PktHdr_t*) { return false; }
+bool SFDAQ::can_inject() { return false; }
+bool SFDAQ::can_inject_raw() { return false; }
+bool SFDAQ::can_replace() { return false; }
+int SFDAQInstance::set_packet_verdict_reason(DAQ_Msg_h, uint8_t) { return 0; }
+DetectionEngine::DetectionEngine() { context = nullptr; }
+DetectionEngine::~DetectionEngine() = default;
+void DetectionEngine::onload() { }
+void DetectionEngine::thread_init() { }
+void DetectionEngine::thread_term() { }
+void DetectionEngine::idle() { }
+void DetectionEngine::reset() { }
+void DetectionEngine::wait_for_context() { }
+void DetectionEngine::set_file_data(const DataPointer&) { }
+void DetectionEngine::set_file_data(const DataPointer&, uint64_t, bool, bool) { }
+void DetectionEngine::clear_replacement() { }
+void DetectionEngine::disable_all(Packet*) { }
+unsigned get_instance_id() { return 0; }
+const SnortConfig* SnortConfig::get_conf() { return nullptr; }
+void SnortConfig::update_thread_reload_id() { }
+void PacketTracer::thread_init() { }
+void PacketTracer::thread_term() { }
+void PacketTracer::dump(Packet*) { }
+void PacketTracer::daq_dump(Packet*) { }
+void PacketTracer::activate(const Packet&) { }
+void TraceApi::thread_init(const TraceConfig*) { }
+void TraceApi::thread_term() { }
+void TraceApi::thread_reinit(const TraceConfig*) { }
+void PacketManager::thread_init() { }
+void PacketManager::decode(
+    Packet*, const DAQ_PktHdr_t*, const uint8_t*, uint32_t, bool, bool) { }
+void PacketManager::encode_update(Packet*) { }
+void PacketManager::thread_term() { }
+void FileService::thread_init() { }
+void FileService::thread_term() { }
+void ErrorMessage(const char*,...) { }
+void LogMessage(const char*,...) { }
+[[noreturn]] void FatalError(const char*,...) { exit(-1); }
+void ParseWarning(WarningGroup, const char*, ...) { }
+void HighAvailabilityManager::thread_init() { }
+void HighAvailabilityManager::process_receive() { }
+void HighAvailabilityManager::thread_term() { }
+void HighAvailabilityManager::thread_term_beginning() { }
+void HighAvailabilityManager::process_update(Flow*, Packet*) { }
+void InspectorManager::thread_init(const SnortConfig*) { }
+void InspectorManager::thread_term() { }
+void InspectorManager::thread_stop(const SnortConfig*) { }
+void InspectorManager::thread_reinit(const SnortConfig*) { }
+void InspectorManager::thread_stop_removed(const SnortConfig*) { }
+void ModuleManager::accumulate(const char*) { }
+void ModuleManager::accumulate_module(const char*) { }
+void Stream::handle_timeouts(bool) { }
+void Stream::purge_flows() { }
+bool Stream::set_packet_action_to_hold(Packet*) { return false; }
+void Stream::init_active_response(const Packet*, Flow*) { }
+void Stream::drop_flow(const Packet* ) { }
+void Stream::block_flow(const Packet*) { }
+IpsContext::IpsContext(unsigned) { }
+NetworkPolicy* get_network_policy() { return nullptr; }
+InspectionPolicy* get_inspection_policy() { return nullptr; }
+Flow::~Flow() = default;
+bool Flow::handle_allowlist() { return true; }
+void ThreadConfig::implement_thread_affinity(SThreadType, unsigned) { }
+void ThreadConfig::apply_thread_policy(SThreadType , unsigned ) { }
+void ThreadConfig::set_instance_tid(int) { }
+void populate_instance_maps() { }
+void invalidate_instance_maps() { }
+}
+
+bool FlowControl::move_to_allowlist(snort::Flow*) { return true; }
+void memory::MemoryCap::thread_init() { }
+void memory::MemoryCap::thread_term() { }
+void memory::MemoryCap::free_space() { }
+
diff --git a/src/packet_io/test/active_packet_trace_test.cc b/src/packet_io/test/active_packet_trace_test.cc
new file mode 100644 (file)
index 0000000..4e1f350
--- /dev/null
@@ -0,0 +1,375 @@
+//--------------------------------------------------------------------------
+// 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.
+//--------------------------------------------------------------------------
+// active_packet_trace_test.cc author Steve Chew <stechew@cisco.com>
+
+// -----------------------------------------------------------------------------
+// unit tests
+// -----------------------------------------------------------------------------
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "../active.cc"
+#include "active_packet_trace_stubs.h"
+#include "packet_io/active.h"
+
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+#include <CppUTestExt/MockSupport.h>
+
+using namespace snort;
+
+const int BUFFER_SIZE = 2048;
+char log_buffer[BUFFER_SIZE] = {};
+bool direct_inject_failed = false;
+bool daq_inject_failed = false;
+bool encode_failed = false;
+bool encode_unreach_failed = false;
+uint8_t encode_buf[BUFFER_SIZE];
+uint16_t max_payload = 0;
+
+void CHECK_STR(const char* str)
+{
+    int failed = strcmp(log_buffer, str);
+    if (failed)
+    {
+        printf("String comparison failed...\n"
+               "Expected: %s"
+               "Actual  : %s", str, log_buffer);
+    }
+    log_buffer[0] = '\0';   // Must set to zero-length string before reuse
+
+    CHECK(not failed);
+}
+
+bool snort::PacketTracer::is_active() { return true; }
+uint16_t PacketManager::encode_get_max_payload(const Packet*) { return max_payload; }
+
+// Override log to write to a buffer instead so we can check the result.
+void PacketTracer::log(const char* format, ...)
+{
+    size_t len = strlen(log_buffer);
+
+    va_list args;
+    va_start(args, format);
+    vsnprintf(&log_buffer[len], BUFFER_SIZE, format, args);
+    va_end(args);
+}
+
+int SFDAQInstance::ioctl(DAQ_IoctlCmd, void*, size_t)
+{
+    if (direct_inject_failed)
+        return -1;
+
+    return DAQ_SUCCESS;
+}
+
+int SFDAQ::inject(DAQ_Msg_h, int, const uint8_t*, uint32_t)
+{
+    if (daq_inject_failed)
+        return -1;
+
+    return DAQ_SUCCESS;
+}
+
+const uint8_t* PacketManager::encode_response(TcpResponse, EncodeFlags, const Packet*, uint32_t&,
+    const uint8_t* const, uint32_t)
+{
+    if (encode_failed)
+        return nullptr;
+
+    return encode_buf;
+}
+
+const uint8_t* PacketManager::encode_reject(UnreachResponse, EncodeFlags, const Packet*, uint32_t&)
+{
+    if (encode_unreach_failed)
+        return nullptr;
+
+    return encode_buf;
+}
+
+TEST_GROUP(active_packet_trace)
+{
+    void setup() {
+        direct_inject_failed = false;
+        daq_inject_failed = false;
+        encode_failed = false;
+        encode_unreach_failed = false;
+        max_payload = 0;
+    }
+
+    void teardown() {
+    }
+};
+
+TEST(active_packet_trace, check_send_reset)
+{
+    EncodeFlags ef = ENC_FLAG_FWD;
+    Packet pkt;
+    Flow flow;
+    Active active;
+    pkt.flow = &flow;
+    pkt.active = &active;
+    s_attempts = 1;
+
+    // Direct inject cases.
+    pkt.packet_flags = PKT_USE_DIRECT_INJECT;
+    direct_inject_failed = true;
+    pkt.active->send_reset(&pkt, ef);
+    CHECK_STR("send_reset: failed direct injection of RST packet in forward direction\n");
+
+    ef = 0;
+    pkt.active->send_reset(&pkt, ef);
+    CHECK_STR("send_reset: failed direct injection of RST packet in reverse direction\n");
+
+    direct_inject_failed = false;
+    pkt.active->send_reset(&pkt, ef);
+    CHECK_STR("send_reset: successful direct injection of RST packet in reverse direction\n");
+
+    ef = ENC_FLAG_FWD;
+    direct_inject_failed = false;
+    pkt.active->send_reset(&pkt, ef);
+    CHECK_STR("send_reset: successful direct injection of RST packet in forward direction\n");
+
+
+    // DAQ inject cases
+    pkt.packet_flags &= ~PKT_USE_DIRECT_INJECT;
+
+    daq_inject_failed = true;
+    pkt.active->send_reset(&pkt, ef);
+    CHECK_STR("send_reset: failed injection of RST packet in forward direction\n");
+
+    ef = 0;
+    pkt.active->send_reset(&pkt, ef);
+    CHECK_STR("send_reset: failed injection of RST packet in reverse direction\n");
+
+    daq_inject_failed = false;
+    pkt.active->send_reset(&pkt, ef);
+    CHECK_STR("send_reset: successful injection of RST packet in reverse direction\n");
+
+    ef = ENC_FLAG_FWD;
+    pkt.active->send_reset(&pkt, ef);
+    CHECK_STR("send_reset: successful injection of RST packet in forward direction\n");
+
+
+    // Encoding failure cases
+    encode_failed = true;
+    pkt.active->send_reset(&pkt, ef);
+    CHECK_STR("send_reset failed to encode: failed injection of RST packet in forward direction\n");
+
+    ef = 0;
+    pkt.active->send_reset(&pkt, ef);
+    CHECK_STR("send_reset failed to encode: failed injection of RST packet in reverse direction\n");
+}
+
+TEST(active_packet_trace, check_send_unreach)
+{
+    Packet pkt;
+    Flow flow;
+    Active active;
+    pkt.flow = &flow;
+    pkt.active = &active;
+    s_attempts = 1;
+
+    pkt.active->send_unreach(&pkt, snort::UnreachResponse::FWD);
+    CHECK_STR("send_unreach: successful injection of packet unreachable in reverse direction\n");
+
+    daq_inject_failed = true;
+    pkt.active->send_unreach(&pkt, snort::UnreachResponse::FWD);
+    CHECK_STR("send_unreach: failed injection of packet unreachable in reverse direction\n");
+
+    encode_unreach_failed = true;
+    pkt.active->send_unreach(&pkt, snort::UnreachResponse::FWD);
+    CHECK_STR("send_unreach failed to encode: failed injection of packet unreachable in reverse direction\n");
+}
+
+TEST(active_packet_trace, check_send_data)
+{
+    EncodeFlags ef = ENC_FLAG_FWD;
+    Packet pkt;
+    Flow flow;
+    Active active;
+    pkt.flow = &flow;
+    pkt.active = &active;
+    s_attempts = 1;
+
+    // Direct inject cases with no RSTs.
+    pkt.packet_flags = PKT_USE_DIRECT_INJECT;
+    direct_inject_failed = true;
+    pkt.active->send_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("send_data: failed direct injection of payload packet (length 2048) in forward direction\n");
+
+    ef = 0;
+    pkt.active->send_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("send_data: failed direct injection of payload packet (length 2048) in reverse direction\n");
+
+    direct_inject_failed = false;
+    pkt.active->send_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("send_data: successful direct injection of payload packet (length 2048) in reverse direction\n");
+
+    ef = ENC_FLAG_FWD;
+    direct_inject_failed = false;
+    pkt.active->send_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("send_data: successful direct injection of payload packet (length 2048) in forward direction\n");
+
+
+    // Direct inject cases with client and server RSTs
+    ef = ENC_FLAG_FWD | ENC_FLAG_RST_CLNT | ENC_FLAG_RST_SRVR;
+
+    direct_inject_failed = true;
+    pkt.active->send_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("send_data to originator: failed direct injection of RST packet in reverse direction\n");
+
+    ef = ENC_FLAG_RST_CLNT | ENC_FLAG_RST_SRVR;
+    pkt.active->send_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("send_data to originator: failed direct injection of RST packet in forward direction\n");
+
+    direct_inject_failed = false;
+    pkt.active->send_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("send_data to originator: successful direct injection of RST packet in forward direction\n"
+              "send_data: successful direct injection of payload packet (length 2048) in reverse direction\n"
+              "send_data: successful direct injection of RST packet in reverse direction\n"
+             );
+
+    ef = ENC_FLAG_FWD | ENC_FLAG_RST_CLNT | ENC_FLAG_RST_SRVR;
+    direct_inject_failed = false;
+    pkt.active->send_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("send_data to originator: successful direct injection of RST packet in reverse direction\n"
+              "send_data: successful direct injection of payload packet (length 2048) in forward direction\n"
+              "send_data: successful direct injection of RST packet in forward direction\n"
+             );
+
+
+    // DAQ inject cases with no RSTs.
+    pkt.packet_flags &= ~PKT_USE_DIRECT_INJECT;
+    ef = ENC_FLAG_FWD;
+
+    // Testing FIN sends (max_payload = 0)
+    pkt.active->send_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("send_data: successful injection of FIN packet in forward direction\n");
+
+    ef = 0;
+    pkt.active->send_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("send_data: successful injection of FIN packet in reverse direction\n");
+
+    // Testing FIN sends (max_payload = 1000)
+    max_payload = 1000;
+    pkt.active->send_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("send_data: successful injection of payload packet (length 2048) in reverse direction\n"
+              "send_data: successful injection of FIN packet in reverse direction\n"
+             );
+
+    ef = ENC_FLAG_FWD;
+    pkt.active->send_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("send_data: successful injection of payload packet (length 2048) in forward direction\n"
+              "send_data: successful injection of FIN packet in forward direction\n"
+             );
+
+    // Testing FIN encoding failure.
+    encode_failed = true;
+    max_payload = 0;
+
+    pkt.active->send_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("send_data failed to encode: failed injection of FIN packet in forward direction\n");
+
+    ef = 0;
+    pkt.active->send_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("send_data failed to encode: failed injection of FIN packet in reverse direction\n");
+
+
+    // Testing server RST encoding failure.
+    ef = ENC_FLAG_RST_SRVR;
+    encode_failed = true;
+
+    pkt.active->send_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("send_data to originator failed to encode: failed injection of RST packet in forward direction\n"
+              "send_data failed to encode: failed injection of FIN packet in reverse direction\n"
+             );
+
+    ef = 0;
+    pkt.active->send_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("send_data failed to encode: failed injection of FIN packet in reverse direction\n");
+
+    // Testing DAQ inject with client and server RSTs.
+    ef = ENC_FLAG_FWD | ENC_FLAG_RST_CLNT | ENC_FLAG_RST_SRVR;
+    encode_failed = false;
+    max_payload = 1000;
+
+    pkt.active->send_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("send_data to originator: successful injection of RST packet in reverse direction\n"
+              "send_data: successful injection of payload packet (length 2048) in forward direction\n"
+              "send_data: successful injection of FIN packet in forward direction\n"
+              "send_data: successful injection of RST packet in forward direction\n"
+             );
+
+    ef = ENC_FLAG_RST_CLNT | ENC_FLAG_RST_SRVR;
+    pkt.active->send_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("send_data to originator: successful injection of RST packet in forward direction\n"
+              "send_data: successful injection of payload packet (length 2048) in reverse direction\n"
+              "send_data: successful injection of FIN packet in reverse direction\n"
+              "send_data: successful injection of RST packet in reverse direction\n"
+             );
+
+    daq_inject_failed = true;
+    ef = ENC_FLAG_FWD | ENC_FLAG_RST_CLNT | ENC_FLAG_RST_SRVR;
+    pkt.active->send_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("send_data to originator: failed injection of RST packet in reverse direction\n"
+              "send_data: failed injection of payload packet (length 2048) in forward direction\n"
+              "send_data: failed injection of FIN packet in forward direction\n"
+              "send_data: failed injection of RST packet in forward direction\n"
+             );
+
+    daq_inject_failed = true;
+    ef = ENC_FLAG_RST_CLNT | ENC_FLAG_RST_SRVR;
+    pkt.active->send_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("send_data to originator: failed injection of RST packet in forward direction\n"
+              "send_data: failed injection of payload packet (length 2048) in reverse direction\n"
+              "send_data: failed injection of FIN packet in reverse direction\n"
+              "send_data: failed injection of RST packet in reverse direction\n"
+             );
+
+}
+
+TEST(active_packet_trace, check_inject_data)
+{
+    EncodeFlags ef = ENC_FLAG_FWD;
+    Packet pkt;
+    Flow flow;
+    Active active;
+    pkt.flow = &flow;
+    pkt.active = &active;
+    s_attempts = 1;
+
+    pkt.active->inject_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("inject_data: successful injection of payload packet (length 2048) in forward direction\n");
+
+    daq_inject_failed = true;
+    pkt.active->inject_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("inject_data: failed injection of payload packet (length 2048) in forward direction\n");
+
+    encode_failed = true;
+    pkt.active->inject_data(&pkt, ef, encode_buf, BUFFER_SIZE);
+    CHECK_STR("inject_data failed to encode: failed injection of payload packet (length 2048) in forward direction\n");
+}
+
+int main(int argc, char** argv)
+{
+    return CommandLineTestRunner::RunAllTests(argc, argv);
+}