]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #3006 in SNORT/snort3 from ~MASHASAN/snort3:tcp_queue_limit to...
authorMasud Hasan (mashasan) <mashasan@cisco.com>
Tue, 7 Sep 2021 22:57:35 +0000 (22:57 +0000)
committerMasud Hasan (mashasan) <mashasan@cisco.com>
Tue, 7 Sep 2021 22:57:35 +0000 (22:57 +0000)
Squashed commit of the following:

commit 2f3254c69d4a43567561a58be055623abbcb229a
Author: russ <rucombs@cisco.com>
Date:   Mon Aug 16 12:53:36 2021 -0400

    stream_tcp: add pegs for maximum observed queue size

commit 6369d870a5914eee19deee4af0c927ea93ae09a8
Author: Masud Hasan <mashasan@cisco.com>
Date:   Wed Aug 18 11:09:00 2021 -0400

    stream_tcp: Set sequence number in trimmed packets up to the queue limit and increase defaults

commit 515a0150397fd289eb5fff9135fdfc306411a63d
Author: Masud Hasan <mashasan@cisco.com>
Date:   Mon Jul 26 01:00:38 2021 -0400

    stream_tcp: Normalize data when queue limits are enabled

commit 47c851ff0db0019bcd16a8f40fd9555fd9455fb9
Author: russ <rucombs@cisco.com>
Date:   Fri Jul 30 10:43:34 2021 -0400

    stream_tcp: only update window on right edge acks

13 files changed:
src/protocols/tcp.h
src/stream/tcp/tcp_defs.h
src/stream/tcp/tcp_module.cc
src/stream/tcp/tcp_module.h
src/stream/tcp/tcp_normalizer.cc
src/stream/tcp/tcp_normalizer.h
src/stream/tcp/tcp_normalizers.h
src/stream/tcp/tcp_reassembler.cc
src/stream/tcp/tcp_session.cc
src/stream/tcp/tcp_session.h
src/stream/tcp/tcp_stream_config.h
src/stream/tcp/tcp_stream_tracker.cc
src/stream/tcp/tcp_stream_tracker.h

index 9bb982774a1abb91fa67ab58ab94200ca741549e..1347735a3a5b48a6bbee54893aec6a7063bc23a4 100644 (file)
@@ -173,6 +173,9 @@ struct TCPHdr
 
     inline void set_raw_urp(uint16_t new_urp)
     { th_urp = new_urp; }
+
+    inline void set_seq(uint32_t new_seq)
+    { th_seq = htonl(new_seq); }
 };
 }  // namespace tcp
 }  // namespace snort
index ff6895b3dbea58716eedb81945ab20808321c708..e82fa757c99a63cf97a940446e413a08cb88f97c 100644 (file)
@@ -52,9 +52,9 @@ struct Packet;
 #define STREAM_UNALIGNED       0
 #define STREAM_ALIGNED         1
 
-#define STREAM_DEFAULT_MAX_QUEUED_BYTES 1048576 /* 1 MB */
-#define AVG_PKT_SIZE            400
-#define STREAM_DEFAULT_MAX_QUEUED_SEGS ( STREAM_DEFAULT_MAX_QUEUED_BYTES / AVG_PKT_SIZE )
+#define MQ_NONE    0
+#define MQ_BYTES   1
+#define MQ_SEGS    2
 
 #define STREAM_DEFAULT_MAX_SMALL_SEG_SIZE 0    /* disabled */
 #define STREAM_DEFAULT_CONSEC_SMALL_SEGS 0     /* disabled */
index 2e2a5c11dafcbd742d09c7cab3e283ba7a83cbd6..4c6a6adaf8176367b89263ed01b0bd986e507085 100644 (file)
@@ -112,6 +112,8 @@ const PegInfo tcp_pegs[] =
     { CountType::SUM, "partial_flush_bytes", "partial flush total bytes" },
     { CountType::SUM, "inspector_fallbacks", "count of fallbacks from assigned service inspector" },
     { CountType::SUM, "partial_fallbacks", "count of fallbacks from assigned service stream splitter" },
+    { CountType::MAX, "max_segs", "maximum number of segments queued in any flow" },
+    { CountType::MAX, "max_bytes", "maximum number of bytes queued in any flow" },
     { CountType::END, nullptr, nullptr }
 };
 
@@ -171,11 +173,11 @@ static const Parameter stream_tcp_small_params[] =
 
 static const Parameter stream_queue_limit_params[] =
 {
-    { "max_bytes", Parameter::PT_INT, "0:max32", "1048576",
-      "don't queue more than given bytes per session and direction" },
+    { "max_bytes", Parameter::PT_INT, "0:max32", "4194304",
+      "don't queue more than given bytes per session and direction, 0 = unlimited" },
 
-    { "max_segments", Parameter::PT_INT, "0:max32", "2621",
-      "don't queue more than given segments per session and direction" },
+    { "max_segments", Parameter::PT_INT, "0:max32", "3072",
+      "don't queue more than given segments per session and direction, 0 = unlimited" },
 
     { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
 };
index 2f6e8459616b5217517b661217de8cd31255c19e..9d9cad9f8c8ddd153d2a531fb4b3d5eccc029156 100644 (file)
@@ -110,6 +110,8 @@ struct TcpStats
     PegCount partial_flush_bytes;
     PegCount inspector_fallbacks;
     PegCount partial_fallbacks;
+    PegCount max_segs;
+    PegCount max_bytes;
 };
 
 extern THREAD_LOCAL struct TcpStats tcpStats;
index cd17146c4cd2d270e2863b8c3ecdb9b9cce5d96b..0b5d135e2eac212b2d2cbb139f3cd47bec23d3e3 100644 (file)
@@ -57,11 +57,15 @@ NormPegs TcpNormalizer::get_normalization_counts(unsigned& c)
     return tcp_norm_stats;
 }
 
-bool TcpNormalizer::trim_payload(
-    TcpNormalizerState&, TcpSegmentDescriptor& tsd, uint32_t max, NormMode mode, TcpPegCounts peg)
+bool TcpNormalizer::trim_payload(TcpNormalizerState&, TcpSegmentDescriptor& tsd, uint32_t max,
+    NormMode mode, TcpPegCounts peg, bool force)
 {
+    if ( force )
+        mode = NORM_MODE_ON;
+
     tcp_norm_stats[peg][mode]++;
-    if (mode == NORM_MODE_ON)
+
+    if ( mode == NORM_MODE_ON )
     {
         uint16_t fat = tsd.get_len() - max;
         tsd.set_len(max);
@@ -125,10 +129,10 @@ void TcpNormalizer::trim_rst_payload(
 }
 
 void TcpNormalizer::trim_win_payload(
-    TcpNormalizerState& tns, TcpSegmentDescriptor& tsd, uint32_t max)
+    TcpNormalizerState& tns, TcpSegmentDescriptor& tsd, uint32_t max, bool force)
 {
     if (tsd.get_len() > max)
-        trim_payload(tns, tsd, max, (NormMode)tns.trim_win, PC_TCP_TRIM_WIN);
+        trim_payload(tns, tsd, max, (NormMode)tns.trim_win, PC_TCP_TRIM_WIN, force);
 }
 
 void TcpNormalizer::trim_mss_payload(
index 96bf9e5a374c2498a0cd5682d8cde1a8e8a11fec..85682ea890cddbcaec86ad88f8c67f71dcfa10e4 100644 (file)
@@ -81,7 +81,8 @@ public:
     virtual bool packet_dropper(State&, TcpSegmentDescriptor&, NormFlags);
     virtual bool trim_syn_payload(State&, TcpSegmentDescriptor&, uint32_t max = 0);
     virtual void trim_rst_payload(State&, TcpSegmentDescriptor&, uint32_t max = 0);
-    virtual void trim_win_payload(State&, TcpSegmentDescriptor&, uint32_t max = 0);
+    virtual void trim_win_payload(State&, TcpSegmentDescriptor&, uint32_t max = 0,
+        bool force = false);
     virtual void trim_mss_payload(State&, TcpSegmentDescriptor&, uint32_t max = 0);
     virtual void ecn_tracker(State&, const snort::tcp::TCPHdr*, bool req3way);
     virtual void ecn_stripper(State&, TcpSegmentDescriptor&);
@@ -99,7 +100,8 @@ public:
 protected:
     TcpNormalizer() = default;
 
-    virtual bool trim_payload(State&, TcpSegmentDescriptor&, uint32_t, NormMode, TcpPegCounts);
+    virtual bool trim_payload(State&, TcpSegmentDescriptor&, uint32_t, NormMode, TcpPegCounts,
+        bool force = false);
     virtual bool strip_tcp_timestamp(
         State&, TcpSegmentDescriptor&, const snort::tcp::TcpOption*, NormMode);
     virtual bool validate_rst_seq_geq(State&, TcpSegmentDescriptor&);
index 0a57b669fab5920e37e885d6219b85b3e26d14ab..3a67096d1d5490bf3009d012479e97be3bac1715 100644 (file)
@@ -59,8 +59,8 @@ public:
     void trim_rst_payload(TcpSegmentDescriptor& tsd, uint32_t max = 0)
     { norm->trim_rst_payload(tns, tsd, max); }
 
-    void trim_win_payload(TcpSegmentDescriptor& tsd, uint32_t max = 0)
-    { norm->trim_win_payload(tns, tsd, max); }
+    void trim_win_payload(TcpSegmentDescriptor& tsd, uint32_t max = 0, bool force = false)
+    { norm->trim_win_payload(tns, tsd, max, force); }
 
     void trim_mss_payload(TcpSegmentDescriptor& tsd, uint32_t max = 0)
     { norm->trim_mss_payload(tns, tsd, max); }
index 0d65f31821e00d62be659b4029894122f6498ec6..db9685c0b0a55161ed9ee5add1785cb8c71967ea 100644 (file)
@@ -152,6 +152,12 @@ void TcpReassembler::queue_reassembly_segment(
     trs.sos.seg_bytes_total += tsn->i_len;
     trs.sos.total_segs_queued++;
     tcpStats.segs_queued++;
+
+    if ( trs.sos.seg_count > tcpStats.max_segs )
+        tcpStats.max_segs = trs.sos.seg_count;
+
+    if ( trs.sos.seg_bytes_total > tcpStats.max_bytes )
+        tcpStats.max_bytes = trs.sos.seg_bytes_total;
 }
 
 bool TcpReassembler::is_segment_fasttrack(
index 51f79c01d90982beb4ae0546184bb545529e033e..38133f6db74ac807181749d85ccb8468f903aa02 100644 (file)
@@ -295,7 +295,7 @@ void TcpSession::update_perf_base_state(char newState)
         DataBus::publish(FLOW_STATE_EVENT, nullptr, flow);
 }
 
-bool TcpSession::flow_exceeds_config_thresholds(const TcpSegmentDescriptor& tsd)
+bool TcpSession::flow_exceeds_config_thresholds(TcpSegmentDescriptor& tsd)
 {
     TcpStreamTracker* listener = tsd.get_listener();
 
@@ -317,20 +317,61 @@ bool TcpSession::flow_exceeds_config_thresholds(const TcpSegmentDescriptor& tsd)
             tel.set_tcp_event(EVENT_MAX_SMALL_SEGS_EXCEEDED);
     }
 
-    if ( tcp_config->max_queued_bytes
-        && ( listener->reassembler.get_seg_bytes_total() > tcp_config->max_queued_bytes ) )
+    if ( tcp_config->max_queued_bytes )
     {
-        tcpStats.exceeded_max_bytes++;
-        // FIXIT-M add one alert per flow per above
-        return true;
+        int32_t space_left =
+            tcp_config->max_queued_bytes - listener->reassembler.get_seg_bytes_total();
+
+        if ( space_left < (int32_t)tsd.get_len() )
+        {
+            tcpStats.exceeded_max_bytes++;
+            bool inline_mode = tsd.is_policy_inline();
+            bool ret_val = true;
+
+            if ( space_left > 0 )
+                ret_val = !inline_mode; // For partial trim, reassemble only if we can force an inject
+            else
+                space_left = 0;
+
+            if ( inline_mode )
+            {
+                if ( listener->max_queue_exceeded == MQ_NONE )
+                {
+                    listener->max_queue_seq_nxt = tsd.get_seq() + space_left;
+                    listener->max_queue_exceeded = MQ_BYTES;
+                }
+                else
+                    (const_cast<tcp::TCPHdr*>(tsd.get_pkt()->ptrs.tcph))->set_seq(listener->max_queue_seq_nxt);
+            }
+            listener->normalizer.trim_win_payload(tsd, space_left, inline_mode);
+            return ret_val;
+        }
+        else if ( listener->max_queue_exceeded == MQ_BYTES )
+            listener->max_queue_exceeded = MQ_NONE;
     }
 
-    if ( tcp_config->max_queued_segs
-        && ( listener->reassembler.get_seg_count() + 1 > tcp_config->max_queued_segs ) )
+    if ( tcp_config->max_queued_segs )
     {
-        tcpStats.exceeded_max_segs++;
-        // FIXIT-M add one alert per flow per above
-        return true;
+        if ( listener->reassembler.get_seg_count() + 1 > tcp_config->max_queued_segs )
+        {
+            tcpStats.exceeded_max_segs++;
+            bool inline_mode = tsd.is_policy_inline();
+
+            if ( inline_mode )
+            {
+                if ( listener->max_queue_exceeded == MQ_NONE )
+                {
+                    listener->max_queue_seq_nxt = tsd.get_seq();
+                    listener->max_queue_exceeded = MQ_SEGS;
+                }
+                else
+                    (const_cast<tcp::TCPHdr*>(tsd.get_pkt()->ptrs.tcph))->set_seq(listener->max_queue_seq_nxt);
+            }
+            listener->normalizer.trim_win_payload(tsd, 0, inline_mode);
+            return true;
+        }
+        else if ( listener->max_queue_exceeded == MQ_SEGS )
+            listener->max_queue_exceeded = MQ_NONE;
     }
 
     return false;
index 3ad78aebce10ab8811600f41b17c3185de2ffa7b..93019a34c025d0719e628933eb57a0c3c6163f20 100644 (file)
@@ -77,7 +77,7 @@ private:
     void process_tcp_stream(TcpSegmentDescriptor&);
     int process_tcp_data(TcpSegmentDescriptor&);
     void set_os_policy() override;
-    bool flow_exceeds_config_thresholds(const TcpSegmentDescriptor&);
+    bool flow_exceeds_config_thresholds(TcpSegmentDescriptor&);
     void update_stream_order(const TcpSegmentDescriptor&, bool aligned);
     void swap_trackers();
     void init_session_on_syn(TcpSegmentDescriptor&);
index 1f73b7ac162dfa2c4c7a8c0aca7f3b34e9718c48..0537e6fbcbc948dc7689c7ef19a01df7843c52d8 100644 (file)
@@ -61,8 +61,8 @@ public:
     uint32_t max_window = 0;
     uint32_t overlap_limit = 0;
 
-    uint32_t max_queued_bytes = STREAM_DEFAULT_MAX_QUEUED_BYTES;
-    uint32_t max_queued_segs = STREAM_DEFAULT_MAX_QUEUED_SEGS;
+    uint32_t max_queued_bytes = 4194304;
+    uint32_t max_queued_segs = 3072;
 
     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 db4c45ea38c4e49b0534554fcf28acb84cc037fa..1bde9321ba326d7b4e0428991c7970a14a446f9a 100644 (file)
@@ -500,8 +500,13 @@ void TcpStreamTracker::update_tracker_ack_sent(TcpSegmentDescriptor& tsd)
     if ( SEQ_GT(tsd.get_end_seq(), snd_nxt) )
         snd_nxt = tsd.get_end_seq();
 
-    if ( SEQ_GT(tsd.get_ack(), r_win_base) )
-        r_win_base = tsd.get_ack();
+    if ( SEQ_GEQ(tsd.get_ack(), r_win_base) )
+    {
+        if ( SEQ_GT(tsd.get_ack(), r_win_base) )
+            r_win_base = tsd.get_ack();
+
+        snd_wnd = tsd.get_wnd();
+    }
 
     if ( ( fin_seq_status == TcpStreamTracker::FIN_WITH_SEQ_SEEN )
         && SEQ_EQ(r_win_base, fin_final_seq) )
@@ -509,7 +514,6 @@ void TcpStreamTracker::update_tracker_ack_sent(TcpSegmentDescriptor& tsd)
         fin_seq_status = TcpStreamTracker::FIN_WITH_SEQ_ACKED;
     }
 
-    snd_wnd = tsd.get_wnd();
     reassembler.flush_on_ack_policy(tsd.get_pkt());
 }
 
index 1bc7f6b95511811007f0bfb4d6f4ebc87e37c432..b0f0708fc2dc593cb8125e13162d04890e9f0070 100644 (file)
@@ -332,6 +332,8 @@ public:
 
     uint32_t r_win_base = 0; // remote side window base sequence number (the last ack we got)
     uint32_t small_seg_count = 0;
+    uint32_t max_queue_seq_nxt; // next expected sequence once queue limit is exceeded
+    uint8_t max_queue_exceeded = MQ_NONE;
     uint8_t order = 0;
     FinSeqNumStatus fin_seq_status = TcpStreamTracker::FIN_NOT_SEEN;
     bool reinit_seg_base = false;