]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #4719: flow: implement a per flow check of the packet timestamp and...
authorDavis McPherson -X (davmcphe - XORIANT CORPORATION at Cisco) <davmcphe@cisco.com>
Fri, 9 May 2025 20:24:39 +0000 (20:24 +0000)
committerSteven Baigal (sbaigal) <sbaigal@cisco.com>
Fri, 9 May 2025 20:24:39 +0000 (20:24 +0000)
Merge in SNORT/snort3 from ~DAVMCPHE/snort3:drop_stale_packets to master

Squashed commit of the following:

commit 27a0456758a6713b2c5cdc94f3d2c59eaa9aa9dc
Author: davis mcpherson <davmcphe@cisco.com>
Date:   Mon May 5 23:18:17 2025 -0400

    snort2lua: add include for cstdint to provide standard c++ integer types

commit 63de2df3d4e5c871a0069b646c0a5c06588d9aa7
Author: davis mcpherson <davmcphe@cisco.com>
Date:   Fri Apr 4 14:45:29 2025 -0400

    flow: implement a per flow check of the packet timestamp and drop packets if the timestamp is earlier than the timestamp of the previous packet

    flow: always count stale packets, only drop if that is enabled by config, set default value for drop_stale_packets to false (disabled)

18 files changed:
src/flow/flow.h
src/flow/flow_control.cc
src/flow/session.h
src/flow/test/flow_cache_test.cc
src/flow/test/flow_control_test.cc
src/main/snort_config.h
src/stream/base/stream_module.cc
src/stream/base/stream_module.h
src/stream/icmp/icmp_session.h
src/stream/ip/ip_session.h
src/stream/tcp/tcp_session.h
src/stream/udp/udp_session.cc
src/stream/udp/udp_session.h
tools/snort2lua/config_states/config_ignore_ports.cc
tools/snort2lua/preprocessor_states/pps_dcerpc_server.cc
tools/snort2lua/preprocessor_states/pps_frag3_engine.cc
tools/snort2lua/preprocessor_states/pps_stream5_tcp.cc
tools/snort2lua/rule_states/rule_gid_sid.cc

index 96be826f5293aefd26b67765c8151a99f5a0dc8b..ea789acaabc0d0e9ef004ea87ee484a7b3a06d27 100644 (file)
@@ -479,6 +479,8 @@ public:  // FIXIT-M privatize if possible
     uint64_t expire_time = 0;
 
     unsigned network_policy_id = 0;
+    struct timeval prev_packet_time = {0, 0};
+    
     unsigned inspection_policy_id = 0;
     unsigned ips_policy_id = 0;
     unsigned reload_id = 0;
index 9d03eb64e99d2c474194c29bbdb536645884063a..4de64434ec77a9becfcbb51667ff007ea7507709 100644 (file)
@@ -20,6 +20,8 @@
 #include "config.h"
 #endif
 
+#include <sys/time.h>
+
 #include <daq_common.h>
 
 #include "flow_control.h"
@@ -35,6 +37,7 @@
 #include "pub_sub/intrinsic_event_ids.h"
 #include "pub_sub/packet_events.h"
 #include "stream/stream.h"
+#include "utils/stats.h"
 #include "utils/util.h"
 
 #include "expect_cache.h"
@@ -401,6 +404,36 @@ static bool want_flow(PktType type, Packet* p)
     return false;
 }
 
+static void log_stale_packet(snort::Packet *p, snort::Flow *flow, bool drop_packet)
+{
+    char ts_flow[TIMEBUF_SIZE];
+    char ts_pkt[TIMEBUF_SIZE];
+    ts_print((const struct timeval *)&p->pkth->ts, ts_pkt);
+    ts_print((const struct timeval *)&flow->prev_packet_time, ts_flow);
+
+    if ( drop_packet )
+        PacketTracer::log("Flow: Dropping stale packet. current packet ts: %s < previous packet ts: %s.\n",
+                          ts_pkt, ts_flow);
+    else
+        PacketTracer::log("Flow: Detected stale packet, dropping disabled. current packet ts: %s < previous packet ts: %s.\n",
+                          ts_pkt, ts_flow);
+}
+
+static inline bool is_packet_stale(const Flow* flow, const Packet* p)
+{
+    return timercmp(&flow->prev_packet_time, &p->pkth->ts, >);
+}
+
+static void drop_stale_packet(snort::Packet *p, snort::Flow *flow)
+{
+    // This is a stale packet, ignore it.
+    p->active->set_drop_reason("snort");
+    p->active->drop_packet(p);
+    p->disable_inspect = true;
+    if ( PacketTracer::is_active() )
+        log_stale_packet(p, flow, true);
+}
+
 bool FlowControl::process(PktType type, Packet* p, bool* new_flow)
 {
     if ( !get_proto_session[to_utype(type)] )
@@ -410,8 +443,26 @@ bool FlowControl::process(PktType type, Packet* p, bool* new_flow)
     bool reversed = set_key(&key, p);
     Flow* flow = cache->find(&key);
 
-    if (flow)
+    if ( flow )
+    {
+        if ( !p->is_retry() and is_packet_stale(flow, p) )
+        {
+            flow->session->count_stale_packet();
+
+            if ( p->context->conf->drop_stale_packets() )
+            {
+                drop_stale_packet(p, flow);
+                return true;
+            }
+            else
+            {
+                if ( PacketTracer::is_active() )
+                    log_stale_packet(p, flow, false);
+            }
+        }
+        
         flow = stale_flow_cleanup(cache, flow, p);
+    }
 
     bool new_ha_flow = false;
     if ( !flow )
@@ -469,6 +520,7 @@ unsigned FlowControl::process(Flow* flow, Packet* p, bool new_ha_flow)
     unsigned news = 0;
 
     flow->previous_ssn_state = flow->ssn_state;
+    flow->prev_packet_time = p->pkth->ts;
 
     p->flow = flow;
     p->disable_inspect = flow->is_inspection_disabled();
index 79b894cea8ac8127d96e2a01df170eea37e1c793..8fe0003897ebe7031f943ed2cce109a03f369837 100644 (file)
@@ -79,6 +79,7 @@ public:
     virtual uint8_t missing_in_reassembled(uint8_t /*dir*/) const { return SSN_MISSING_NONE; }
 
     virtual bool set_packet_action_to_hold(snort::Packet*) { return false; }
+    virtual void count_stale_packet() { }
 
 protected:
     Session(snort::Flow* f) { flow = f; }
@@ -96,7 +97,8 @@ public:
     { CountType::SUM, "created", module " session trackers created" }, \
     { CountType::SUM, "released", module " session trackers released" }, \
     { CountType::SUM, "timeouts", module " session timeouts" }, \
-    { CountType::SUM, "prunes", module " session prunes" }
+    { CountType::SUM, "prunes", module " session prunes" }, \
+    { CountType::SUM, "stale_packets", module " stale packets" }
 
 // See above. Add to end of stats array.
 #define SESSION_STATS \
@@ -105,7 +107,8 @@ public:
     PegCount created; \
     PegCount released; \
     PegCount timeouts; \
-    PegCount prunes
+    PegCount prunes; \
+    PegCount stale_packets
 
 // Do not change the semantics of max. Max = the highest seen during the perf interval.
 // To obtain max over the entire run, determine the maximum of reported max pegs.
index 2ad175ec8a2619e316e70976d6caf6798bb8862d..5e83dc2f4a07e9cbfc282e712bbed58ad5202e08 100644 (file)
@@ -105,6 +105,7 @@ void Flow::set_client_initiate(Packet*) { }
 void Flow::set_direction(Packet*) { }
 void Flow::set_mpls_layer_per_dir(Packet*) { }
 void packet_gettimeofday(struct timeval* ) { }
+SO_PUBLIC void ts_print(const struct timeval*, char*, bool) { }
 
 time_t packet_time() { return 0; }
 
index 707c83f97da45f7a9a9d3e5b2568409add50a049..db361d13497d16fe45a171747df858f58ca89ef4 100644 (file)
@@ -88,6 +88,7 @@ bool ExpectCache::check(Packet*, Flow*) { return true; }
 Flow* HighAvailabilityManager::import(Packet&, FlowKey&) { return nullptr; }
 bool FlowCache::move_to_allowlist(snort::Flow*) { return true; }
 uint64_t FlowCache::get_lru_flow_count(uint8_t) const { return 0; }
+SO_PUBLIC void snort::ts_print(const struct timeval*, char*, bool) { }
 
 namespace snort
 {
index c35260cad4a682beb64108230d7dd350274b5585..015bb2f8bc35a0e51f64d391a5d43242749efff0 100644 (file)
@@ -78,6 +78,7 @@ enum RunFlag
 #ifdef SHELL
     RUN_FLAG__SHELL               = 0x02000000,
 #endif
+    RUN_FLAG__DROP_STALE_PACKETS  = 0x04000000,
 };
 
 enum OutputFlag
@@ -675,6 +676,9 @@ public:
     bool ip_frags_only() const
     { return (run_flags & RUN_FLAG__IP_FRAGS_ONLY) != 0; }
 
+    bool drop_stale_packets() const
+    { return (run_flags & RUN_FLAG__DROP_STALE_PACKETS) != 0; }
+    
     void clear_run_flags(RunFlag flag)
     { run_flags &= ~flag; }
 
index 8d2fe1692c138b88655803479acd65219bfa6e30..ce3ad5f39e10e0077be32a6193b17ebc2af73dca 100644 (file)
@@ -113,6 +113,9 @@ static const Parameter s_params[] =
 
     { "allowlist_cache", Parameter::PT_TABLE, allowlist_cache_params, nullptr, "configure allowlist cache" },
 
+    { "drop_stale_packets", Parameter::PT_BOOL, nullptr, "false",
+      "enable dropping of packets with stale timestamp" },
+
     FLOW_TYPE_TABLE("ip_cache",   "ip",   ip_params),
     FLOW_TYPE_TABLE("icmp_cache", "icmp", icmp_params),
     FLOW_TYPE_TABLE("tcp_cache",  "tcp",  tcp_params),
@@ -433,6 +436,15 @@ bool StreamModule::set(const char* fqn, Value& v, SnortConfig* c)
     else if ( !strcmp(fqn, "stream.udp_cache.idle_timeout") )
         config.flow_cache_cfg.proto[to_utype(PktType::UDP)].nominal_timeout = v.get_uint32();
 
+    else if ( v.is("drop_stale_packets") )
+    {
+        config.drop_stale_packets = v.get_bool();
+        if (config.drop_stale_packets)
+            c->set_run_flags(RUN_FLAG__DROP_STALE_PACKETS);
+        else
+            c->clear_run_flags(RUN_FLAG__DROP_STALE_PACKETS);
+        return true;
+    }
     else
     {
         assert(!strcmp(fqn, "stream.user_cache.idle_timeout"));
@@ -601,7 +613,8 @@ void StreamModuleConfig::show() const
     ConfigLogger::log_value("pruning_timeout", flow_cache_cfg.pruning_timeout);
     ConfigLogger::log_value("prune_flows", flow_cache_cfg.prune_flows);
     ConfigLogger::log_limit("require_3whs", hs_timeout, -1, hs_timeout < 0 ? hs_timeout : -1);
-
+    ConfigLogger::log_value("drop_stale_packets", drop_stale_packets ? "enabled" : "disabled");
+    
     for (int i = to_utype(PktType::IP); i < to_utype(PktType::PDU); ++i)
     {
         std::string tmp;
index 50e8f9e7225a2c5593a729011cbd25d571a5e410..1a1b78d27dd234eafe4d52f81219e3ec40a0d786 100644 (file)
@@ -125,7 +125,8 @@ struct StreamModuleConfig
 #endif
     uint32_t held_packet_timeout = 1000;  // in milliseconds
     int hs_timeout = -1;
-
+    bool drop_stale_packets = false;
+    
     void show() const;
 };
 
index 07aa405a151bb72aa041bfa54bed5e9c7696cfcc..9b126cb0511e4a899f9c71d382fac7a5000952a6 100644 (file)
 #define ICMP_SESSION_H
 
 #include <sys/time.h>
+
 #include "flow/session.h"
 
+#include "icmp_module.h"
+
 class IcmpSession : public Session
 {
 public:
@@ -31,7 +34,8 @@ public:
     bool setup(snort::Packet*) override;
     int process(snort::Packet*) override;
     void clear() override;
-
+    void count_stale_packet() override
+    { icmpStats.stale_packets++; }
 public:
     uint32_t echo_count = 0;
     struct timeval ssn_time = {};
index cfc285e6cd9fbfc5a26b210ec541b74053a60e2c..a5c4832a9eb29c7c14247a9d7b1daf4f1172eb44 100644 (file)
@@ -25,6 +25,8 @@
 struct Fragment;
 struct FragEngine;
 
+extern THREAD_LOCAL IpStats ip_stats;
+
 /* Only track a certain number of alerts per session */
 #define MAX_FRAG_ALERTS 8
 
@@ -84,12 +86,12 @@ public:
 
     bool add_alert(snort::Packet*, uint32_t gid, uint32_t sid) override;
     bool check_alerted(snort::Packet*, uint32_t gid, uint32_t sid) override;
+    void count_stale_packet() override
+    { ip_stats.stale_packets++; }
 
 public:
     FragTracker tracker = {};
 };
 
-extern THREAD_LOCAL IpStats ip_stats;
-
 #endif
 
index cf74001fbed7ebc002e1bea6b3fcd99dd6846ac2..e83939f161663b66fc34c2167a2c514b2ecb0a10 100644 (file)
@@ -119,6 +119,9 @@ public:
     void handle_data_segment(TcpSegmentDescriptor&, bool flush = true);
     bool validate_packet_established_session(TcpSegmentDescriptor&);
 
+    void count_stale_packet() override
+    { tcpStats.stale_packets++; }
+
     TcpStreamTracker client;
     TcpStreamTracker server;
     TcpStreamConfig* tcp_config = nullptr;
index 5abe5c203225ed66778720ff1f92a67939394fbd..e03bdd9de94928cbaa8e58accb754ec57d0c10aa 100644 (file)
@@ -194,3 +194,7 @@ int UdpSession::process(Packet* p)
     return 0;
 }
 
+void UdpSession::count_stale_packet()
+{
+    udpStats.stale_packets++;
+}
index 3884c3acf7532db071e9659debac092fc0ada26c..6e5b43128859de6427036ead9717ee3662d38c12 100644 (file)
@@ -25,6 +25,7 @@
 #include "flow/session.h"
 
 class SO_PUBLIC UdpSession : public Session
+
 {
 public:
     UdpSession(snort::Flow*);
@@ -34,8 +35,9 @@ public:
     void update_direction(char dir, const snort::SfIp*, uint16_t port) override;
     int process(snort::Packet*) override;
     void clear() override;
+    void count_stale_packet() override;
 
-public:
+    public:
     struct timeval ssn_time = {};
     uint64_t payload_bytes_seen_client = 0;
     uint64_t payload_bytes_seen_server = 0;
index 4ddfebb5d9ad4babf8226fde8f56d8a1d7d063b0..9e19eb976348a64162b26be410ca3c212572ad64 100644 (file)
@@ -17,6 +17,7 @@
 //--------------------------------------------------------------------------
 // config_ignore_ports.cc author Josh Rosenbaum <jrosenba@cisco.com>
 
+#include <cstdint>
 #include <sstream>
 #include <vector>
 #include <string>
index 2a41decb8206025fa54e43c1ca38d217d454e1fd..c752da4d52eacd94b60da29dbce03267e6ce86af 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "pps_dcerpc_server.h"
 
+#include <cstdint>
 #include <algorithm>
 #include <cstring>
 
index d5c1fcf4d3c47f20454a6173c0c61fe23bb6df4e..27729424951abf589360318e381d5acb967164d6 100644 (file)
@@ -17,6 +17,7 @@
 //--------------------------------------------------------------------------
 // pps_frag3_engine.cc author Josh Rosenbaum <jrosenba@cisco.com>
 
+#include <cstdint>
 #include <sstream>
 #include <vector>
 
index 1e97a58ad147604944c7c5cdd57e3873891db60e..1ce62689d7934f64b8f26266bf4089fe60cbd738 100644 (file)
@@ -17,6 +17,7 @@
 //--------------------------------------------------------------------------
 // pps_stream_tcp.cc author Josh Rosenbaum <jrosenba@cisco.com>
 
+#include <cstdint>
 #include <sstream>
 #include <vector>
 #include <string>
index 7f2b84ae495432053ef4497b6534e0b00d7201ff..44404a3ccbcad488305bc97f6374e90f3cabe5d2 100644 (file)
@@ -25,6 +25,7 @@
 // sid.
 // Handle 2 cases: sid was read before/after gid.
 
+#include <cstdint>
 #include <sstream>
 #include <unordered_map>