]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #4201: actions: Add action counters and aggregate them under ips_actions.
authorVitalii Tron -X (vtron - SOFTSERVE INC at Cisco) <vtron@cisco.com>
Wed, 8 May 2024 01:34:48 +0000 (01:34 +0000)
committerSteve Chew (stechew) <stechew@cisco.com>
Wed, 8 May 2024 01:34:48 +0000 (01:34 +0000)
Merge in SNORT/snort3 from ~VTRON/snort3:action_counter_logs to master

Squashed commit of the following:

commit 0430f3a6f7250523fdb8029ed1a195a813736de5
Author: Steve Chew <stechew@cisco.com>
Date:   Wed Jan 31 13:03:00 2024 -0500

    actions: Add action counters and aggregate them under ips_actions.

17 files changed:
src/actions/CMakeLists.txt
src/actions/act_alert.cc
src/actions/act_block.cc
src/actions/act_drop.cc
src/actions/act_file_id.cc
src/actions/act_log.cc
src/actions/act_pass.cc
src/actions/act_react.cc
src/actions/act_reject.cc
src/actions/act_replace.cc
src/actions/actions_module.cc [new file with mode: 0644]
src/actions/actions_module.h [new file with mode: 0644]
src/framework/base_api.h
src/framework/module.h
src/main/modules.cc
src/managers/module_manager.cc
src/network_inspectors/perf_monitor/base_tracker.cc

index 2ec6f48c7d8c9ce4a674cf6bc38ae9f229d2f3b4..7dde1c9f4c3cc83dde620df92a783f0dfd8d4296 100644 (file)
@@ -5,6 +5,8 @@ set ( ACTIONS_INCLUDES
 
 set (IPS_ACTION_SOURCES
     actions.cc
+    actions_module.cc
+    actions_module.h
     ips_actions.cc
     ips_actions.h
     act_alert.cc
@@ -33,7 +35,7 @@ else (STATIC_IPS_ACTIONS)
         ${IPS_ACTION_SOURCES}
     )
 
-    add_dynamic_module(act_react ips_actions act_react.cc)
+    add_dynamic_module(act_react ips_actions act_react.cc actions_module.cc)
 
 endif (STATIC_IPS_ACTIONS)
 
index 72a181f8e8001c4047dcc581475e97571bff9d44..d46302d8552eec60a511be93172280e9df01fb0d 100644 (file)
@@ -21,6 +21,7 @@
 #include "config.h"
 #endif
 
+#include "actions/actions_module.h"
 #include "framework/ips_action.h"
 #include "framework/module.h"
 #include "protocols/packet.h"
 
 using namespace snort;
 
-#define s_name "alert"
-
-#define s_help \
+#define action_name "alert"
+#define action_help \
     "generate alert on the current packet"
 
+#define module_name "alert"
+#define module_help \
+    "manage the counters for the alert action"
+
+static THREAD_LOCAL struct AlertStats
+{
+    PegCount alert;
+} alert_stats;
+
+const PegInfo alert_pegs[] =
+{
+    { CountType::SUM, "alert", "number of packets that matched an IPS alert rule" },
+    { CountType::END, nullptr, nullptr }
+};
+
 //-------------------------------------------------------------------------
 class AlertAction : public IpsAction
 {
 public:
-    AlertAction() : IpsAction(s_name, nullptr) { }
+    AlertAction() : IpsAction(action_name, nullptr) { }
 
     void exec(Packet*, const OptTreeNode* otn) override;
 };
@@ -46,9 +61,35 @@ public:
 void AlertAction::exec(Packet* p, const OptTreeNode* otn)
 {
     Actions::alert(p, otn);
+    ++alert_stats.alert;
 }
 
 //-------------------------------------------------------------------------
+class AlertActionModule : public Module
+{
+public:
+    AlertActionModule() : Module(module_name, module_help)
+    { ActionsModule::add_action(module_name, alert_pegs); }
+
+    bool stats_are_aggregated() const override
+    { return true; }
+
+    void show_stats() override
+    { /* These stats are shown by ActionsModule. */ }
+
+    const PegInfo* get_pegs() const override
+    { return alert_pegs; }
+
+    PegCount* get_counts() const override
+    { return (PegCount*)&alert_stats; }
+};
+
+//-------------------------------------------------------------------------
+static Module* mod_ctor()
+{ return new AlertActionModule; }
+
+static void mod_dtor(Module* m)
+{ delete m; }
 
 static IpsAction* alert_ctor(Module*)
 { return new AlertAction; }
@@ -65,10 +106,10 @@ static ActionApi alert_api
         0,
         API_RESERVED,
         API_OPTIONS,
-        s_name,
-        s_help,
-        nullptr,  // mod_ctor
-        nullptr,  // mod_dtor
+        action_name,
+        action_help,
+        mod_ctor,
+        mod_dtor,
     },
     IpsAction::IAP_ALERT,
     nullptr,
index 244a0739b79c5d621aa16f639fa04436caa6e579..7e9e0d5df69d7c3f30449322ec028398be9804bf 100644 (file)
@@ -21,6 +21,7 @@
 #include "config.h"
 #endif
 
+#include "actions/actions_module.h"
 #include "framework/ips_action.h"
 #include "framework/module.h"
 #include "packet_io/active.h"
 
 using namespace snort;
 
-#define s_name "block"
-
-#define s_help \
+#define action_name "block"
+#define action_help \
     "block current packet and all the subsequent packets in this flow"
 
+#define module_name "block"
+#define module_help \
+    "manage the counters for the block action"
+
+static THREAD_LOCAL struct BlockStats
+{
+    PegCount block;
+} block_stats;
+
+const PegInfo block_pegs[] =
+{
+    { CountType::SUM, "block", "number of packets that matched an IPS block rule" },
+    { CountType::END, nullptr, nullptr }
+};
+
 //-------------------------------------------------------------------------
 class BlockAction : public IpsAction
 {
 public:
-    BlockAction() : IpsAction(s_name, nullptr) { }
+    BlockAction() : IpsAction(action_name, nullptr) { }
 
     void exec(Packet*, const OptTreeNode* otn) override;
     bool drops_traffic() override { return true; }
@@ -51,9 +66,35 @@ void BlockAction::exec(Packet* p, const OptTreeNode* otn)
     p->active->set_drop_reason("ips");
 
     Actions::alert(p, otn);
+    ++block_stats.block;
 }
 
 //-------------------------------------------------------------------------
+class BlockActionModule : public Module
+{
+public:
+    BlockActionModule() : Module(module_name, module_help)
+    { ActionsModule::add_action(module_name, block_pegs); }
+
+    bool stats_are_aggregated() const override
+    { return true; }
+
+    void show_stats() override
+    { /* These stats are shown by ActionsModule. */ }
+
+    const PegInfo* get_pegs() const override
+    { return block_pegs; }
+
+    PegCount* get_counts() const override
+    { return (PegCount*)&block_stats; }
+};
+
+//-------------------------------------------------------------------------
+static Module* mod_ctor()
+{ return new BlockActionModule; }
+
+static void mod_dtor(Module* m)
+{ delete m; }
 
 static IpsAction* block_ctor(Module*)
 { return new BlockAction; }
@@ -70,10 +111,10 @@ static ActionApi block_api
         0,
         API_RESERVED,
         API_OPTIONS,
-        s_name,
-        s_help,
-        nullptr,  // mod_ctor
-        nullptr,  // mod_dtor
+        action_name,
+        action_help,
+        mod_ctor,
+        mod_dtor,
     },
     IpsAction::IAP_BLOCK,
     nullptr,
index 2e112ae6b98e4df3a372539462bd12a186544a40..ae69f6e3e65f323f953976ed9611b8a5af8e3e7d 100644 (file)
@@ -21,6 +21,7 @@
 #include "config.h"
 #endif
 
+#include "actions/actions_module.h"
 #include "framework/ips_action.h"
 #include "framework/module.h"
 #include "packet_io/active.h"
 
 using namespace snort;
 
-#define s_name "drop"
-
-#define s_help \
+#define action_name "drop"
+#define action_help \
     "drop the current packet"
 
+#define module_name "drop"
+#define module_help \
+    "manage the counters for the drop action"
+
+static THREAD_LOCAL struct DropStats
+{
+    PegCount drop;
+} drop_stats;
+
+const PegInfo drop_pegs[] =
+{
+    { CountType::SUM, "drop", "number of packets that matched an IPS drop rule" },
+    { CountType::END, nullptr, nullptr }
+};
+
 //-------------------------------------------------------------------------
 class DropAction : public IpsAction
 {
 public:
-    DropAction() : IpsAction(s_name, nullptr) { }
+    DropAction() : IpsAction(action_name, nullptr) { }
 
     void exec(Packet*, const OptTreeNode* otn) override;
     bool drops_traffic() override { return true; }
@@ -51,9 +66,36 @@ void DropAction::exec(Packet* p, const OptTreeNode* otn)
     p->active->set_drop_reason("ips");
 
     Actions::alert(p, otn);
+    ++drop_stats.drop;
 }
 
 //-------------------------------------------------------------------------
+class DropActionModule : public Module
+{
+public:
+    DropActionModule() : Module(module_name, module_help)
+    { ActionsModule::add_action(module_name, drop_pegs); }
+
+    bool stats_are_aggregated() const override
+    { return true; }
+
+    void show_stats() override
+    { /* These stats are shown by ActionsModule. */ }
+
+    const PegInfo* get_pegs() const override
+    { return drop_pegs; }
+
+    PegCount* get_counts() const override
+    { return (PegCount*)&drop_stats; }
+};
+
+//-------------------------------------------------------------------------
+
+static Module* mod_ctor()
+{ return new DropActionModule; }
+
+static void mod_dtor(Module* m)
+{ delete m; }
 
 static IpsAction* drop_ctor(Module*)
 { return new DropAction; }
@@ -70,10 +112,10 @@ static ActionApi drop_api
         0,
         API_RESERVED,
         API_OPTIONS,
-        s_name,
-        s_help,
-        nullptr,  // mod_ctor
-        nullptr,  // mod_dtor
+        action_name,
+        action_help,
+        mod_ctor,
+        mod_dtor,
     },
     IpsAction::IAP_DROP,
     nullptr,
index fb5af763fa9e19d80346dadebd42b1f71d97b26d..70386fbee0cb1d47cf614f13b022cf807400940c 100644 (file)
@@ -22,6 +22,7 @@
 #endif
 
 #include "actions.h"
+#include "actions/actions_module.h"
 #include "detection/detect.h"
 #include "file_api/file_flows.h"
 #include "file_api/file_identifier.h"
 
 using namespace snort;
 
-#define s_name "file_id"
-
-#define s_help \
+#define action_name "file_id"
+#define action_help \
     "file_id file type id"
 
+#define module_name "file_id_action"
+#define module_help \
+    "manage the counters for the file_id action"
+
+static THREAD_LOCAL struct File_IdStats
+{
+    PegCount file_id;
+} file_id_stats;
+
+const PegInfo file_id_pegs[] =
+{
+    { CountType::SUM, "file_id", "number of packets that matched an IPS file_id rule" },
+    { CountType::END, nullptr, nullptr }
+};
+
 //-------------------------------------------------------------------------
 // ips action
 //-------------------------------------------------------------------------
@@ -43,7 +58,7 @@ using namespace snort;
 class File_IdAction : public IpsAction
 {
 public:
-    File_IdAction() : IpsAction(s_name, nullptr) { }
+    File_IdAction() : IpsAction(action_name, nullptr) { }
     void exec(Packet*, const OptTreeNode* otn) override;
 };
 
@@ -58,10 +73,38 @@ void File_IdAction::exec(Packet* p, const OptTreeNode* otn)
     if (!file)
         return;
     file->set_file_type(otn->sigInfo.file_id);
+
+    ++file_id_stats.file_id;
 }
 
 //-------------------------------------------------------------------------
 
+class File_IdActionModule : public Module
+{
+public:
+    File_IdActionModule() : Module(module_name, module_help)
+    { ActionsModule::add_action(module_name, file_id_pegs); }
+
+    bool stats_are_aggregated() const override
+    { return true; }
+
+    void show_stats() override
+    { /* These stats are shown by ActionsModule. */ }
+
+    const PegInfo* get_pegs() const override
+    { return file_id_pegs; }
+
+    PegCount* get_counts() const override
+    { return (PegCount*)&file_id_stats; }
+};
+
+//-------------------------------------------------------------------------
+static Module* mod_ctor()
+{ return new File_IdActionModule; }
+
+static void mod_dtor(Module* m)
+{ delete m; }
+
 static IpsAction* file_id_ctor(Module*)
 { return new File_IdAction; }
 
@@ -77,10 +120,10 @@ static ActionApi file_id_api
         0,
         API_RESERVED,
         API_OPTIONS,
-        s_name,
-        s_help,
-        nullptr,  // mod_ctor
-        nullptr,  // mod_dtor
+        action_name,
+        action_help,
+        mod_ctor,
+        mod_dtor,
     },
     IpsAction::IAP_OTHER,
     nullptr,
index 5293e6d15dd62ac791ba60141de3e7e2e3e85cf7..8b6689c7535e74ea25b2dde1163f0da73786abfa 100644 (file)
@@ -21,6 +21,7 @@
 #include "config.h"
 #endif
 
+#include "actions/actions_module.h"
 #include "framework/ips_action.h"
 #include "framework/module.h"
 #include "protocols/packet.h"
 
 using namespace snort;
 
-#define s_name "log"
-
-#define s_help \
+#define action_name "log"
+#define action_help \
     "log the current packet"
 
+#define module_name "log"
+#define module_help \
+    "manage the counters for the log action"
+
+static THREAD_LOCAL struct LogStats
+{
+    PegCount log;
+} log_stats;
+
+const PegInfo log_pegs[] =
+{
+    { CountType::SUM, "log", "number of packets that matched an IPS log rule" },
+    { CountType::END, nullptr, nullptr }
+};
+
 //-------------------------------------------------------------------------
 class LogAction : public IpsAction
 {
 public:
-    LogAction() : IpsAction(s_name, nullptr) { }
+    LogAction() : IpsAction(action_name, nullptr) { }
 
     void exec(Packet*, const OptTreeNode* otn) override;
 };
@@ -46,11 +61,40 @@ public:
 void LogAction::exec(Packet* p, const OptTreeNode* otn)
 {
     if ( otn )
+    {
         Actions::log(p, otn);
+        ++log_stats.log;
+    }
 }
 
 //-------------------------------------------------------------------------
 
+class LogActionModule : public Module
+{
+public:
+    LogActionModule() : Module(module_name, module_help)
+    { ActionsModule::add_action(module_name, log_pegs); }
+
+    bool stats_are_aggregated() const override
+    { return true; }
+
+    void show_stats() override
+    { /* These stats are shown by ActionsModule. */ }
+
+    const PegInfo* get_pegs() const override
+    { return log_pegs; }
+
+    PegCount* get_counts() const override
+    { return (PegCount*)&log_stats; }
+};
+
+//-------------------------------------------------------------------------
+static Module* mod_ctor()
+{ return new LogActionModule; }
+
+static void mod_dtor(Module* m)
+{ delete m; }
+
 static IpsAction* log_ctor(Module*)
 { return new LogAction; }
 
@@ -66,10 +110,10 @@ static ActionApi log_api
         0,
         API_RESERVED,
         API_OPTIONS,
-        s_name,
-        s_help,
-        nullptr,  // mod_ctor
-        nullptr,  // mod_dtor
+        action_name,
+        action_help,
+        mod_ctor,
+        mod_dtor,
     },
     IpsAction::IAP_LOG,
     nullptr,
index 420887ee356e41abda4e618c13eea130a02e58f8..15987dbce5d8972385655b2200e68b6b922d7d78 100644 (file)
@@ -21,6 +21,7 @@
 #include "config.h"
 #endif
 
+#include "actions/actions_module.h"
 #include "framework/ips_action.h"
 #include "framework/module.h"
 #include "protocols/packet.h"
 
 using namespace snort;
 
-#define s_name "pass"
-
-#define s_help \
+#define action_name "pass"
+#define action_help \
     "mark the current packet as passed"
 
+#define module_name "pass"
+#define module_help \
+    "manage the counters for the pass action"
+
+static THREAD_LOCAL struct PassStats
+{
+    PegCount pass;
+} pass_stats;
+
+const PegInfo pass_pegs[] =
+{
+    { CountType::SUM, "pass", "number of packets that matched an IPS pass rule" },
+    { CountType::END, nullptr, nullptr }
+};
+
 //-------------------------------------------------------------------------
 class PassAction : public IpsAction
 {
 public:
-    PassAction() : IpsAction(s_name, nullptr) { }
+    PassAction() : IpsAction(action_name, nullptr) { }
 
     void exec(Packet*, const OptTreeNode*) override;
 };
@@ -49,11 +64,38 @@ void PassAction::exec(Packet* p, const OptTreeNode* otn)
     {
         Actions::pass();
         p->packet_flags |= PKT_PASS_RULE;
+        ++pass_stats.pass;
     }
 }
 
 //-------------------------------------------------------------------------
 
+class PassActionModule : public Module
+{
+public:
+    PassActionModule() : Module(module_name, module_help)
+    { ActionsModule::add_action(module_name, pass_pegs); }
+
+    bool stats_are_aggregated() const override
+    { return true; }
+
+    void show_stats() override
+    { /* These stats are shown by ActionsModule. */ }
+
+    const PegInfo* get_pegs() const override
+    { return pass_pegs; }
+
+    PegCount* get_counts() const override
+    { return (PegCount*)&pass_stats; }
+};
+
+//-------------------------------------------------------------------------
+static Module* mod_ctor()
+{ return new PassActionModule; }
+
+static void mod_dtor(Module* m)
+{ delete m; }
+
 static IpsAction* pass_ctor(Module*)
 { return new PassAction; }
 
@@ -69,10 +111,10 @@ static ActionApi pass_api
         0,
         API_RESERVED,
         API_OPTIONS,
-        s_name,
-        s_help,
-        nullptr,  // mod_ctor
-        nullptr,  // mod_dtor
+        action_name,
+        action_help,
+        mod_ctor,
+        mod_dtor,
     },
     IpsAction::IAP_PASS,
     nullptr,
index 091c917889f035220b57eddf3c5621daa90c02d6..14ed536ce4373bf575f3b8ba5a52dac985ad95f4 100644 (file)
@@ -48,6 +48,7 @@
 #include <fstream>
 #include <string>
 
+#include "actions/actions_module.h"
 #include "framework/ips_action.h"
 #include "framework/module.h"
 #include "log/messages.h"
@@ -67,11 +68,14 @@ using namespace snort;
 using namespace HttpCommon;
 using namespace Http2Enums;
 
-#define s_name "react"
-
-#define s_help \
+#define action_name "react"
+#define action_help \
     "send response to client and terminate session"
 
+#define module_name "react"
+#define module_help \
+    "manage the data and the counters for the react action"
+
 static THREAD_LOCAL ProfileStats reactPerfStats;
 
 #define DEFAULT_HTTP \
@@ -129,6 +133,18 @@ private:
     std::string resp_buf;      // response to send
 };
 
+static THREAD_LOCAL struct ReactStats
+{
+    PegCount react;
+} react_stats;
+
+const PegInfo react_pegs[] =
+{
+    { CountType::SUM, "react", "number of packets that matched an IPS react rule" },
+    { CountType::END, nullptr, nullptr }
+};
+
+
 //-------------------------------------------------------------------------
 // active action
 //-------------------------------------------------------------------------
@@ -192,7 +208,7 @@ class ReactAction : public IpsAction
 {
 public:
     ReactAction(ReactData* c)
-        : IpsAction(s_name, &react_act_action), config(c), react_act_action(c)
+        : IpsAction(action_name, &react_act_action), config(c), react_act_action(c)
     { }
 
     ~ReactAction() override
@@ -212,13 +228,14 @@ void ReactAction::exec(Packet* p, const OptTreeNode* otn)
     p->active->set_drop_reason("ips");
 
     Actions::alert(p, otn);
+    ++react_stats.react;
 }
 
 //-------------------------------------------------------------------------
 // module
 //-------------------------------------------------------------------------
 
-static const Parameter s_params[] =
+static const Parameter module_params[] =
 {
     { "page", Parameter::PT_STRING, nullptr, nullptr,
       "file containing HTTP response body" },
@@ -229,8 +246,8 @@ static const Parameter s_params[] =
 class ReactModule : public Module
 {
 public:
-    ReactModule() : Module(s_name, s_help, s_params)
-    { }
+    ReactModule() : Module(module_name, module_help, module_params)
+    { ActionsModule::add_action(module_name, react_pegs); }
 
     bool begin(const char*, int, SnortConfig*) override;
     bool set(const char*, Value&, SnortConfig*) override;
@@ -256,6 +273,18 @@ public:
 
     std::string get_data();
 
+    bool stats_are_aggregated() const override
+    { return true; }
+
+    void show_stats() override
+    { /* These stats are shown by ActionsModule. */ }
+
+    const PegInfo* get_pegs() const override
+    { return react_pegs; }
+
+    PegCount* get_counts() const override
+    { return (PegCount*)&react_stats; }
+
 private:
     std::string page;
     bool getpage(const char* file);
@@ -327,8 +356,8 @@ static const ActionApi react_api =
         0,
         API_RESERVED,
         API_OPTIONS,
-        s_name,
-        s_help,
+        action_name,
+        action_help,
         mod_ctor,
         mod_dtor
     },
index 1a6844c565b384ea7c63ef767bd1f8d38eebb2d3..28bc7d805f7a151ff688bbba2a7742ba8174c9c8 100644 (file)
@@ -48,6 +48,7 @@
 #include "config.h"
 #endif
 
+#include "actions/actions_module.h"
 #include "framework/ips_action.h"
 #include "framework/module.h"
 #include "main/snort_config.h"
 
 using namespace snort;
 
-#define s_name "reject"
-
-#define s_help \
+#define action_name "reject"
+#define action_help \
     "terminate session with TCP reset or ICMP unreachable"
 
+#define module_name "reject"
+#define module_help \
+    "manage the data and the counters for the reject action"
+
 enum
 {
     REJ_NONE     = 0x00,
@@ -78,6 +82,17 @@ enum
 
 THREAD_LOCAL ProfileStats rejPerfStats;
 
+static THREAD_LOCAL struct RejectStats
+{
+    PegCount reject;
+} reject_stats;
+
+const PegInfo reject_pegs[] =
+{
+    { CountType::SUM, "reject", "number of packets that matched an IPS reject rule" },
+    { CountType::END, nullptr, nullptr }
+};
+
 //-------------------------------------------------------------------------
 // active action
 //-------------------------------------------------------------------------
@@ -165,7 +180,7 @@ private:
 // class methods
 //-------------------------------------------------------------------------
 
-RejectAction::RejectAction(uint32_t f) : IpsAction(s_name, &rej_act_action) , rej_act_action(f)
+RejectAction::RejectAction(uint32_t f) : IpsAction(action_name, &rej_act_action) , rej_act_action(f)
 { }
 
 void RejectAction::exec(Packet* p, const OptTreeNode* otn)
@@ -176,13 +191,14 @@ void RejectAction::exec(Packet* p, const OptTreeNode* otn)
     p->active->update_status(p);
 
     Actions::alert(p, otn);
+    ++reject_stats.reject;
 }
 
 //-------------------------------------------------------------------------
 // module
 //-------------------------------------------------------------------------
 
-static const Parameter s_params[] =
+static const Parameter module_params[] =
 {
     { "reset", Parameter::PT_ENUM, "none|source|dest|both", "both",
       "send TCP reset to one or both ends" },
@@ -196,7 +212,8 @@ static const Parameter s_params[] =
 class RejectModule : public Module
 {
 public:
-    RejectModule() : Module(s_name, s_help, s_params) { }
+    RejectModule() : Module(module_name, module_help, module_params) 
+    { ActionsModule::add_action(module_name, reject_pegs); }
 
     bool begin(const char*, int, SnortConfig*) override;
     bool set(const char*, Value&, SnortConfig*) override;
@@ -209,6 +226,18 @@ public:
 
     uint32_t get_data();
 
+    bool stats_are_aggregated() const override
+    { return true; }
+
+    void show_stats() override
+    { /* These stats are shown by ActionsModule. */ }
+
+    const PegInfo* get_pegs() const override
+    { return reject_pegs; }
+
+    PegCount* get_counts() const override
+    { return (PegCount*)&reject_stats; }
+
 private:
     uint32_t flags = 0;
 };
@@ -298,8 +327,8 @@ static const ActionApi rej_api =
         0,
         API_RESERVED,
         API_OPTIONS,
-        s_name,
-        s_help,
+        action_name,
+        action_help,
         mod_ctor,
         mod_dtor
     },
index 0cb023bddf35dab42f290a07f44a4d2050762419..14c24fa4d03e42bd655bdc78db8e0be397db7363 100644 (file)
@@ -21,6 +21,7 @@
 #include "config.h"
 #endif
 
+#include "actions/actions_module.h"
 #include "detection/detection_engine.h"
 #include "framework/ips_action.h"
 #include "framework/module.h"
 
 using namespace snort;
 
-#define s_name "rewrite"
-
-#define s_help \
+#define action_name "rewrite"
+#define action_help \
     "overwrite packet contents with the \"replace\" option content"
 
+#define module_name "rewrite"
+#define module_help \
+    "manage the counters for the rewrite action"
+
+static THREAD_LOCAL struct ReplaceStats
+{
+    PegCount replace;
+} replace_stats;
+
+const PegInfo replace_pegs[] =
+{
+    { CountType::SUM, "rewrite", "number of packets that matched an IPS rewrite rule" },
+    { CountType::END, nullptr, nullptr }
+};
+
 //--------------------------------------------------------------------------
 // queue foo
 //--------------------------------------------------------------------------
@@ -96,7 +111,7 @@ void ReplaceActiveAction::delayed_exec(Packet* p)
 class ReplaceAction : public IpsAction
 {
 public:
-    ReplaceAction() : IpsAction(s_name, &rep_act_action) { }
+    ReplaceAction() : IpsAction(action_name, &rep_act_action) { }
 
     void exec(Packet*, const OptTreeNode* otn) override;
 
@@ -109,10 +124,37 @@ void ReplaceAction::exec(Packet* p, const OptTreeNode* otn)
     p->active->rewrite_packet(p);
 
     Actions::alert(p, otn);
+    ++replace_stats.replace;
 }
 
 //-------------------------------------------------------------------------
 
+class ReplaceActionModule : public Module
+{
+public:
+    ReplaceActionModule() : Module(module_name, module_help)
+    { ActionsModule::add_action(module_name, replace_pegs); }
+
+    bool stats_are_aggregated() const override
+    { return true; }
+
+    void show_stats() override
+    { /* These stats are shown by ActionsModule. */ }
+
+    const PegInfo* get_pegs() const override
+    { return replace_pegs; }
+
+    PegCount* get_counts() const override
+    { return (PegCount*)&replace_stats; }
+};
+
+//-------------------------------------------------------------------------
+static Module* mod_ctor()
+{ return new ReplaceActionModule; }
+
+static void mod_dtor(Module* m)
+{ delete m; }
+
 static IpsAction* rep_ctor(Module*)
 { return new ReplaceAction; }
 
@@ -128,10 +170,10 @@ static ActionApi rep_api
         0,
         API_RESERVED,
         API_OPTIONS,
-        s_name,
-        s_help,
-        nullptr,  // mod_ctor
-        nullptr,  // mod_dtor
+        action_name,
+        action_help,
+        mod_ctor,
+        mod_dtor,
     },
     IpsAction::IAP_REWRITE,
     nullptr,
diff --git a/src/actions/actions_module.cc b/src/actions/actions_module.cc
new file mode 100644 (file)
index 0000000..ddeb0e4
--- /dev/null
@@ -0,0 +1,129 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2024-2024 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.
+//--------------------------------------------------------------------------
+
+// actions_module.cc author Steve Chew <stechew@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <algorithm>
+#include <vector>
+
+#include "actions_module.h"
+#include "actions/actions.h"
+#include "log/messages.h"
+#include "managers/action_manager.h"
+#include "managers/module_manager.h"
+
+const PegInfo end_peg[] =
+{
+    { CountType::END, nullptr, nullptr }
+};
+
+std::map<std::string, std::vector<PegInfo>> ActionsModule::module_peg_info_map { };
+
+std::array<PegInfo, ACTIONS_ARRAY_SIZE> ActionsModule::peg_info_array { {end_peg[0]} };
+
+THREAD_LOCAL std::array<PegCount, ACTIONS_ARRAY_SIZE> ActionsModule::peg_count_array {{0}};
+THREAD_LOCAL std::array<PegCount, ACTIONS_ARRAY_SIZE> ActionsModule::prev_peg_count_array {{0}};
+
+
+void ActionsModule::add_action(std::string module_name, const PegInfo* pegs)
+{
+    std::vector<PegInfo> aggregated_pegs;
+    for (int i = 0; pegs[i].type != CountType::END; i++)
+        aggregated_pegs.push_back(pegs[i]);
+
+    module_peg_info_map.emplace(module_name, aggregated_pegs);
+
+    // FIXIT-M: Probably not needed unless things change on a reload?
+    std::fill(std::begin(peg_info_array), std::end(peg_info_array), end_peg[0]);
+
+    // Go through the module names alphabetically and add their pegs.
+    int i = 0;
+    for (const auto& kv : module_peg_info_map)
+    {
+        for (const auto& peg_info : kv.second)
+        {
+            peg_info_array[i++] = peg_info;
+
+            // FIXIT-L: Limited by array size.
+            assert(i < MAX_ACTIONS);
+            if (i >= MAX_ACTIONS)
+            {
+                snort::WarningMessage("Exceeded max action pegs limit (%u). Ignoring remaining action pegs.\n", MAX_ACTIONS);
+                return;
+            }
+        }
+    }
+
+    // Peg info array must terminate with CountType::END.
+    peg_info_array[i] = end_peg[0];
+}
+
+void ActionsModule::prep_counts(bool dump_stats)
+{
+    int peg_count = 0;
+    for (auto& kv : module_peg_info_map)
+    {
+        Module* mod = snort::ModuleManager::get_module(kv.first.c_str());
+        const PegInfo* pegs = mod->get_pegs();
+        const PegCount* counts = mod->get_counts();
+
+        for (int i=0; pegs[i].type != CountType::END; i++)
+        {
+            for (const auto& peg_info : kv.second)
+            {
+                if (0 == strcmp(peg_info.name, pegs[i].name))
+                {
+                    if (dump_stats)
+                    {
+                        // For dumping stats
+                        peg_count_array[peg_count++] = counts[i];
+                    }
+                    else
+                    {
+                        // For perf monitor
+                        peg_count_array[peg_count] = counts[i] - prev_peg_count_array[peg_count];
+                        prev_peg_count_array[peg_count] = counts[i];
+
+                        ++peg_count;
+                    }
+
+                    // FIXIT-L: Limited by array size.
+                    assert(peg_count < MAX_ACTIONS);
+                    if (peg_count >= MAX_ACTIONS)
+                        return;
+                    break;
+                }
+            }
+        }
+    }
+}
+
+PegCount* ActionsModule::get_counts() const
+{
+    return (PegCount*)&peg_count_array[0];
+}
+
+const PegInfo* ActionsModule::get_pegs() const
+{
+    return (PegInfo*)&peg_info_array[0];
+}
+
diff --git a/src/actions/actions_module.h b/src/actions/actions_module.h
new file mode 100644 (file)
index 0000000..703b8ce
--- /dev/null
@@ -0,0 +1,62 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2024-2024 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.
+//--------------------------------------------------------------------------
+
+// actions_module.h author Steve Chew <stechew@cisco.com>
+
+// Aggregates counters from all of the IPS actions.
+
+#ifndef ACTIONS_MODULE_H
+#define ACTIONS_MODULE_H
+
+#include <map>
+#include <vector>
+
+#include "framework/module.h"
+
+#define ACTIONS_ARRAY_SIZE UINT8_MAX
+#define MAX_ACTIONS (ACTIONS_ARRAY_SIZE-1)
+
+class ActionsModule : public snort::Module
+{
+public:
+    ActionsModule() : snort::Module("ips_actions", "aggregate action counters")
+    { }
+
+    Usage get_usage() const override
+    { return GLOBAL; }
+
+    static void add_action(std::string module_name, const PegInfo* pegs);
+    void prep_counts(bool) override;
+    PegCount* get_counts() const override;
+    const PegInfo* get_pegs() const override;
+
+    bool counts_need_prep() const override
+    { return true; }
+
+    bool is_aggregator() const override
+    { return true; }
+
+private:
+    static std::map<std::string, std::vector<PegInfo>> module_peg_info_map;
+    static std::array<PegInfo, ACTIONS_ARRAY_SIZE> peg_info_array;
+    static THREAD_LOCAL std::array<PegCount, ACTIONS_ARRAY_SIZE> peg_count_array;
+    static THREAD_LOCAL std::array<PegCount, ACTIONS_ARRAY_SIZE> prev_peg_count_array;
+};
+
+#endif
+
index 421b320e1861576914293b27f5f31610096eb606..49bbfdf09f8efa0ef8a39de217c80199e3dd8830 100644 (file)
@@ -29,7 +29,7 @@
 
 // this is the current version of the base api
 // must be prefixed to subtype version
-#define BASE_API_VERSION 16
+#define BASE_API_VERSION 17
 
 // set options to API_OPTIONS to ensure compatibility
 #ifndef API_OPTIONS
index 24b87b537fa80dac9b4280c952469fc0bced83be..f2f97bfabee2a4d094c262a05caca50353f8a459 100644 (file)
@@ -171,6 +171,16 @@ public:
     virtual bool global_stats() const
     { return false; }
 
+    // Return true only if all of the module's stats are aggregated into
+    // another module.
+    virtual bool stats_are_aggregated() const
+    { return false; }
+
+    // Return true only if all of the module's stats are aggregated from
+    // other modules.
+    virtual bool is_aggregator() const
+    { return false; }
+
     virtual void sum_stats(bool dump_stats);
     virtual void show_interval_stats(IndexVec&, FILE*);
     virtual void show_stats();
index 59ee55975f153638b83843360310deaa35d97448..e6db41f74ea1cd8547f41c960dc03e65ab611f0f 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <sys/resource.h>
 
+#include "actions/actions_module.h"
 #include "codecs/codec_module.h"
 #include "detection/detection_module.h"
 #include "detection/fp_config.h"
@@ -1968,6 +1969,7 @@ void module_init()
     ModuleManager::add_module(new SearchEngineModule);
     ModuleManager::add_module(new SFDAQModule);
     ModuleManager::add_module(new PayloadInjectorModule);
+    ModuleManager::add_module(new ActionsModule);
 
     // these could but probably shouldn't be policy specific
     // or should be broken into policy and non-policy parts
index f28d832f43e4aa43d10a7f0983c6f68411bbcb9a..32747e3521e73ede4f81de067088b60682fb2f00 100644 (file)
@@ -1332,7 +1332,7 @@ void ModuleManager::show_pegs(const char* pfx, bool exact)
         const Module* m = mh->mod;
         assert(m);
 
-        if ( !selected(m, pfx, exact) )
+        if ( !selected(m, pfx, exact) || m->stats_are_aggregated())
             continue;
 
         const PegInfo* pegs = m->get_pegs();
index ced852c5062b82407a1c108b8172954ea01d040b..d89bcdf6146feb233c6b2aab8a84a24cc14fd82d 100644 (file)
@@ -39,6 +39,9 @@ BaseTracker::BaseTracker(PerfConfig* perf) : PerfTracker(perf, PERF_NAME "_base"
 {
     for ( ModuleConfig& mod : modules )
     {
+        if (mod.ptr->stats_are_aggregated())
+            continue;
+
         formatter->register_section(mod.ptr->get_name());
 
         for ( auto const& idx : mod.pegs )
@@ -58,6 +61,9 @@ void BaseTracker::process(bool summary)
     {
         for ( const ModuleConfig& mod : modules )
         {
+            if (mod.ptr->is_aggregator())
+                continue;
+
             if (strstr(ModuleManager::dynamic_stats_modules, mod.ptr->get_name()) || mod.ptr->global_stats())
             {
                 lock_guard<mutex> lock(ModuleManager::stats_mutex);