]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #274 in SNORT/snort3 from ~JOCORNET/snort3:latency to master
authorRuss Combs (rucombs) <rucombs@cisco.com>
Wed, 24 Feb 2016 00:23:43 +0000 (19:23 -0500)
committerRuss Combs (rucombs) <rucombs@cisco.com>
Wed, 24 Feb 2016 00:23:43 +0000 (19:23 -0500)
Squashed commit of the following:

commit f7b908a98e5abb31e98464d047015d543a445075
Author: Joel Cornett <joel.cornett@gmail.com>
Date:   Mon Feb 22 18:19:57 2016 -0500

    added rule latency logging

    fixed rule latency unittests
    update dev_notes

commit 51a331d2c25a74f1bec56028179ef0ccda947b7a
Author: Joel Cornett <joel.cornett@gmail.com>
Date:   Fri Feb 19 14:21:56 2016 -0500

    factored out rule tree logic

    updated rule latency unit tests & rule interface
    added latency_ prefix to member fields

commit 0aaeb3de46c6c19f0c4007b4260720a8649ad08b
Author: Joel Cornett <joel.cornett@gmail.com>
Date:   Thu Feb 18 13:45:18 2016 -0500

    replace ppm_dot_root_state_t with RuleLatencyState

    update Makefile.am to include rule_latency_state.h
    implemented suspend/reenable and fixed latency counts
    fixed includes in rule_latency.cc
    refactored timer stack
    fixed default ctor for OtnState

commit c4440677ce5d75b1fd6a7e8d03ce28c595fb65e1
Author: Joel Cornett <joel.cornett@gmail.com>
Date:   Tue Feb 16 19:28:34 2016 -0500

    added rule latency

    Updated stats and RuleLatency interface

    - updated latency peg counts
    - added rule latency rules to module
    - updated packet latency stats accounting
    - replaced PPM_RULE with RuleLatency contexts

    added rule latency config options
    cleaned up stats and config options, added to rule latency
    removed comments & LatencyTimer::marked

22 files changed:
src/detection/detect.cc
src/detection/detection_options.cc
src/detection/detection_options.h
src/detection/fp_create.cc
src/detection/fp_detect.cc
src/detection/treenodes.h
src/latency/CMakeLists.txt
src/latency/Makefile.am
src/latency/dev_notes.txt
src/latency/latency_config.h
src/latency/latency_module.cc
src/latency/latency_rules.h
src/latency/latency_stats.h
src/latency/latency_timer.h
src/latency/packet_latency.cc
src/latency/packet_latency_config.h
src/latency/rule_latency.cc [new file with mode: 0644]
src/latency/rule_latency.h [new file with mode: 0644]
src/latency/rule_latency_config.h [new file with mode: 0644]
src/latency/rule_latency_state.h [new file with mode: 0644]
src/profiler/rule_profiler.cc
src/utils/stats.cc

index a605502a16371b6a2ee8a57bd8b0870d0d8f3c66..1a17bfb11be346e767b52e4725f9f4d1f7fd475a 100644 (file)
@@ -84,8 +84,6 @@ void snort_ignore(Packet*) { }
 
 void snort_inspect(Packet* p)
 {
-    uint64_t pktcnt = 0;
-
     {
         PacketLatency::Context pkt_latency_ctx { p };
 
@@ -151,9 +149,6 @@ void snort_inspect(Packet* p)
             InspectorManager::clear(p);
     }
 
-    if ( PPM_RULES_ENABLED() )
-        PPM_RULE_LOG(pktcnt, p);
-
     Profile profile(eventqPerfStats);
     SnortEventqLog(p);
     SnortEventqReset();
index a70a10c3502e02f2c395f6aba7dfa972260f8636..8c7345a9e8a10a481a7397dfb30adcaef9d3dcd6 100644 (file)
@@ -752,11 +752,12 @@ struct node_profile_stats
     hr_duration elapsed_match;
     hr_duration elapsed_no_match;
     uint64_t checks;
-    uint64_t ppm_disables;
+    uint64_t latency_timeouts;
+    uint64_t latency_suspends;
 };
 
 static void detection_option_node_update_otn_stats(detection_option_tree_node_t* node,
-    node_profile_stats* stats, uint64_t checks, uint64_t disables)
+    node_profile_stats* stats, uint64_t checks, uint64_t timeouts, uint64_t suspends)
 {
     int i;
     node_profile_stats local_stats; /* cumulative stats for this node */
@@ -771,79 +772,84 @@ static void detection_option_node_update_otn_stats(detection_option_tree_node_t*
         node_stats.elapsed_no_match += node->state[i].elapsed_no_match;
         node_stats.checks += node->state[i].checks;
     }
-    if (stats)
+
+    if ( stats )
     {
         local_stats.elapsed = stats->elapsed + node_stats.elapsed;
         local_stats.elapsed_match = stats->elapsed_match + node_stats.elapsed_match;
         local_stats.elapsed_no_match = stats->elapsed_no_match + node_stats.elapsed_no_match;
+
         if (node_stats.checks > stats->checks)
             local_stats.checks = node_stats.checks;
         else
             local_stats.checks = stats->checks;
 
-        local_stats.ppm_disables = disables;
+        local_stats.latency_timeouts = timeouts;
+        local_stats.latency_suspends = suspends;
     }
+
     else
     {
         local_stats.elapsed = node_stats.elapsed;
         local_stats.elapsed_match = node_stats.elapsed_match;
         local_stats.elapsed_no_match = node_stats.elapsed_no_match;
         local_stats.checks = node_stats.checks;
-        local_stats.ppm_disables = disables;
+        local_stats.latency_timeouts = timeouts;
+        local_stats.latency_suspends = suspends;
     }
 
-    if (node->option_type == RULE_OPTION_TYPE_LEAF_NODE)
+    if ( node->option_type == RULE_OPTION_TYPE_LEAF_NODE )
     {
-        /* Update stats for this otn */
-        // FIXIT-H J this should either be called from the packet threads at exit
+        // Update stats for this otn
+        // FIXIT-L J this should either be called from the packet threads at exit
         // or *all* the states should get totalled by the main thread
         // Right now, it looks like we're missing out on some stats although it's possible
         // that this is "corrected" in the profiler code
-        OptTreeNode* otn = (OptTreeNode*)node->option_data;
-        OtnState* state = otn->state + get_instance_id();
-        state->elapsed += local_stats.elapsed;
-        state->elapsed_match += local_stats.elapsed_match;
-        state->elapsed_no_match += local_stats.elapsed_no_match;
-        if (local_stats.checks > state->checks)
-            state->checks = local_stats.checks;
-
-        state->ppm_disable_cnt += local_stats.ppm_disables;
+        auto* otn = (OptTreeNode*)node->option_data;
+        auto& state = otn->state[get_instance_id()];
+
+        state.elapsed += local_stats.elapsed;
+        state.elapsed_match += local_stats.elapsed_match;
+        state.elapsed_no_match += local_stats.elapsed_no_match;
+
+        if (local_stats.checks > state.checks)
+            state.checks = local_stats.checks;
+
+        state.latency_timeouts += local_stats.latency_timeouts;
+        state.latency_suspends += local_stats.latency_suspends;
     }
 
     if ( node->num_children )
     {
         for ( i=0; i < node->num_children; ++i )
             detection_option_node_update_otn_stats(node->children[i], &local_stats, checks,
-                disables);
+                timeouts, suspends);
     }
 }
 
 void detection_option_tree_update_otn_stats(SFXHASH* doth)
 {
-    if (doth == NULL)
+    if ( !doth )
         return;
 
-    /* Find the first tree root in the table */
-    SFXHASH_NODE* hashnode = sfxhash_findfirst(doth);
-
-    while (hashnode)
+    for ( auto hnode = sfxhash_findfirst(doth); hnode; hnode = sfxhash_findnext(doth) )
     {
-        detection_option_tree_node_t* node =
-            (detection_option_tree_node_t*)hashnode->data;
+        auto* node = (detection_option_tree_node_t*)hnode->data;
+        assert(node);
 
         uint64_t checks = 0;
-        uint64_t disables = 0;
+        uint64_t timeouts = 0;
+        uint64_t suspends = 0;
 
         for ( unsigned i = 0; i < get_instance_max(); ++i )
         {
             checks += node->state[i].checks;
-            disables += node->state[i].ppm_disable_cnt;
+            timeouts += node->state[i].latency_timeouts;
+            suspends += node->state[i].latency_suspends;
         }
 
         if ( checks )
-            detection_option_node_update_otn_stats(node, nullptr, checks, disables);
-
-        hashnode = sfxhash_findnext(doth);
+            detection_option_node_update_otn_stats(node, nullptr, checks, timeouts, suspends);
     }
 }
 
@@ -853,8 +859,7 @@ detection_option_tree_root_t* new_root()
     detection_option_tree_root_t* p = (detection_option_tree_root_t*)
         SnortAlloc(sizeof(detection_option_tree_root_t));
 
-    p->state = (ppm_dot_root_state_t*)
-        SnortAlloc(sizeof(ppm_dot_root_state_t)*get_instance_max());
+    p->latency_state = new RuleLatencyState[get_instance_max()]();
 
     return p;
 }
@@ -869,7 +874,7 @@ void free_detection_option_root(void** existing_tree)
     root = (detection_option_tree_root_t*)*existing_tree;
     free(root->children);
 
-    free(root->state);
+    delete[] root->latency_state;
     free(root);
     *existing_tree = NULL;
 }
index f67c33f5df2448910f4b53b102951cca346bf742..75131175c9f494d38d542c48abbeb2599f3dce6f 100644 (file)
@@ -36,8 +36,9 @@
 #endif
 
 #include <sys/time.h>
-#include "main/snort_types.h"
 #include "detection/rule_option_types.h"
+#include "main/snort_types.h"
+#include "latency/rule_latency_state.h"
 #include "time/clock_defs.h"
 
 struct Packet;
@@ -65,8 +66,8 @@ struct dot_node_state_t
     uint64_t checks;
     uint64_t disables;
 
-    uint64_t ppm_disable_cnt;
-    uint64_t ppm_enable_cnt;
+    unsigned latency_timeouts;
+    unsigned latency_suspends;
 
     // FIXIT-L J perf profiler stuff should be factored of the node state struct
     void update(hr_duration delta, bool match)
@@ -94,19 +95,11 @@ struct detection_option_tree_node_t
     dot_node_state_t* state;
 };
 
-// this is per packet thread
-struct ppm_dot_root_state_t
-{
-    uint64_t ppm_suspend_time;
-    uint64_t ppm_disable_cnt;
-    bool enabled;
-};
-
 struct detection_option_tree_root_t
 {
     int num_children;
     detection_option_tree_node_t** children;
-    ppm_dot_root_state_t* state;
+    RuleLatencyState* latency_state;
 };
 
 struct detection_option_eval_data_t
index fe46cb210c150f40376836362c7e0f35c7b8a288..d691cc4db3431df10e8fc44ae70fa49eb837ed63 100644 (file)
@@ -117,9 +117,6 @@ int otn_create_tree(OptTreeNode* otn, void** existing_tree)
 
     detection_option_tree_root_t* root = (detection_option_tree_root_t*)*existing_tree;
 
-    for ( unsigned i = 0; i < get_instance_max(); ++i )
-        root->state[i].enabled = true;
-
     OptFpList* opt_fp = otn->opt_func;
 
     if (!root->children)
index 6f1a806633257304de86a3486f03cabc421f52e5..37dd643ed558173919a83478cf311dbfd25e1d19 100644 (file)
@@ -55,6 +55,7 @@
 #include "treenodes.h"
 
 #include "latency/packet_latency.h"
+#include "latency/rule_latency.h"
 #include "main/snort_config.h"
 #include "main/snort_debug.h"
 #include "framework/cursor.h"
@@ -402,52 +403,24 @@ int fpEvalRTN(RuleTreeNode* rtn, Packet* p, int check_ports)
     return 1;
 }
 
-static int detection_option_tree_evaluate(
-    detection_option_tree_root_t* root,
+static int detection_option_tree_evaluate(detection_option_tree_root_t* root,
     detection_option_eval_data_t* eval_data)
 {
-    if (!root)
+    if ( !root )
         return 0;
 
-    /* Start Rule Timer */
-    if ( PPM_RULES_ENABLED() )
-    {
-        PPM_GET_TIME();
-        PPM_INIT_RULE_TIMER();
-        ppm_dot_root_state_t* root_state = root->state + get_instance_id();
-
-        if ( !root_state->enabled )
-        {
-            PPM_REENABLE_TREE(root, eval_data->p);
+    RuleLatency::Context rule_latency_ctx(root);
 
-            if ( !root_state->enabled )
-            {
-                PPM_END_RULE_TIMER();
-                return 0;
-            }
-        }
-    }
+    if ( !RuleLatency::enabled() )
+        return 0;
 
     Cursor c(eval_data->p);
-
     int rval = 0;
-    for ( int i = 0; i< root->num_children; i++)
-    {
-        /* Increment number of events generated from that child */
-        rval += detection_option_node_evaluate(root->children[i], eval_data, c);
-    }
 
-    if ( PPM_ENABLED() )
+    for ( int i = 0; i < root->num_children; ++i )
     {
-        PPM_GET_TIME();
-
-        /* Rule test */
-        if ( PPM_RULES_ENABLED() )
-        {
-            PPM_RULE_TEST(root, eval_data->p);
-            PPM_ACCUM_RULE_TIME();
-            PPM_END_RULE_TIMER();
-        }
+        // Increment number of events generated from that child 
+        rval += detection_option_node_evaluate(root->children[i], eval_data, c);
     }
 
     return rval;
@@ -1087,9 +1060,6 @@ static inline int fpEvalHeaderSW(PortGroup* port_group, Packet* p,
                 return 0;
     }
 
-    if ( PPM_ENABLED() )
-        PPM_GET_TIME();
-
     do
     {
         // FIXIT-L restrict to non-data packets?  (non-data includes
index 4b4fa8d1ea77b831aa1ed0af9c0c81a002751bd3..8780a79933f07cc5bc2e930f4733cb50a8d4b613 100644 (file)
@@ -57,28 +57,20 @@ struct OtnState
 {
     // profiling
     // FIXIT-L J factor the profiling stuff out
-    hr_duration elapsed;
-    hr_duration elapsed_match;
-    hr_duration elapsed_no_match;
+    hr_duration elapsed = 0_ticks;
+    hr_duration elapsed_match = 0_ticks;
+    hr_duration elapsed_no_match = 0_ticks;
 
-    uint64_t checks;
-    uint64_t matches;
-    uint8_t noalerts;
-    uint64_t alerts;
+    uint64_t checks = 0;
+    uint64_t matches = 0;
+    uint8_t noalerts = 0;
+    uint64_t alerts = 0;
 
-    // ppm
-    uint64_t ppm_suspend_time;
-    uint64_t ppm_disable_cnt;
+    uint64_t latency_timeouts = 0;
+    uint64_t latency_suspends = 0;
 
     operator bool() const
     { return elapsed > 0_ticks || checks > 0; }
-
-    void reset()
-    {
-        elapsed = elapsed_match = elapsed_no_match = 0_ticks;
-        checks = matches = noalerts = alerts = 0;
-        ppm_suspend_time = ppm_disable_cnt = 0;
-    }
 };
 
 // one of these for each rule
index a8b26bdc0c62435ec77af6f74d84ff075bb5babb..343a5a6c6641fd8a70899b47961be269028be9f7 100644 (file)
@@ -1,5 +1,6 @@
 set ( LATENCY_INCLUDES
     packet_latency.h
+    rule_latency.h
     latency_rules.h
     )
 
@@ -7,6 +8,7 @@ set ( LATENCY_SOURCES
     latency_timer.h
     latency_util.h
     packet_latency.cc
+    rule_latency.cc
     latency_module.cc
     )
 
index 48f87dfc882be7c4a2506afd70af96f422af8b31..a3f799f767c9d3f075e099b3e0c40c52dfa5f5f4 100644 (file)
@@ -3,7 +3,6 @@ noinst_LIBRARIES = liblatency.a
 x_includedir = $(pkgincludedir)/latency
 
 x_include_HEADERS = \
-packet_latency.h \
 latency_rules.h
 
 liblatency_a_SOURCES = \
@@ -16,4 +15,8 @@ latency_module.h \
 latency_module.cc \
 packet_latency_config.h \
 packet_latency.h \
-packet_latency.cc
+packet_latency.cc \
+rule_latency_config.h \
+rule_latency_state.h \
+rule_latency.h \
+rule_latency.cc
index 4145f499119c2d46a1b50e4d69b77cb5fe03fa63..73ac669070d6e451ad1958f24b775839bbf0a35c 100644 (file)
@@ -8,8 +8,21 @@ The latency directory contains code for latency logging and management.
   based on whether the packet was fastpathed and depending on
   how the manager was configured.
 
-* Rule latency: To be ported over from ppm/
+* Rule latency: tracks and manages latency in rule tree evaluation.
+  Rule latency works much like packet latency. Instead of fastpath
+  the API contains an enabled() check that tests whether the
+  current rule tree should be evaluated. Pushing a rule tree
+  onto the stack has the side effect of re-enabling the rule
+  tree if conditions are met. Popping a rule tree off of the stack
+  has the side effect of suspending the rule tree if certain conditions
+  are met. Re-enables, suspends, and rule tree timeouts all
+  propagate log events. Re-enables and suspends propagate
+  alerts.
 
-Notes:
+  Pushing a rule tree side-effect: A rule tree is re-enabled if
+  1) it is currently disabled and 2) the suspend timeout is nonzero
+  and has been reached.
 
- * Latency is intended to replace the legacy ppm/ code
+  Popping a rule tree side-effect: A rule tree is suspended if
+  1) it is timed out and 2) the timeout threshold is met or
+  exceeded.
index a9c440ea8e29eaedfe632034d61f17522c42ef93..f097d9294cf6a38efa4e1c57b9bfc95234f7ed38 100644 (file)
 #define LATENCY_CONFIG_H
 
 #include "packet_latency_config.h"
+#include "rule_latency_config.h"
 
 struct LatencyConfig
 {
     PacketLatencyConfig packet_latency;
+    RuleLatencyConfig rule_latency;
 };
 
 #endif
index 151a81d59f2eba60f22313563f9096c5b6be3c85..bc846f32cb7116acfa55feabe5de87f8afed05e8 100644 (file)
 
 static const Parameter s_packet_params[] =
 {
-    { "enable", Parameter::PT_BOOL, nullptr, "true",
-        "enable packet latency" },
-
-    { "max_time", Parameter::PT_INT, "0:", "0",
+    { "max_time", Parameter::PT_INT, "0:", "500",
         "set timeout for packet latency thresholding (usec)" },
 
     { "fastpath", Parameter::PT_BOOL, nullptr, "false",
         "fastpath expensive packets (max_time exceeded)" },
 
+    // FIXIT-L J what is the most intuitive default action?
+    { "action", Parameter::PT_ENUM, "none | alert | log | alert_and_log", "alert_and_log",
+        "event action if packet times out and is fastpathed" },
+
+    { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
+static const Parameter s_rule_params[] =
+{
+    { "max_time", Parameter::PT_INT, "0:", "500",
+        "set timeout for rule evaluation (usec)" },
+
+    // We could just treat suspend_threshold == 0 as suspend == false
+    // but we leave this here for parity with packet latency
+    { "suspend", Parameter::PT_BOOL, nullptr, "false",
+        "temporarily suspend expensive rules" },
+
+    // FIXIT-L J what is a sensible default for this?
+    { "suspend_threshold", Parameter::PT_INT, "1:", "5",
+        "set threshold for number of timeouts before suspending a rule" },
+
+    { "max_suspend_time", Parameter::PT_INT, "0:", "30000",
+        "set max time for suspending a rule (ms, 0 means permanently disable rule)" },
+
+    // FIXIT-L J what is the most intuitive default action?
     { "action", Parameter::PT_ENUM, "none | alert | log | alert_and_log", "alert_and_log",
-        "event action if packet latency times out" },
+        "event action for rule latency enable and suspend events" },
 
     { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
 };
@@ -57,11 +79,16 @@ static const Parameter s_params[] =
     { "packet", Parameter::PT_TABLE, s_packet_params, nullptr,
       "packet latency" },
 
+    { "rule", Parameter::PT_TABLE, s_rule_params, nullptr,
+      "rule latency" },
+
     { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
 };
 
 static const RuleMap latency_rules[] =
 {
+    { LATENCY_EVENT_RULE_TREE_SUSPENDED, "rule tree suspended due to latency" },
+    { LATENCY_EVENT_RULE_TREE_ENABLED, "rule tree re-enabled after suspend timeout" },
     { LATENCY_EVENT_PACKET_FASTPATHED, "packet fastpathed due to latency" },
 
     { 0, nullptr }
@@ -71,8 +98,11 @@ THREAD_LOCAL LatencyStats latency_stats;
 
 static const PegInfo latency_pegs[] =
 {
-    { "packets", "total packets monitored" },
-    { "timeouts", "packets that timed out" },
+    { "total_packets", "total packets monitored" },
+    { "packet_timeouts", "packets that timed out" },
+    { "total_rule_evals", "total rule evals monitored" },
+    { "rule_eval_timeouts", "rule evals that timed out" },
+    { "rule_tree_enables", "rule tree re-enables" },
     { nullptr, nullptr }
 };
 
@@ -85,10 +115,7 @@ static inline bool latency_set(Value& v, PacketLatencyConfig& config)
     using std::chrono::duration_cast;
     using std::chrono::microseconds;
 
-    if ( v.is("enable") )
-        config.enable = v.get_bool();
-
-    else if ( v.is("max_time") )
+    if ( v.is("max_time") )
         config.max_time =
             duration_cast<decltype(config.max_time)>(microseconds(v.get_long()));
 
@@ -105,6 +132,36 @@ static inline bool latency_set(Value& v, PacketLatencyConfig& config)
     return true;
 }
 
+static inline bool latency_set(Value& v, RuleLatencyConfig& config)
+{
+    using std::chrono::duration_cast;
+    using std::chrono::microseconds;
+    using std::chrono::milliseconds;
+
+    if ( v.is("max_time") )
+        config.max_time =
+            duration_cast<decltype(config.max_time)>(microseconds(v.get_long()));
+
+    else if ( v.is("suspend") )
+        config.suspend = v.get_bool();
+
+    else if ( v.is("suspend_threshold") )
+        config.suspend_threshold = v.get_long();
+
+    else if ( v.is("max_suspend_time") )
+        config.max_suspend_time =
+            duration_cast<decltype(config.max_time)>(milliseconds(v.get_long()));
+
+    else if ( v.is("action") )
+        config.action =
+            static_cast<decltype(config.action)>(v.get_long());
+
+    else
+        return false;
+
+    return true;
+}
+
 LatencyModule::LatencyModule() :
     Module(s_name, s_help, s_params)
 { }
@@ -112,9 +169,14 @@ LatencyModule::LatencyModule() :
 bool LatencyModule::set(const char* fqn, Value& v, SnortConfig* sc)
 {
     const char* slp = "latency.packet";
+    const char* slr = "latency.rule";
 
     if ( !strncmp(fqn, slp, strlen(slp)) )
         return latency_set(v, sc->latency->packet_latency);
+
+    else if ( !strncmp(fqn, slr, strlen(slr)) )
+        return latency_set(v, sc->latency->rule_latency);
+
     else
         return false;
 
index 6caa8eeb4b7d05209caed4c83cc574b4a8df275b..ddb2e8b92e7fd7a52a23d1ddff4e83dfb13dd0fd 100644 (file)
@@ -24,6 +24,8 @@
 #define GID_LATENCY 134
 
 // SIDs
+#define LATENCY_EVENT_RULE_TREE_SUSPENDED   1
+#define LATENCY_EVENT_RULE_TREE_ENABLED     2
 #define LATENCY_EVENT_PACKET_FASTPATHED     3
 
 #endif
index cdedf0bbdd90c2721a872d556944f498915281a0..9b317e3165636bdfc2303e1bc8b9ca2c229ee9f7 100644 (file)
 
 struct LatencyStats
 {
-    PegCount packets;
-    PegCount timeouts;
+    PegCount total_packets;
+    PegCount packet_timeouts;
+    PegCount total_rule_evals;
+    PegCount rule_eval_timeouts;
+    PegCount rule_tree_enables;
 };
 
 extern THREAD_LOCAL LatencyStats latency_stats;
index 3568053f12a06817462787162bceb9f7673264b7..9e8168bb406a9b87f564a2ff6a4500740fce0684 100644 (file)
@@ -39,8 +39,6 @@ public:
     bool timed_out() const
     { return elapsed() > max_time; }
 
-    bool marked = false;
-
 private:
     duration max_time;
     Stopwatch<Clock> sw;
index 0cf48a333fad34c42302bb9b283d5183957c4891..5f8878ca87260db575c28447d8f539e9d2e29c4a 100644 (file)
@@ -21,7 +21,6 @@
 #include "packet_latency.h"
 
 #include <cassert>
-#include <mutex>
 #include <sstream>
 #include <vector>
 
@@ -59,6 +58,16 @@ struct Event
     typename DefaultClock::duration elapsed;
 };
 
+template<typename Clock>
+class PacketTimer : public LatencyTimer<Clock>
+{
+public:
+    PacketTimer(typename Clock::duration d) :
+        LatencyTimer<Clock>(d) { }
+
+    bool marked_as_fastpathed = false;
+};
+
 using ConfigWrapper = ReferenceWrapper<PacketLatencyConfig>;
 using EventHandler = EventingWrapper<Event>;
 
@@ -78,9 +87,9 @@ static inline std::ostream& operator<<(std::ostream& os, const Event& e)
 
     os << "latency: packet timed out";
     if ( e.fastpathed )
-        os << " (fastpathed)";
-    else
-        os << ": ";
+        os << " (fastpathed)";
+
+    os << ": ";
 
     os << duration_cast<microseconds>(e.elapsed).count() << " usec, [";
     os << e.packet->ptrs.ip_api.get_src() << " -> " <<
@@ -104,7 +113,9 @@ public:
     bool fastpath();
 
 private:
-    std::vector<LatencyTimer<Clock>> timers;
+    // FIXIT-H J use custom struct instead of std::pair for better semantics
+    // std::vector<std::pair<LatencyTimer<Clock>, bool>> contexts;
+    std::vector<PacketTimer<Clock>> timers;
     const ConfigWrapper& config;
     EventHandler& event_handler;
     EventHandler& log_handler;
@@ -119,8 +130,8 @@ template<typename Clock>
 inline void Impl<Clock>::push()
 {
     using std::chrono::duration_cast;
-    timers.push_back(duration_cast<typename Clock::duration>(
-        config->max_time));
+    auto max_time = duration_cast<typename Clock::duration>(config->max_time);
+    timers.emplace_back(max_time);
 }
 
 template<typename Clock>
@@ -128,17 +139,20 @@ inline bool Impl<Clock>::pop(const Packet* p)
 {
     assert(!timers.empty());
     const auto& timer = timers.back();
-    // timer.mark implies fastpath-related timeout
-    bool timed_out = timer.marked;
+
+    auto timed_out = timer.marked_as_fastpathed;
 
     if ( timer.timed_out() )
     {
-        Event e { p, timed_out, timers.back().elapsed() };
+        timed_out = true;
+
+        // timer.mark implies fastpath-related timeout
+        Event e { p, timer.marked_as_fastpathed, timer.elapsed() };
 
         if ( config->action & PacketLatencyConfig::LOG )
             log_handler.handle(e);
 
-        if ( timed_out and (config->action & PacketLatencyConfig::ALERT) )
+        if ( timer.marked_as_fastpathed and (config->action & PacketLatencyConfig::ALERT) )
             event_handler.handle(e);
     }
 
@@ -154,10 +168,14 @@ inline bool Impl<Clock>::fastpath()
 
     assert(!timers.empty());
     auto& timer = timers.back();
-    if ( timer.timed_out() )
-        timer.marked = true;
 
-    return timer.marked;
+    if ( !timer.marked_as_fastpathed )
+    {
+        if ( timer.timed_out() )
+            timer.marked_as_fastpathed = true;
+    }
+
+    return timer.marked_as_fastpathed;
 }
 
 // -----------------------------------------------------------------------------
@@ -206,25 +224,25 @@ static inline Impl<>& get_impl()
 
 void PacketLatency::push()
 {
-    if ( packet_latency::config->enable )
+    if ( packet_latency::config->enabled() )
     {
         packet_latency::get_impl().push();
-        ++latency_stats.packets;
+        ++latency_stats.total_packets;
     }
 }
 
 void PacketLatency::pop(const Packet* p)
 {
-    if ( packet_latency::config->enable )
+    if ( packet_latency::config->enabled() )
     {
         if ( packet_latency::get_impl().pop(p) )
-            ++latency_stats.timeouts;
+            ++latency_stats.packet_timeouts;
     }
 }
 
 bool PacketLatency::fastpath()
 {
-    if ( packet_latency::config->enable )
+    if ( packet_latency::config->enabled() )
         return packet_latency::get_impl().fastpath();
 
     return false;
@@ -329,7 +347,7 @@ TEST_CASE ( "packet latency impl", "[latency]" )
             MockClock::inc(config.config.max_time + 1_ticks);
 
             CHECK_FALSE( impl.fastpath() );
-            CHECK_FALSE( impl.pop(nullptr) );
+            CHECK( impl.pop(nullptr) );
 
             CHECK( event_handler.count == 0 );
             CHECK( log_handler.count == 1 );
index f0af5c4d849de8df30b68a6e2b722e4343e6e994..0f1410795a5f8cd9ac0591831d6a062f72a9c1b4 100644 (file)
 
 struct PacketLatencyConfig
 {
-    bool enable = false;
-    hr_duration max_time = 0_ticks;
-    bool fastpath = false;
-    enum Action : uint8_t
+    enum Action
     {
         NONE = 0x00,
         ALERT = 0x01,
         LOG = 0x02,
         ALERT_AND_LOG = ALERT | LOG
-    } action = NONE;
+    };
+
+    hr_duration max_time = 0_ticks;
+    bool fastpath = false;
+    Action action = NONE;
+
+    bool enabled() const { return max_time > 0_ticks; }
 };
 
 #endif
diff --git a/src/latency/rule_latency.cc b/src/latency/rule_latency.cc
new file mode 100644 (file)
index 0000000..39ed195
--- /dev/null
@@ -0,0 +1,587 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2016-2016 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.
+//--------------------------------------------------------------------------
+
+// rule_latency.cc author Joel Cornett <jocornet@cisco.com>
+
+#include "rule_latency.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cassert>
+
+#include "detection/detection_options.h"
+#include "events/event_queue.h"
+#include "log/messages.h"
+#include "main/snort_config.h"
+#include "latency_config.h"
+#include "latency_rules.h"
+#include "latency_stats.h"
+#include "latency_timer.h"
+#include "latency_util.h"
+
+#ifdef UNIT_TEST
+#include "catch/catch.hpp"
+#endif
+
+namespace rule_latency
+{
+// -----------------------------------------------------------------------------
+// helpers
+// -----------------------------------------------------------------------------
+
+using DefaultClock = hr_clock;
+
+struct Event
+{
+    enum Type
+    {
+        EVENT_ENABLED,
+        EVENT_TIMED_OUT,
+        EVENT_SUSPENDED
+    };
+
+    Type type;
+    typename DefaultClock::duration elapsed;
+    detection_option_tree_root_t* root;
+};
+
+template<typename Clock>
+class RuleTimer : public LatencyTimer<Clock>
+{
+public:
+    RuleTimer(typename Clock::duration d, detection_option_tree_root_t* root) :
+        LatencyTimer<Clock>(d), root(root) { }
+
+    detection_option_tree_root_t* root;
+};
+
+using ConfigWrapper = ReferenceWrapper<RuleLatencyConfig>;
+using EventHandler = EventingWrapper<Event>;
+
+static inline std::ostream& operator<<(std::ostream& os, const Event& e)
+{
+    using std::chrono::duration_cast;
+    using std::chrono::microseconds;
+
+    os << "latency: ";
+
+    if ( e.type == Event::EVENT_ENABLED ) 
+        os << "rule tree enabled: ";
+
+    else
+    {
+        os << "rule tree timed out";
+        if ( e.type == Event::EVENT_SUSPENDED )
+            os << " (suspended)";
+
+        os << ": ";
+
+        os << duration_cast<microseconds>(e.elapsed).count() << " usec, ";
+    }
+
+    // FIXIT-L J seeing the address of the dot root is not particularly helpful
+    // except during debugging (ported from legacy ppm)
+    os << "[" << e.root << "]";
+
+    return os;
+}
+
+// -----------------------------------------------------------------------------
+// rule tree interface
+// -----------------------------------------------------------------------------
+
+// goes in a static structure so we can templatize Impl
+struct DefaultRuleInterface
+{
+    static bool is_enabled(const detection_option_tree_root_t& root)
+    { return root.latency_state[get_instance_id()].enabled; }
+
+    // return true if rule was *reenabled*
+    template<typename Duration, typename Time>
+    static bool reenable(detection_option_tree_root_t& root, Duration max_suspend_time,
+        Time cur_time)
+    {
+        auto& state = root.latency_state[get_instance_id()];
+        if ( !state.enabled && (cur_time - state.suspend_time > max_suspend_time) )
+        {
+            state.enable();
+            return true;
+        }
+
+        return false;
+    }
+
+    // FIXIT-L J traversing the children 2 separate times with timeout & suspend is inefficient
+    static inline void timeout(detection_option_tree_root_t& root)
+    {
+        ++root.latency_state[get_instance_id()].timeouts;
+        for ( int i = 0; i < root.num_children; ++i )
+            // FIXIT-L J rename to something like latency_timeout_count
+            ++root.children[i]->state[get_instance_id()].latency_timeouts;
+    }
+
+    template<typename Time>
+    static bool suspend(detection_option_tree_root_t& root, unsigned threshold,
+        Time cur_time)
+    {
+        auto& state = root.latency_state[get_instance_id()];
+        if ( state.timeouts >= threshold )
+        {
+            state.suspend(cur_time);
+            for ( int i = 0; i < root.num_children; ++i )
+                ++root.children[i]->state[get_instance_id()].latency_suspends;
+
+            return true;
+        }
+
+        return false;
+    }
+
+    template<typename Time>
+    static bool timeout_and_suspend(detection_option_tree_root_t& root, unsigned threshold,
+        Time time, bool do_suspend)
+    {
+        auto& state = root.latency_state[get_instance_id()];
+
+        ++state.timeouts;
+
+        // the separate loops in each branch are so we can avoid iterating
+        // over the children twice in the suspend case
+
+        if ( do_suspend )
+        {
+            if ( state.timeouts >= threshold )
+            {
+                state.suspend(time);
+
+                for ( int i = 0; i < root.num_children; ++i )
+                {
+                    auto& child_state = root.children[i]->state[get_instance_id()];
+                    // FIXIT-L J rename to something like latency_timeout_count
+                    ++child_state.latency_timeouts;
+                    ++child_state.latency_suspends;
+                }
+
+                return true;
+            }
+        }
+
+        else
+        {
+            for ( int i = 0; i < root.num_children; ++i )
+            {
+                // FIXIT-L J rename to something like latency_timeout_count
+                ++root.children[i]->state[get_instance_id()].latency_timeouts;
+            }
+        }
+
+        return false;
+    }
+};
+
+
+// -----------------------------------------------------------------------------
+// implementation
+// -----------------------------------------------------------------------------
+
+template<typename Clock = DefaultClock, typename RuleTree = DefaultRuleInterface>
+class Impl
+{
+public:
+    Impl(const ConfigWrapper&, EventHandler&, EventHandler&);
+
+    bool push(detection_option_tree_root_t*);
+    bool pop();
+    // FIXIT-L J should this logic be inverted to suspended()?
+    // returns whether current rule tree is enabled
+    bool enabled() const;
+
+private:
+    void handle(const Event&);
+
+    std::vector<RuleTimer<Clock>> timers;
+    const ConfigWrapper& config;
+    EventHandler& event_handler;
+    EventHandler& log_handler;
+};
+
+template<typename Clock, typename RuleTree>
+inline Impl<Clock, RuleTree>::Impl(const ConfigWrapper& cfg, EventHandler& eh, EventHandler& lh) :
+    config(cfg), event_handler(eh), log_handler(lh)
+{ }
+
+template<typename Clock, typename RuleTree>
+inline bool Impl<Clock, RuleTree>::push(detection_option_tree_root_t* root)
+{
+    assert(root);
+
+    // FIXIT-L J rule timer is pushed even if rule is not enabled (no visible side-effects)
+    timers.emplace_back(config->max_time, root);
+
+    if ( config->allow_reenable() )
+    {
+        if ( RuleTree::reenable(*root, config->max_suspend_time, Clock::now()) )
+        {
+            Event e {
+                Event::EVENT_ENABLED,
+                typename Clock::duration(0),
+                root
+            };
+
+            handle(e);
+            return true;
+        }
+    }
+
+    return false;
+}
+
+template<typename Clock, typename RuleTree>
+inline bool Impl<Clock, RuleTree>::pop()
+{
+    assert(!timers.empty());
+    const auto& timer = timers.back();
+
+    auto timed_out = timer.timed_out();
+    if ( timed_out )
+    {
+        auto suspended = RuleTree::timeout_and_suspend(*timer.root, config->suspend_threshold,
+            Clock::now(), config->suspend);
+
+        // FIXIT-L J first field doesn't matter
+        Event e {
+            suspended ? Event::EVENT_SUSPENDED : Event::EVENT_TIMED_OUT,
+            timer.elapsed(),
+            timer.root
+        };
+
+        handle(e);
+    }
+
+    timers.pop_back();
+    return timed_out;
+}
+
+template<typename Clock, typename RuleTree>
+inline bool Impl<Clock, RuleTree>::enabled() const
+{
+    if ( !config->suspend )
+        return true;
+
+    assert(!timers.empty());
+    return RuleTree::is_enabled(*timers.back().root);
+}
+
+template<typename Clock, typename RuleTree>
+inline void Impl<Clock, RuleTree>::handle(const Event& e)
+{
+    if ( config->action & RuleLatencyConfig::LOG )
+        log_handler.handle(e);
+
+    if ( config->action & RuleLatencyConfig::ALERT )
+        event_handler.handle(e);
+}
+
+// -----------------------------------------------------------------------------
+// static variables
+// -----------------------------------------------------------------------------
+
+static struct SnortConfigWrapper : ConfigWrapper
+{
+    const RuleLatencyConfig* operator->() const override
+    { return &snort_conf->latency->rule_latency; }
+
+} config;
+
+static struct SnortEventHandler : EventHandler
+{
+    void handle(const Event& e) override
+    {
+        switch ( e.type )
+        {
+            case Event::EVENT_ENABLED:
+                SnortEventqAdd(GID_LATENCY, LATENCY_EVENT_RULE_TREE_ENABLED);
+                break;
+
+            case Event::EVENT_SUSPENDED:
+                SnortEventqAdd(GID_LATENCY, LATENCY_EVENT_RULE_TREE_SUSPENDED);
+                break;
+
+            default:
+                break;
+        }
+    }
+} event_handler;
+
+static struct SnortLogHandler : EventHandler
+{
+    void handle(const Event& e) override
+    {
+        std::ostringstream ss;
+        ss << e;
+        LogMessage("%s\n", ss.str().c_str());
+    }
+} log_handler;
+
+static THREAD_LOCAL Impl<>* impl = nullptr;
+
+static inline Impl<>& get_impl()
+{
+    if ( !impl )
+        impl = new Impl<>(config, event_handler, log_handler);
+
+    return *impl;
+}
+
+} // namespace rule_latency
+
+// -----------------------------------------------------------------------------
+// rule latency interface
+// -----------------------------------------------------------------------------
+
+void RuleLatency::push(detection_option_tree_root_t* root)
+{
+    if ( rule_latency::config->enabled() )
+    {
+        if ( rule_latency::get_impl().push(root) )
+            ++latency_stats.rule_tree_enables;
+
+        ++latency_stats.total_rule_evals;
+    }
+}
+
+void RuleLatency::pop()
+{
+    if ( rule_latency::config->enabled() )
+    {
+        if ( rule_latency::get_impl().pop() )
+            ++latency_stats.rule_eval_timeouts;
+    }
+}
+
+bool RuleLatency::enabled()
+{
+    if ( rule_latency::config->enabled() )
+        return rule_latency::get_impl().enabled();
+
+    return true;
+}
+
+// -----------------------------------------------------------------------------
+// unit tests
+// -----------------------------------------------------------------------------
+
+#ifdef UNIT_TEST
+
+namespace t_rule_latency
+{
+
+struct MockConfigWrapper : rule_latency::ConfigWrapper
+{
+    RuleLatencyConfig config;
+
+    const RuleLatencyConfig* operator->() const override
+    { return &config; }
+};
+
+struct EventHandlerSpy : rule_latency::EventHandler
+{
+    unsigned count = 0;
+    void handle(const rule_latency::Event&) override
+    { ++count; }
+};
+
+struct MockClock : ClockTraits<hr_clock>
+{
+    static hr_time t;
+
+    static void reset()
+    { t = hr_time(0_ticks); }
+
+    static void inc(hr_duration d = 1_ticks)
+    { t += d; }
+
+    static hr_time now()
+    { return t; }
+};
+
+hr_time MockClock::t = hr_time(0_ticks);
+
+struct RuleInterfaceSpy
+{
+    static bool is_enabled_result;
+    static bool is_enabled_called;
+    static bool reenable_result;
+    static bool reenable_called;
+    static bool timeout_and_suspend_result;
+    static bool timeout_and_suspend_called;
+
+    static void reset()
+    {
+        is_enabled_result = false;
+        is_enabled_called = false;
+        reenable_result = false;
+        reenable_called = false;
+        timeout_and_suspend_result = false;
+        timeout_and_suspend_called = false;
+    }
+
+    static bool is_enabled(const detection_option_tree_root_t&)
+    { is_enabled_called = true; return is_enabled_result; }
+
+    template<typename Duration, typename Time>
+    static bool reenable(detection_option_tree_root_t&, Duration, Time)
+    { reenable_called = true; return reenable_result; }
+
+    template<typename Time>
+    static bool timeout_and_suspend(detection_option_tree_root_t&, unsigned, Time, bool)
+    { timeout_and_suspend_called = true; return timeout_and_suspend_result; }
+};
+
+bool RuleInterfaceSpy::is_enabled_result = false;
+bool RuleInterfaceSpy::is_enabled_called = false;
+bool RuleInterfaceSpy::reenable_result = false;
+bool RuleInterfaceSpy::reenable_called = false;
+bool RuleInterfaceSpy::timeout_and_suspend_result = false;
+bool RuleInterfaceSpy::timeout_and_suspend_called = false;
+
+} // namespace t_rule_latency
+
+TEST_CASE ( "rule latency impl", "[latency]" )
+{
+    using namespace t_rule_latency;
+
+    MockConfigWrapper config;
+    EventHandlerSpy event_handler;
+    EventHandlerSpy log_handler;
+
+    MockClock::reset();
+    RuleInterfaceSpy::reset();
+
+    config.config.action = RuleLatencyConfig::ALERT_AND_LOG;
+
+    detection_option_tree_root_t root;
+
+    rule_latency::Impl<MockClock, RuleInterfaceSpy> impl(config, event_handler, log_handler);
+
+    SECTION( "push" )
+    {
+        SECTION( "reenable allowed" )
+        {
+            config.config.max_suspend_time = 1_ticks;
+
+            SECTION( "push rule" )
+            {
+                CHECK_FALSE( impl.push(&root) );
+                CHECK( log_handler.count == 0 );
+                CHECK( event_handler.count == 0 );
+                CHECK( RuleInterfaceSpy::reenable_called );
+            }
+
+            SECTION( "push rule -- reenabled" )
+            {
+                RuleInterfaceSpy::reenable_result = true;
+
+                CHECK( impl.push(&root) );
+                CHECK( log_handler.count == 1 );
+                CHECK( event_handler.count == 1 );
+                CHECK( RuleInterfaceSpy::reenable_called );
+            }
+        }
+
+        SECTION( "reenable not allowed" )
+        {
+            config.config.max_suspend_time = 0_ticks;
+
+            SECTION( "push rule" )
+            {
+                CHECK_FALSE( impl.push(&root) );
+                CHECK( log_handler.count == 0 );
+                CHECK( event_handler.count == 0 );
+                CHECK_FALSE( RuleInterfaceSpy::reenable_called );
+            }
+        }
+    }
+
+    SECTION( "enabled" )
+    {
+        RuleInterfaceSpy::is_enabled_result = false;
+
+        impl.push(&root);
+
+        SECTION( "suspending of rules disabled" )
+        {
+            config.config.suspend = false;
+
+            CHECK( impl.enabled() );
+            CHECK_FALSE( RuleInterfaceSpy::is_enabled_called );
+        }
+
+        SECTION( "suspend of rules enabled" )
+        {
+            config.config.suspend = true;
+
+            CHECK_FALSE( impl.enabled() );
+            CHECK( RuleInterfaceSpy::is_enabled_called );
+        }
+    }
+
+    SECTION( "pop" )
+    {
+        config.config.max_time = 1_ticks;
+
+        impl.push(&root);
+
+        SECTION( "rule timeout" )
+        {
+            MockClock::inc(2_ticks);
+
+            SECTION( "rule suspended" )
+            {
+                RuleInterfaceSpy::timeout_and_suspend_result = true;
+
+                CHECK( impl.pop() );
+                CHECK( log_handler.count == 1 );
+                CHECK( event_handler.count == 1 );
+                CHECK( RuleInterfaceSpy::timeout_and_suspend_called );
+            }
+
+            SECTION( "rule not suspended" )
+            {
+                RuleInterfaceSpy::timeout_and_suspend_result = false;
+
+                CHECK( impl.pop() );
+                CHECK( log_handler.count == 1 );
+                CHECK( event_handler.count == 1 );
+                CHECK( RuleInterfaceSpy::timeout_and_suspend_called );
+            }
+        }
+
+        SECTION( "no rule timeout" )
+        {
+            CHECK_FALSE( impl.pop() );
+            CHECK( log_handler.count == 0 );
+            CHECK( event_handler.count == 0 );
+            CHECK_FALSE( RuleInterfaceSpy::timeout_and_suspend_called );
+        }
+    }
+}
+
+#endif
diff --git a/src/latency/rule_latency.h b/src/latency/rule_latency.h
new file mode 100644 (file)
index 0000000..087ef7e
--- /dev/null
@@ -0,0 +1,44 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2016-2016 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.
+//--------------------------------------------------------------------------
+
+// rule_latency.h author Joel Cornett <jocornet@cisco.com>
+
+#ifndef RULE_LATENCY_H
+#define RULE_LATENCY_H
+
+struct detection_option_tree_root_t;
+
+class RuleLatency
+{
+public:
+    static void push(detection_option_tree_root_t*);
+    static void pop();
+    static bool enabled();
+
+    class Context
+    {
+    public:
+        Context(detection_option_tree_root_t* root)
+        { RuleLatency::push(root); }
+
+        ~Context()
+        { RuleLatency::pop(); }
+    };
+};
+
+#endif
diff --git a/src/latency/rule_latency_config.h b/src/latency/rule_latency_config.h
new file mode 100644 (file)
index 0000000..ed18ffb
--- /dev/null
@@ -0,0 +1,47 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2016-2016 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.
+//--------------------------------------------------------------------------
+
+// rule_latency_config.h author Joel Cornett <jocornet@cisco.com>
+
+#ifndef RULE_LATENCY_CONFIG_H
+#define RULE_LATENCY_CONFIG_H
+
+#include <cstdint>
+#include "time/clock_defs.h"
+
+struct RuleLatencyConfig
+{
+    enum Action
+    {
+        NONE = 0x00,
+        ALERT = 0x01,
+        LOG = 0x02,
+        ALERT_AND_LOG = ALERT | LOG
+    };
+
+    hr_duration max_time = 0_ticks;
+    bool suspend = false;
+    unsigned suspend_threshold = 0;
+    hr_duration max_suspend_time = 0_ticks;
+    Action action = NONE;
+
+    bool enabled() const { return max_time > 0_ticks; }
+    bool allow_reenable() const { return max_suspend_time > 0_ticks; }
+};
+
+#endif
diff --git a/src/latency/rule_latency_state.h b/src/latency/rule_latency_state.h
new file mode 100644 (file)
index 0000000..f928de8
--- /dev/null
@@ -0,0 +1,46 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2016-2016 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.
+//--------------------------------------------------------------------------
+
+// rule_latency_state.h author Joel Cornett <jocornet@cisco.com>
+
+#ifndef RULE_LATENCY_STATE_H
+#define RULE_LATENCY_STATE_H
+
+#include "time/clock_defs.h"
+
+struct RuleLatencyState
+{
+    hr_time suspend_time { };
+    unsigned timeouts = 0;
+    // FIXIT-L J should this be inverted to suspended?
+    bool enabled = true;
+
+    void enable()
+    {
+        timeouts = 0;
+        enabled = true;
+    }
+
+    void suspend(hr_time cur_time)
+    {
+        suspend_time = cur_time;
+        enabled = false;
+    }
+};
+
+#endif
index 52d5378fe5158ac61d4fc0f55d94625a0d424949..b40bc04c7befd1354291a2091688535e92f764ce 100644 (file)
@@ -73,7 +73,8 @@ static const StatsTable::Field fields[] =
     { "avg/check", 10, '\0', 1, std::ios_base::fmtflags() },
     { "avg/match", 10, '\0', 1, std::ios_base::fmtflags() },
     { "avg/non-match", 14, '\0', 1, std::ios_base::fmtflags() },
-    { "disables", 9, '\0', 0, std::ios_base::fmtflags() },
+    { "timeouts", 9, '\0', 0, std::ios_base::fmtflags() },
+    { "suspends", 9, '\0', 0, std::ios_base::fmtflags() },
     { nullptr, 0, '\0', 0, std::ios_base::fmtflags() }
 };
 
@@ -103,8 +104,11 @@ struct View
     uint64_t alerts() const
     { return state.alerts; }
 
-    uint64_t ppm_disable_count() const
-    { return state.ppm_disable_cnt; }
+    uint64_t timeouts() const
+    { return state.latency_timeouts; }
+
+    uint64_t suspends() const
+    { return state.latency_suspends; }
 
     hr_duration time_per(hr_duration d, uint64_t v) const
     {
@@ -235,7 +239,8 @@ static void print_single_entry(const View& v, unsigned n)
         table << duration_cast<microseconds>(v.avg_match()).count(); // avg/match
         table << duration_cast<microseconds>(v.avg_no_match()).count(); // avg/non-match
 
-        table << v.ppm_disable_count(); // disables
+        table << v.timeouts();
+        table << v.suspends();
     }
 
     LogMessage("%s", ss.str().c_str());
@@ -314,7 +319,7 @@ void reset_rule_profiler_stats()
         for ( unsigned i = 0; i < get_instance_max(); ++i )
         {
             auto& state = otn->state[i];
-            state.reset();
+            state = OtnState();
         }
     }
 }
@@ -352,18 +357,14 @@ static inline OtnState make_otn_state(
     hr_duration elapsed, hr_duration elapsed_match,
     uint64_t checks, uint64_t matches)
 {
-    return {
-        elapsed,
-        elapsed_match,
-        0_ticks,
-
-        checks,
-        matches,
-        0,
-        0,
-        0,
-        0
-    };
+    OtnState state;
+
+    state.elapsed = elapsed;
+    state.elapsed_match = elapsed_match;
+    state.checks = checks;
+    state.matches = matches;
+
+    return state;
 }
 
 static inline rule_stats::View make_rule_entry(
@@ -381,11 +382,27 @@ static void avoid_optimization()
 
 TEST_CASE( "otn state", "[profiler][rule_profiler]" )
 {
-    OtnState state_a = { 1_ticks, 2_ticks, 3_ticks, 1, 2, 3, 4, 0, 0};
+    OtnState state_a;
+
+    state_a.elapsed = 1_ticks;
+    state_a.elapsed_match = 2_ticks;
+    state_a.elapsed_no_match = 2_ticks;
+    state_a.checks = 1;
+    state_a.matches = 2;
+    state_a.noalerts = 3;
+    state_a.alerts = 4;
 
     SECTION( "incremental addition" )
     {
-        OtnState state_b = { 4_ticks, 5_ticks, 6_ticks, 5, 6, 7, 8, 0, 0};
+        OtnState state_b;
+
+        state_b.elapsed = 4_ticks;
+        state_b.elapsed_match = 5_ticks;
+        state_b.elapsed_no_match = 6_ticks;
+        state_b.checks = 5;
+        state_b.matches = 6;
+        state_b.noalerts = 7;
+        state_b.alerts = 8;
 
         state_a += state_b;
 
@@ -398,7 +415,8 @@ TEST_CASE( "otn state", "[profiler][rule_profiler]" )
 
     SECTION( "reset" )
     {
-        state_a.reset();
+        state_a = OtnState();
+
         CHECK( state_a.elapsed == 0_ticks );
         CHECK( state_a.elapsed_match == 0_ticks );
         CHECK( state_a.checks == 0 );
@@ -427,7 +445,8 @@ TEST_CASE( "rule entry", "[profiler][rule_profiler]" )
     SigInfo sig_info;
     auto entry = make_rule_entry(3_ticks, 2_ticks, 3, 2);
     entry.state.alerts = 77;
-    entry.state.ppm_disable_cnt = 5;
+    entry.state.latency_timeouts = 5;
+    entry.state.latency_suspends = 2;
 
     SECTION( "copy assignment" )
     {
@@ -482,11 +501,17 @@ TEST_CASE( "rule entry", "[profiler][rule_profiler]" )
         CHECK( entry.alerts() == 77 );
     }
 
-    SECTION( "ppm_disable_count" )
+    SECTION( "timeouts" )
+    {
+        CHECK( entry.timeouts() == 5 );
+    }
+
+    SECTION( "suspends" )
     {
-        CHECK( entry.ppm_disable_count() == 5 );
+        CHECK( entry.suspends() == 2 );
     }
 
+
     SECTION( "avg_match" )
     {
         auto ticks = entry.avg_match();
index 7dc597c4f9d75f95199c4b25514be6b39463ce52..cc2ad1321765f4939ad486cd0900435192bd47c9 100644 (file)
@@ -328,8 +328,6 @@ void DropStats()
     LogLabel("Summary Statistics");
     show_stats((PegCount*)&gpc, pc_names, array_size(pc_names)-1, "detection");
 
-    PPM_PRINT_SUMMARY(snort_conf->ppm_cfg);
-
     proc_stats.attribute_table_hosts = SFAT_NumberOfHosts();
     show_stats((PegCount*)&proc_stats, proc_names, array_size(proc_names)-1, "process");