]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #4463: stream_tcp: implement flush on asymmetric flows in IDS mode when...
authorDavis McPherson -X (davmcphe - XORIANT CORPORATION at Cisco) <davmcphe@cisco.com>
Tue, 1 Oct 2024 20:44:40 +0000 (20:44 +0000)
committerSteven Baigal (sbaigal) <sbaigal@cisco.com>
Tue, 1 Oct 2024 20:44:40 +0000 (20:44 +0000)
Merge in SNORT/snort3 from ~DAVMCPHE/snort3:ids_asymmetric_support to master

Squashed commit of the following:

commit 97bdb1f6e267a42f2f83dc62444a9c0bf97170ad
Author: davis mcpherson <davmcphe@cisco.com>
Date:   Thu Sep 5 09:30:58 2024 -0400

    stream_tcp: implement flush on asymmetric flows in IDS mode when queued bytes exceeds configure threshold

14 files changed:
src/stream/tcp/tcp_module.cc
src/stream/tcp/tcp_reassembler.cc
src/stream/tcp/tcp_reassembler.h
src/stream/tcp/tcp_reassembler_ids.cc
src/stream/tcp/tcp_reassembler_ids.h
src/stream/tcp/tcp_reassembler_ips.cc
src/stream/tcp/tcp_reassembler_ips.h
src/stream/tcp/tcp_reassembly_segments.cc
src/stream/tcp/tcp_session.cc
src/stream/tcp/tcp_state_syn_sent.cc
src/stream/tcp/tcp_stream_config.cc
src/stream/tcp/tcp_stream_config.h
src/stream/tcp/tcp_stream_tracker.cc
src/stream/tcp/tcp_stream_tracker.h

index 0bfe2f7c9cda8ac5a7e6634d68e214683bd30e27..894ffe5537e243ca0423016e9fc024b020a11f50 100644 (file)
@@ -238,6 +238,10 @@ static const Parameter s_params[] =
     { "idle_timeout", Parameter::PT_INT, "1:max31", "3600",
       "session deletion on idle " },
 
+    { "asymmetric_ids_flush_threshold", Parameter::PT_INT, "1:max31", "65535",
+      "max bytes queued on asymmetric flow before flush in IDS mode" },
+
+
     { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
 };
 
@@ -331,6 +335,9 @@ bool StreamTcpModule::set(const char*, Value& v, SnortConfig*)
     else if ( v.is("max_segments") )
         config->max_queued_segs = v.get_uint32();
 
+    else if ( v.is("asymmetric_ids_flush_threshold") )
+        config->asymmetric_ids_flush_threshold = v.get_uint32();
+
     else if ( v.is("max_window") )
         config->max_window = v.get_uint32();
 
index 03d117dc2b21ce89edd9129e0dbb6ad37ea49926..e7255ec57fd281bce2a53473cb29069aa248e8e5 100644 (file)
@@ -595,7 +595,7 @@ bool TcpReassembler::final_flush_on_fin(int32_t flush_amt, Packet *p, FinSeqNumS
         && !p->flow->searching_for_service();
 }
 
-bool  TcpReassembler::flush_on_asymmetric_flow(uint32_t flushed, snort::Packet *p)
+bool TcpReassembler::asymmetric_flow_flushed(uint32_t flushed, snort::Packet *p)
 {
     bool asymmetric = flushed && seglist.seg_count && !p->flow->two_way_traffic() && !p->ptrs.tcph->is_syn();
     if ( asymmetric )
index c7c03a96081bcf50795969384706269021a3f243..66fed13e890ac9e971ed99c13f6aa677dd291a61 100644 (file)
@@ -63,6 +63,7 @@ public:
 
     virtual int eval_flush_policy_on_ack(snort::Packet*) = 0;
     virtual int eval_flush_policy_on_data(snort::Packet*) = 0;
+    virtual int eval_asymmetric_flush(snort::Packet*) = 0;
     virtual int flush_stream(snort::Packet*, uint32_t dir, bool final_flush = false) = 0;
     void flush_queued_segments(snort::Flow* flow, bool clear, snort::Packet* = nullptr);
     void finish_and_final_flush(snort::Flow* flow, bool clear, snort::Packet*);
@@ -124,7 +125,7 @@ protected:
     void check_first_segment_hole();
     uint32_t perform_partial_flush(snort::Packet*, uint32_t flushed = 0);
     bool final_flush_on_fin(int32_t flush_amt, snort::Packet*, FinSeqNumStatus);
-    bool flush_on_asymmetric_flow(uint32_t flushed, snort::Packet *p);
+    bool asymmetric_flow_flushed(uint32_t flushed, snort::Packet *p);
 
     ProtocolAwareFlusher paf;
     TcpStreamTracker& tracker;
@@ -154,6 +155,9 @@ public:
     int eval_flush_policy_on_data(snort::Packet*) override
     { return 0; }
 
+    int eval_asymmetric_flush(snort::Packet*) override
+    { return 0; }
+
     int flush_stream(snort::Packet*, uint32_t, bool) override
     { return 0; }
 
index 43d7c2329afc1ab43e3f68d465b0f5c02ea5a4ba..124b855a0083fc452cb82d5fba4ff52ce3fe43e2 100644 (file)
@@ -231,25 +231,39 @@ int TcpReassemblerIds::eval_flush_policy_on_data(Packet* p)
     if ( tracker.is_retransmit_of_held_packet(p) )
         flushed = perform_partial_flush(p, flushed);
 
-    if ( flush_on_asymmetric_flow(flushed, p) )
+    if ( !p->flow->two_way_traffic() and
+        seglist.get_seg_bytes_total() > seglist.session->tcp_config->asymmetric_ids_flush_threshold )
     {
-        purge_to_seq(seglist.head->start_seq() + flushed);
-        tracker.r_win_base = seglist.seglist_base_seq;
-        tcpStats.flush_on_asymmetric_flow++;
+        seglist.skip_holes();
+        flushed += eval_asymmetric_flush(p);
     }
 
     return flushed;
 }
 
+int TcpReassemblerIds::eval_asymmetric_flush(snort::Packet* p)
+{
+    // asymmetric flush in IDS mode.. advance r_win_base to end of in-order data
+    tracker.r_win_base = tracker.rcv_nxt;
+
+    uint32_t flushed = eval_flush_policy_on_ack(p);
+    if ( flushed )
+        tcpStats.flush_on_asymmetric_flow++;
+
+    return flushed;
+}
+
 int TcpReassemblerIds::flush_stream(Packet* p, uint32_t dir, bool final_flush)
 {
-    if ( seglist.session->flow->two_way_traffic()
-        or (tracker.get_tcp_state() == TcpStreamTracker::TCP_MID_STREAM_RECV) )
-    {
-        uint32_t bytes = get_q_footprint();  // num bytes in post-ack mode
-        if ( bytes )
-            return flush_to_seq(bytes, p, dir);
-    }
+    uint32_t bytes = 0;
+
+    if ( seglist.session->flow->two_way_traffic() )
+         bytes = get_q_footprint();
+    else
+        bytes = get_q_sequenced();
+
+    if ( bytes )
+        return flush_to_seq(bytes, p, dir);
 
     if ( final_flush )
         return do_zero_byte_flush(p, dir);
index 8d8b643ee95b6a22dea13bbc016d129cb0f472e9..4ee533d551da72292e7d6a0d3d745bc6edf27658 100644 (file)
@@ -47,6 +47,7 @@ public:
 
     int eval_flush_policy_on_ack(snort::Packet*) override;
     int eval_flush_policy_on_data(snort::Packet*) override;
+    int eval_asymmetric_flush(snort::Packet*) override;
     int flush_stream(snort::Packet*, uint32_t dir, bool final_flush = false) override;
 
     FlushPolicy get_flush_policy() const override
index 3b474baa33ca6268a753782872b834a9b515545e..f0a513d7676d4fdf7d66e777c69658b4e9026fa4 100644 (file)
@@ -153,7 +153,7 @@ int TcpReassemblerIps::eval_flush_policy_on_data(Packet* p)
     if ( tracker.is_retransmit_of_held_packet(p) )
         flushed = perform_partial_flush(p, flushed);
 
-    if ( flush_on_asymmetric_flow(flushed, p) )
+    if ( asymmetric_flow_flushed(flushed, p) )
     {
         purge_to_seq(seglist.head->start_seq() + flushed);
         tracker.r_win_base = seglist.seglist_base_seq;
@@ -163,6 +163,11 @@ int TcpReassemblerIps::eval_flush_policy_on_data(Packet* p)
     return flushed;
 }
 
+int TcpReassemblerIps::eval_asymmetric_flush(snort::Packet* p)
+{
+    return eval_flush_policy_on_data(p);
+}
+
 int TcpReassemblerIps::flush_stream(Packet* p, uint32_t dir, bool final_flush)
 {
     if ( seglist.session->flow->two_way_traffic()
index bf21acf8e0c5b86fd7b279c0c15c897aca96d3a5..3a81efd1ee10138ddd013d123170218cd6f4a5e9 100644 (file)
@@ -46,6 +46,8 @@ public:
 
     int eval_flush_policy_on_ack(snort::Packet*) override;
     int eval_flush_policy_on_data(snort::Packet*) override;
+    int eval_asymmetric_flush(snort::Packet*) override;
+
     int flush_stream(snort::Packet*, uint32_t dir, bool final_flush = false) override;
 
     FlushPolicy get_flush_policy() const override
index b394869910b748ac9d2d411decbb73a93cf53025..0a3e0469f22aa21b665e1338953a2eebfeb5a3bf 100644 (file)
@@ -362,7 +362,7 @@ void TcpReassemblySegments::purge_segments_left_of_hole(const TcpSegmentNode* en
     tracker->set_order(TcpStreamTracker::OUT_OF_SEQUENCE);
 
     if (PacketTracer::is_active())
-        PacketTracer::log("Stream: Skipped %u packets before seglist hole)\n", packets_skipped);
+        PacketTracer::log("Stream: Skipped %u packets before seglist hole\n", packets_skipped);
 }
 
 void TcpReassemblySegments::advance_rcv_nxt(TcpSegmentNode *tsn)
index 2bfffd190f5be28330765a1a2bf7d7bde1544903..9ed5f7276cb575b41a763e7e66bee95ad75b8da8 100644 (file)
@@ -612,9 +612,9 @@ bool TcpSession::check_reassembly_queue_thresholds(TcpSegmentDescriptor& tsd, Tc
             tcpStats.exceeded_max_bytes++;
             bool ret_val = true;
 
-            // if inline and this is an asymmetric flow then skip over any seglist holes
+            // if this is an asymmetric flow then skip over any seglist holes
             // and flush to free up seglist space
-            if ( tsd.is_ips_policy_inline()  && !tsd.get_pkt()->flow->two_way_traffic() )
+            if ( !tsd.get_pkt()->flow->two_way_traffic() )
             {
                 space_left = listener->kickstart_asymmetric_flow(tsd, tcp_config->max_queued_bytes);
                 if ( space_left >= (int32_t)tsd.get_len() )
@@ -647,9 +647,9 @@ bool TcpSession::check_reassembly_queue_thresholds(TcpSegmentDescriptor& tsd, Tc
         {
             tcpStats.exceeded_max_segs++;
 
-            // if inline and this is an asymmetric flow then skip over any seglist holes
+            // if this is an asymmetric flow then skip over any seglist holes
             // and flush to free up seglist space
-            if ( tsd.is_ips_policy_inline() && !tsd.get_pkt()->flow->two_way_traffic() )
+            if ( !tsd.get_pkt()->flow->two_way_traffic() )
             {
                 listener->kickstart_asymmetric_flow(tsd, tcp_config->max_queued_bytes);
                 if ( listener->seglist.get_seg_count() + 1 <= tcp_config->max_queued_segs )
index 4334cca5c2bef0eaac1a171ab146764a567fad24..80b833c4ef30ce08394dd27db682c6023bc44de7 100644 (file)
@@ -151,6 +151,7 @@ bool TcpStateSynSent::fin_sent(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
         }
         trk.set_tcp_state(TcpStreamTracker::TCP_FIN_WAIT1);
     }
+
     return true;
 }
 
index 104e65273790b2df39e6e1993d2be0211c9da830..4bf07654722d18fce0b40721c629cbf5a4ef7d43 100644 (file)
@@ -51,7 +51,10 @@ void TcpStreamConfig::show() const
     str += std::to_string(max_queued_segs);
     str += " }";
     ConfigLogger::log_value("queue_limit", str.c_str());
-
+    str = "{ flush threshold = ";
+    str += std::to_string(asymmetric_ids_flush_threshold);
+    str += " }";
+    ConfigLogger::log_value("asymmetric_ids", str.c_str());
     ConfigLogger::log_flag("reassemble_async", !(flags & STREAM_CONFIG_NO_ASYNC_REASSEMBLY));
     ConfigLogger::log_limit("require_3whs", hs_timeout, -1, hs_timeout < 0 ? hs_timeout : -1);
     ConfigLogger::log_value("session_timeout", session_timeout);
index 6e9459e0ac6324c57b2238fad0b0f1fded8b2a80..5aa25605c59b4bd220f5e1a06a964d255fc459fb 100644 (file)
@@ -63,6 +63,7 @@ public:
 
     uint32_t max_queued_bytes = 4194304;
     uint32_t max_queued_segs = 3072;
+    uint32_t asymmetric_ids_flush_threshold = 65535;
 
     uint32_t max_consec_small_segs = STREAM_DEFAULT_CONSEC_SMALL_SEGS;
     uint32_t max_consec_small_seg_size = STREAM_DEFAULT_MAX_SMALL_SEG_SIZE;
index 279b9eaad1607464a09e1a541724eeef877d0e2e..29d2584dc57f15604994aa93ba565b847a5ce47f 100644 (file)
@@ -126,9 +126,7 @@ int TcpStreamTracker::eval_flush_policy_on_ack(snort::Packet* p)
         oaitw_reassembler = nullptr;
     }
 
-    reassembler->eval_flush_policy_on_ack(p);
-
-    return 0;
+    return reassembler->eval_flush_policy_on_ack(p);
 }
 
 int TcpStreamTracker::eval_flush_policy_on_data(snort::Packet* p)
@@ -144,6 +142,19 @@ int TcpStreamTracker::eval_flush_policy_on_data(snort::Packet* p)
     return 0;
 }
 
+int TcpStreamTracker::eval_asymmetric_flush(snort::Packet* p)
+{
+    if( oaitw_reassembler )
+    {
+        delete oaitw_reassembler;
+        oaitw_reassembler = nullptr;
+    }
+
+    reassembler->eval_asymmetric_flush(p);
+
+    return 0;
+}
+
 TcpStreamTracker::TcpEvent TcpStreamTracker::set_tcp_event(const TcpSegmentDescriptor& tsd)
 {
     bool talker;
@@ -481,6 +492,10 @@ void TcpStreamTracker::fallback()
     //assert(server_side == to_server && server_side == !tracker.client_tracker);
 #endif
 
+    if (PacketTracer::is_active())
+        PacketTracer::log("Stream: %s tracker fallback to the Atom splitter.\n",
+            client_tracker ? "client" : "server");
+
     set_splitter(new AtomSplitter(!client_tracker));
     tcpStats.partial_fallbacks++;
 
@@ -872,7 +887,7 @@ int32_t TcpStreamTracker::kickstart_asymmetric_flow(const TcpSegmentDescriptor&
     else
         reassembler->reset_paf();
 
-    eval_flush_policy_on_data(tsd.get_pkt());
+    eval_asymmetric_flush(tsd.get_pkt());
 
     int32_t space_left = max_queued_bytes - seglist.get_seg_bytes_total();
 
index b8850bfc532955c50bf9c0fe11e19480b45c80c9..a2096bb6f1590b67300dfe38107aa54c47305998 100644 (file)
@@ -97,6 +97,7 @@ public:
 
     int eval_flush_policy_on_ack(snort::Packet*);
     int eval_flush_policy_on_data(snort::Packet*);
+    int eval_asymmetric_flush(snort::Packet*);
     void update_stream_order(const TcpSegmentDescriptor&, bool aligned);
 
     void fallback();