]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #4240: stream_tcp: Refactor tcp overlap, segment list and reassembly...
authorDavis McPherson -X (davmcphe - XORIANT CORPORATION at Cisco) <davmcphe@cisco.com>
Tue, 23 Jul 2024 17:56:56 +0000 (17:56 +0000)
committerSteven Baigal (sbaigal) <sbaigal@cisco.com>
Tue, 23 Jul 2024 17:56:56 +0000 (17:56 +0000)
Merge in SNORT/snort3 from ~DAVMCPHE/snort3:reassembly_rewrite_u2.0 to master

Squashed commit of the following:

commit 6e4456f7895584520a5e6bb2d3eae0458b33d516
Author: davis mcpherson <davmcphe@cisco.com>
Date:   Thu Feb 22 11:44:08 2024 -0500

    stream_tcp: initial refactor to move alert functions to their own class

    stream_tcp: refactor to move tcp overlap processing out of reassembly class

    stream_tcp: merge TcpStreamSession into TcpSession

    stream_tcp: refactor segment nodes to implement reassembly cursor and
    eliminate tracking variables such as c_seq/c_len, etc. add helper functions
    to track state

    stream_tcp: improve variable and function names for overlap processing

    stream_tcp: refactor TcpReassembler into a virtual base class and subclasses for each mode: ignore, IPS and IDS

    stream: refactor paf logic into a c++ class

    stream_tcp: integrate and streamline setting of flush policy and splitter
                improve tracking of rcv_nxt state

    stream_tcp: delete lws_init, it was redundant with tcp_init; delete FIXITs that are no longer relevant
    stream_tcp: updates from PR review comments

50 files changed:
src/flow/flow_cache.cc
src/flow/flow_control.cc
src/service_inspectors/dce_rpc/dce_http_proxy.cc
src/service_inspectors/dce_rpc/dce_http_server.cc
src/stream/CMakeLists.txt
src/stream/pafng.cc [new file with mode: 0644]
src/stream/pafng.h [new file with mode: 0644]
src/stream/stream.cc
src/stream/stream.h
src/stream/tcp/CMakeLists.txt
src/stream/tcp/ips_stream_reassemble.cc
src/stream/tcp/segment_overlap_editor.cc [deleted file]
src/stream/tcp/segment_overlap_editor.h [deleted file]
src/stream/tcp/stream_tcp.cc
src/stream/tcp/tcp_alerts.cc [new file with mode: 0644]
src/stream/tcp/tcp_alerts.h [new file with mode: 0644]
src/stream/tcp/tcp_defs.h
src/stream/tcp/tcp_normalizer.cc
src/stream/tcp/tcp_normalizer.h
src/stream/tcp/tcp_normalizers.cc
src/stream/tcp/tcp_normalizers.h
src/stream/tcp/tcp_overlap_resolver.cc [new file with mode: 0644]
src/stream/tcp/tcp_overlap_resolver.h [new file with mode: 0644]
src/stream/tcp/tcp_reassembler.cc
src/stream/tcp/tcp_reassembler.h
src/stream/tcp/tcp_reassembler_ids.cc [new file with mode: 0644]
src/stream/tcp/tcp_reassembler_ids.h [new file with mode: 0644]
src/stream/tcp/tcp_reassembler_ips.cc [new file with mode: 0644]
src/stream/tcp/tcp_reassembler_ips.h [new file with mode: 0644]
src/stream/tcp/tcp_reassemblers.cc [deleted file]
src/stream/tcp/tcp_reassemblers.h [deleted file]
src/stream/tcp/tcp_reassembly_segments.cc [new file with mode: 0644]
src/stream/tcp/tcp_reassembly_segments.h [new file with mode: 0644]
src/stream/tcp/tcp_segment_descriptor.cc
src/stream/tcp/tcp_segment_descriptor.h
src/stream/tcp/tcp_segment_node.cc
src/stream/tcp/tcp_segment_node.h
src/stream/tcp/tcp_session.cc
src/stream/tcp/tcp_session.h
src/stream/tcp/tcp_state_closed.cc
src/stream/tcp/tcp_state_handler.cc
src/stream/tcp/tcp_state_mid_stream_recv.cc
src/stream/tcp/tcp_state_mid_stream_sent.cc
src/stream/tcp/tcp_state_syn_sent.cc
src/stream/tcp/tcp_stream_session.cc [deleted file]
src/stream/tcp/tcp_stream_session.h [deleted file]
src/stream/tcp/tcp_stream_tracker.cc
src/stream/tcp/tcp_stream_tracker.h
src/stream/tcp/tcp_trace.cc
src/stream/tcp/test/tcp_normalizer_test.cc

index 7515ce89b06ee2eca75ba4791a59d129b4581957..87077b7f1a399588cb9c9fdb6915c963cc3bc959 100644 (file)
@@ -39,7 +39,7 @@
 #include "packet_io/active.h"
 #include "packet_io/packet_tracer.h"
 #include "stream/base/stream_module.h"
-#include "stream/tcp/tcp_stream_session.h"
+#include "stream/tcp/tcp_session.h"
 #include "stream/tcp/tcp_trace.h"
 #include "time/packet_time.h"
 #include "trace/trace_api.h"
@@ -845,7 +845,7 @@ void FlowCache::output_flow(std::fstream& stream, const Flow& flow, const struct
                 << dst_ip << "/" << dst_port;
             if (flow.session)
             {
-                TcpStreamSession* tcp_session = static_cast<TcpStreamSession*>(flow.session);
+                TcpSession* tcp_session = static_cast<TcpSession*>(flow.session);
                 proto << " state client " << stream_tcp_state_to_str(tcp_session->client)
                     << " server " << stream_tcp_state_to_str(tcp_session->server);
             }
index 0d83883dccee42da6cf1e4f34697a3bc4b1cd9a6..b12106b05b47d7d21174f70f22ea923010d871b1 100644 (file)
@@ -613,7 +613,7 @@ void FlowControl::check_expected_flow(Flow* flow, Packet* p)
 
     if ( ignore )
     {
-        flow->ssn_state.ignore_direction = ignore;
+        flow->ssn_state.ignore_direction = SSN_DIR_BOTH;
         DetectionEngine::disable_all(p);
     }
 }
index 174222b595925625da29d6eddf38ee50fb531819..a9b385828ae38c173e334a482e350a149d6bc0ee 100644 (file)
@@ -25,7 +25,8 @@
 
 #include "dce_http_proxy_module.h"
 
-#include "stream/tcp/tcp_stream_session.h"
+#include "managers/inspector_manager.h"
+#include "stream/tcp/tcp_session.h"
 
 #include "dce_http_proxy_splitter.h"
 
@@ -58,7 +59,7 @@ void DceHttpProxy::clear(Packet* p)
     {
         if ( !p->test_session_flags(SSNFLAG_ABORT_CLIENT | SSNFLAG_ABORT_SERVER) )
         {
-            TcpStreamSession* tcp_session = (TcpStreamSession*)flow->session;
+            TcpSession* tcp_session = (TcpSession*)flow->session;
 
             DceHttpProxySplitter* c2s_splitter =
                 (DceHttpProxySplitter*)(tcp_session->get_splitter(true));
index 65391880f3c1ec2ab5bdb8ddeb9b3291f3e1c82a..9624fe82b581f970d36d9cbb6f2278e7d54077b2 100644 (file)
@@ -25,7 +25,8 @@
 
 #include "dce_http_server_module.h"
 
-#include "stream/tcp/tcp_stream_session.h"
+#include "managers/inspector_manager.h"
+#include "stream/tcp/tcp_session.h"
 
 #include "dce_http_server_splitter.h"
 
@@ -58,7 +59,7 @@ void DceHttpServer::clear(Packet* p)
     {
         if ( !p->test_session_flags(SSNFLAG_ABORT_SERVER) )
         {
-            TcpStreamSession* tcp_session = (TcpStreamSession*)flow->session;
+            TcpSession* tcp_session = (TcpSession*)flow->session;
             DceHttpServerSplitter* splitter =
                 (DceHttpServerSplitter*)(tcp_session->get_splitter(false));
 
index a31e9b29a7ac2b34eadb2fe7ddbd0d969ebb73c7..bce84efd96137a40039f4c2da795d71d7e931549 100644 (file)
@@ -10,6 +10,7 @@ add_subdirectory(test)
 
 set (STREAM_INCLUDES
     paf.h
+    pafng.h
     stream.h
     stream_splitter.h
 )
@@ -26,6 +27,7 @@ add_library( stream_paf OBJECT
     flush_bucket.h
     paf.cc
     paf_stats.h
+    pafng.cc
 )
 
 install (FILES ${STREAM_INCLUDES}
diff --git a/src/stream/pafng.cc b/src/stream/pafng.cc
new file mode 100644 (file)
index 0000000..6c60cec
--- /dev/null
@@ -0,0 +1,315 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2024-2024 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// pafng.cc author davis mcpherson davmcphe@cisco.com
+// based on paf.cc author Russ Combs <rcombs@sourcefire.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "pafng.h"
+
+#include "detection/detection_engine.h"
+#include "protocols/packet.h"
+#include "protocols/tcp.h"
+
+using namespace snort;
+
+//--------------------------------------------------------------------
+// private state
+//--------------------------------------------------------------------
+
+
+#define PAF_LIMIT_FUZZ 1500
+
+// 255 is max pseudo-random flush point; eth mtu ensures that maximum flushes
+// are not trimmed which throws off the tracking total in stream5_paf.c
+// max paf max = max datagram - eth mtu - 255 = 63780
+#define MAX_PAF_MAX (65535 -  PAF_LIMIT_FUZZ - 255)
+
+extern THREAD_LOCAL snort::ProfileStats pafPerfStats;
+
+//--------------------------------------------------------------------
+
+uint32_t ProtocolAwareFlusher::paf_flush (const PafAux& px, uint32_t* flags)
+{
+    uint32_t at = 0;
+    *flags &= ~(PKT_PDU_HEAD | PKT_PDU_TAIL);
+
+    switch ( px.ft )
+    {
+    case FT_NOP:
+        return -1;
+
+    case FT_SFP:
+        *flags = 0;
+        return -1;
+
+    case FT_PAF:
+        at = fpt;
+        *flags |= PKT_PDU_TAIL;
+        break;
+
+    case FT_LIMIT:
+        if (fpt > px.len)
+        {
+            at = px.len;
+            fpt -= px.len;
+        }
+        else
+        {
+            at = fpt;
+            fpt = px.len - fpt; // number of characters scanned but not flushing
+        }
+        break;
+
+    // use of px.len is suboptimal here because the actual amount
+    // flushed is determined later and can differ in certain cases
+    // such as exceeding s5_pkt->max_dsize.  the actual amount
+    // flushed would ideally be applied to fpt later.  for
+    // now we try to circumvent such cases so we track correctly.
+    //
+    // FIXIT-L max_dsize should no longer be exceeded since it excludes headers.
+    case FT_MAX:
+        at = px.len;
+        if ( fpt > px.len )
+            fpt -= px.len;
+        else
+            fpt = 0;
+        break;
+    }
+
+    if ( !at || !px.len )
+        return -1;
+
+    // safety - prevent seq + at < seq
+    if ( at > 0x7FFFFFFF )
+        at = 0x7FFFFFFF;
+
+    if ( !tot )
+        *flags |= PKT_PDU_HEAD;
+
+    if ( *flags & PKT_PDU_TAIL )
+        tot = 0;
+    else
+        tot += at;
+
+    return at;
+}
+
+//--------------------------------------------------------------------
+
+bool ProtocolAwareFlusher::paf_callback (PafAux& px, Packet* pkt, const uint8_t* data,
+    uint32_t len, uint32_t flags)
+{
+    fpt = 0;
+    state = splitter->scan(pkt, data, len, flags, &fpt);
+
+    if ( state == StreamSplitter::ABORT || state == StreamSplitter::STOP )
+        return false;
+
+    if ( state != StreamSplitter::SEARCH )
+    {
+        fpt += px.idx;
+        if ( fpt <= px.len )
+        {
+            px.idx = fpt;
+            return true;
+        }
+    }
+    px.idx = px.len;
+    return false;
+}
+
+//--------------------------------------------------------------------
+
+bool ProtocolAwareFlusher::paf_eval(PafAux& px, Packet* pkt, uint32_t flags,
+    const uint8_t* data, uint32_t len)
+{
+    switch ( state )
+    {
+    case StreamSplitter::SEARCH:
+        if ( px.len > px.idx )
+            return paf_callback(px, pkt, data, len, flags);
+
+        return false;
+
+    case StreamSplitter::FLUSH:
+        if ( px.len >= fpt )
+        {
+            px.ft = FT_PAF;
+            state = StreamSplitter::SEARCH;
+            return true;
+        }
+        if ( px.len >= splitter->max(pkt->flow) )
+        {
+            px.ft = FT_MAX;
+            return false;
+        }
+        return false;
+
+    case StreamSplitter::LIMIT:
+        // if we are within PAF_LIMIT_FUZZ character of paf_max ...
+        if ( px.len + PAF_LIMIT_FUZZ >= splitter->max(pkt->flow))
+        {
+            px.ft = FT_LIMIT;
+            state = StreamSplitter::LIMITED;
+            return false;
+        }
+        state = StreamSplitter::SEARCH;
+        return false;
+
+    case StreamSplitter::SKIP:
+        if ( px.len > fpt )
+        {
+            if ( fpt > px.idx )
+            {
+                uint32_t delta = fpt - px.idx;
+                if ( delta > len )
+                    return false;
+
+                data += delta;
+                len -= delta;
+            }
+            px.idx = fpt;
+            return paf_callback(px, pkt, data, len, flags);
+        }
+        return false;
+
+    case StreamSplitter::LIMITED:
+        // increment position by previously scanned bytes. set in paf_flush
+        state = StreamSplitter::SEARCH;
+        px.idx += fpt;
+        fpt = 0;
+        return true;
+
+    default:
+        // StreamSplitter::ABORT || StreamSplitter::START
+        break;
+    }
+
+    px.ft = FT_SFP;
+    return false;
+}
+
+//--------------------------------------------------------------------
+
+int32_t ProtocolAwareFlusher::paf_check (Packet* pkt, const uint8_t* data, uint32_t len,
+    uint32_t total, uint32_t seq, uint32_t* flags)
+{
+    Profile profile(pafPerfStats);  // cppcheck-suppress unreadVariable
+    PafAux px;
+
+    if ( !paf_initialized() )
+    {
+        seq_num = pos = seq;
+        fpt = tot = 0;
+        state = StreamSplitter::SEARCH;
+    }
+    else if ( SEQ_GT(seq, seq_num) )
+    {
+        // if seq jumped we have a gap.  Flush any queued data, then abort
+        px.len = total - len;
+
+        if ( px.len )
+        {
+            fpt = 0;
+            px.ft = FT_MAX;
+            state = StreamSplitter::ABORT;
+            return paf_flush(px, flags);
+        }
+        *flags = 0;
+        state = StreamSplitter::ABORT;
+        return -1;
+    }
+    else if ( SEQ_LEQ(seq + len, seq_num) )
+    {
+        return -1;
+    }
+    else if ( SEQ_LT(seq, seq_num) )
+    {
+        uint32_t shift = seq_num - seq;
+        data += shift;
+        len -= shift;
+    }
+
+    seq_num += len;
+    px.idx = total - len;
+
+    // if 'total' is greater than the maximum paf_max AND 'total' is greater
+    // than paf_max bytes (i.e. after we have finished analyzing the
+    // current segment, total bytes analyzed will be greater than the
+    // configured paf_max == splitter->max(), we must ensure a flush
+    // occurs at the paf_max byte. So, we manually set the data's length and
+    // total queued bytes (px.len) to guarantee that at most paf_max bytes will
+    // be analyzed and flushed since the last flush point.  It should also be
+    // noted that we perform the check here rather in in paf_flush() to
+    // avoid scanning the same data twice. The first scan would analyze the
+    // entire segment and the second scan would analyze this segments
+    // unflushed data.
+    if ( total >= MAX_PAF_MAX && total > splitter->max(pkt->flow) )
+    {
+        px.len = MAX_PAF_MAX;
+        len = len + px.len - total;
+    }
+    else
+        px.len = total;
+
+    do
+    {
+        px.ft = FT_NOP;
+        uint32_t idx = px.idx;
+
+        const bool cont = paf_eval(px, pkt, *flags, data, len);
+
+        if ( px.ft != FT_NOP )
+        {
+            int32_t fp = paf_flush(px, flags);
+            paf_jump(fp);
+            return fp;
+        }
+
+        if ( !cont )
+            break;
+
+        if ( px.idx > idx )
+        {
+            uint32_t shift = px.idx - idx;
+            if ( shift > len )
+                shift = len;
+            data += shift;
+            len -= shift;
+        }
+    }
+    while ( true );
+
+    if ( state == StreamSplitter::ABORT )
+        *flags = 0;
+
+    else if ( (state != StreamSplitter::FLUSH) && (px.len > splitter->max(pkt->flow)) )
+    {
+        px.ft = FT_MAX;
+        uint32_t fp = paf_flush(px, flags);
+        paf_jump(fp);
+        return fp;
+    }
+
+    return -1;
+}
+
diff --git a/src/stream/pafng.h b/src/stream/pafng.h
new file mode 100644 (file)
index 0000000..caf9c7f
--- /dev/null
@@ -0,0 +1,112 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2024-2024 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+//--------------------------------------------------------------------
+// protocol aware flushing stuff
+// pafng.h author davis mcpherson davmcphe@cisco.com
+//--------------------------------------------------------------------
+
+#ifndef PAFNG_H
+#define PAFNG_H
+
+#include "main/snort_types.h"
+#include "main/thread.h"
+#include "profiler/profiler_defs.h"
+#include "stream/stream_splitter.h"
+
+namespace snort
+{
+struct Packet;
+}
+
+enum FlushType
+{
+    FT_NOP,  // no flush
+    FT_SFP,  // abort paf
+    FT_PAF,  // flush to paf pt when len >= paf
+    FT_LIMIT,  // flush to paf pt, don't update flags
+    FT_MAX   // flush len when len >= max
+};
+
+struct PafAux
+{
+    FlushType ft;
+    uint32_t len;  // total bytes queued
+    uint32_t idx;  // offset from start of queued bytes
+};
+
+class  ProtocolAwareFlusher
+{
+public:
+    ProtocolAwareFlusher() { }
+    ~ProtocolAwareFlusher() { }
+
+    SO_PUBLIC void paf_setup(snort::StreamSplitter* ss)
+    {
+        splitter = ss;
+        state = snort::StreamSplitter::START;
+    }
+
+    void paf_reset ()
+    { state = snort::StreamSplitter::START; }
+
+    void paf_clear ()
+    { state = snort::StreamSplitter::ABORT; }
+
+    uint32_t paf_position ()
+    { return seq_num; }
+
+    SO_PUBLIC uint32_t paf_initialized ()
+    { return ( state != snort::StreamSplitter::START ); }
+
+    SO_PUBLIC void paf_initialize(uint32_t seq)
+    {
+        seq_num = pos = seq;
+        fpt = tot = 0;
+        state = snort::StreamSplitter::SEARCH;
+    }
+
+    uint32_t paf_active ()
+    { return ( state != snort::StreamSplitter::ABORT ); }
+
+    void paf_jump(uint32_t n)
+    {
+        pos += n;
+        seq_num = pos;
+    }
+
+    // called on each in order segment
+    SO_PUBLIC int32_t paf_check(snort::Packet* p, const uint8_t* data, uint32_t len,
+        uint32_t total, uint32_t seqnum, uint32_t* flags);
+
+    uint32_t seq_num = 0;    // stream cursor
+    uint32_t pos = 0;    // last flush position
+    uint32_t fpt = 0;    // current flush point
+    uint32_t tot = 0;    // total bytes flushed
+    snort::StreamSplitter::Status state = snort::StreamSplitter::START;  // current scan state
+
+private:
+    uint32_t paf_flush (const PafAux& px, uint32_t* flags);
+    bool paf_callback (PafAux&, snort::Packet*, const uint8_t* data, uint32_t len, uint32_t flags);
+    bool paf_eval (PafAux&, snort::Packet*, uint32_t flags, const uint8_t* data, uint32_t len);
+
+    snort::StreamSplitter* splitter = nullptr;
+};
+
+#endif
+
index cd20d669a3b98f2a250a13e4a79b575e39de2ece..7ac15a1d2ccacd421d4233cc78d577847cd71789 100644 (file)
@@ -46,7 +46,6 @@
 #include "utils/util.h"
 
 #include "tcp/tcp_session.h"
-#include "tcp/tcp_stream_session.h"
 #include "tcp/tcp_stream_tracker.h"
 
 using namespace snort;
@@ -221,7 +220,7 @@ void Stream::start_proxy(Flow* flow)
 {
     assert(flow and flow->session and flow->pkt_type == PktType::TCP);
 
-    TcpStreamSession* tcp_session = (TcpStreamSession*)flow->session;
+    TcpSession* tcp_session = (TcpSession*)flow->session;
     tcp_session->start_proxy();
 }
 
@@ -742,12 +741,6 @@ void Stream::disable_reassembly(Flow* flow)
     return flow->session->disable_reassembly(flow);
 }
 
-char Stream::get_reassembly_direction(Flow* flow)
-{
-    assert(flow && flow->session);
-    return flow->session->get_reassembly_direction();
-}
-
 bool Stream::is_stream_sequenced(Flow* flow, uint8_t dir)
 {
     assert(flow && flow->session);
@@ -770,7 +763,7 @@ uint16_t Stream::get_mss(Flow* flow, bool to_server)
 {
     assert(flow and flow->session and flow->pkt_type == PktType::TCP);
 
-    TcpStreamSession* tcp_session = (TcpStreamSession*)flow->session;
+    TcpSession* tcp_session = (TcpSession*)flow->session;
     return tcp_session->get_mss(to_server);
 }
 
@@ -778,7 +771,7 @@ uint8_t Stream::get_tcp_options_len(Flow* flow, bool to_server)
 {
     assert(flow and flow->session and flow->pkt_type == PktType::TCP);
 
-    TcpStreamSession* tcp_session = (TcpStreamSession*)flow->session;
+    TcpSession* tcp_session = (TcpSession*)flow->session;
     return tcp_session->get_tcp_options_len(to_server);
 }
 
@@ -794,7 +787,7 @@ bool Stream::can_set_no_ack_mode(Flow* flow)
 {
     assert(flow and flow->session and flow->pkt_type == PktType::TCP);
 
-    TcpStreamSession* tcp_session = (TcpStreamSession*)flow->session;
+    TcpSession* tcp_session = (TcpSession*)flow->session;
     return tcp_session->can_set_no_ack();
 }
 
@@ -802,7 +795,7 @@ bool Stream::set_no_ack_mode(Flow* flow, bool on_off)
 {
     assert(flow and flow->session and flow->pkt_type == PktType::TCP);
 
-    TcpStreamSession* tcp_session = (TcpStreamSession*)flow->session;
+    TcpSession* tcp_session = (TcpSession*)flow->session;
     return tcp_session->set_no_ack(on_off);
 }
 
@@ -811,9 +804,9 @@ void Stream::partial_flush(Flow* flow, bool to_server)
     if ( flow->pkt_type == PktType::TCP )
     {
         if ( to_server )
-            ((TcpStreamSession*)flow->session)->server.perform_partial_flush();
+            ((TcpSession*)flow->session)->server.perform_partial_flush();
         else
-            ((TcpStreamSession*)flow->session)->client.perform_partial_flush();
+            ((TcpSession*)flow->session)->client.perform_partial_flush();
     }
 }
 
@@ -822,7 +815,7 @@ bool Stream::get_held_pkt_seq(Flow* flow, uint32_t& seq)
     if (!flow or !flow->session or !(flow->pkt_type == PktType::TCP))
         return false;
 
-    TcpStreamSession* tcp_session = (TcpStreamSession*)flow->session;
+    TcpSession* tcp_session = (TcpSession*)flow->session;
 
     if (tcp_session->held_packet_dir == SSN_DIR_NONE)
         return false;
@@ -866,6 +859,7 @@ unsigned Stream::get_pub_id()
 TEST_CASE("Stream API", "[stream_api][stream]")
 {
     // initialization code here
+    TcpNormalizerFactory::initialize();
     Flow* flow = new Flow;
 
     SECTION("set/get ignore direction")
@@ -976,6 +970,7 @@ TEST_CASE("Stream API", "[stream_api][stream]")
     }
 
     delete flow;
+    TcpNormalizerFactory::term();
 }
 
 #endif
index 02c8b57b4224153374626ebdd2181374f65ce26e..f355e42eed66da8ca5a1f7a778c3789b87b577df 100644 (file)
@@ -157,8 +157,8 @@ public:
         Flow*, Packet* p, uint32_t gid, uint32_t sid,
         uint32_t eventId, uint32_t eventSecond);
 
+
     static void disable_reassembly(Flow*);
-    static char get_reassembly_direction(Flow*);
 
     // Returns true if stream data for the flow is in sequence, otherwise return false.
     static bool is_stream_sequenced(Flow*, uint8_t dir);
index fe7dec693db65472892a544719be8bd010fc00ea..0c96942befe54186d725873efe44bfb33f107d8b 100644 (file)
@@ -7,10 +7,10 @@ add_library( stream_tcp OBJECT
     held_packet_queue.h
     ips_stream_reassemble.cc
     ips_stream_size.cc
-    segment_overlap_editor.cc
-    segment_overlap_editor.h
     stream_tcp.cc
     stream_tcp.h
+    tcp_alerts.cc
+    tcp_alerts.h
     tcp_defs.h
     tcp_event_logger.cc
     tcp_event_logger.h
@@ -22,10 +22,16 @@ add_library( stream_tcp OBJECT
     tcp_normalizer.h
     tcp_normalizers.cc
     tcp_normalizers.h
+    tcp_overlap_resolver.cc
+    tcp_overlap_resolver.h
+    tcp_reassembler_ids.cc
+    tcp_reassembler_ids.h
+    tcp_reassembler_ips.cc
+    tcp_reassembler_ips.h
     tcp_reassembler.cc
     tcp_reassembler.h
-    tcp_reassemblers.cc
-    tcp_reassemblers.h
+    tcp_reassembly_segments.cc
+    tcp_reassembly_segments.h
     tcp_segment_descriptor.cc
     tcp_segment_descriptor.h
     tcp_segment_node.cc
@@ -66,8 +72,6 @@ add_library( stream_tcp OBJECT
     tcp_state_time_wait.h
     tcp_stream_config.cc
     tcp_stream_config.h
-    tcp_stream_session.cc
-    tcp_stream_session.h
     tcp_stream_tracker.cc
     tcp_stream_tracker.h
     tcp_trace.cc
index 39286e7c24cc43aa67398ee399c27283d9219807..e1a93ee0aed95f71367ac02309a6761cf43d233b 100644 (file)
@@ -108,49 +108,34 @@ IpsOption::EvalStatus ReassembleOption::eval(Cursor&, Packet* pkt)
     if (!pkt->flow || !pkt->ptrs.tcph)
         return NO_MATCH;
 
+    Flow* flow = (Flow*)pkt->flow;
+
+    if ( !srod.enable ) /* Turn it off */
+    {
+        if ( srod.direction & SSN_DIR_FROM_SERVER )
+            Stream::set_splitter(flow, true);
+
+        if ( srod.direction & SSN_DIR_FROM_CLIENT )
+            Stream::set_splitter(flow, false);
+    }
+    else
+    {
+        // FIXIT-M PAF need to instantiate service splitter?
+        // FIXIT-M PAF need to check for ips / on-data
+        if ( srod.direction & SSN_DIR_FROM_SERVER )
+            Stream::set_splitter(flow, true, new AtomSplitter(true));
+
+        if ( srod.direction & SSN_DIR_FROM_CLIENT )
+            Stream::set_splitter(flow, false, new AtomSplitter(false));
+    }
+
+    if (srod.fastpath)
     {
-        Flow* lwssn = (Flow*)pkt->flow;
-        TcpSession* tcpssn = (TcpSession*)lwssn->session;
-
-        if ( !srod.enable ) /* Turn it off */
-        {
-            if ( srod.direction & SSN_DIR_FROM_SERVER )
-            {
-                tcpssn->server.set_flush_policy(STREAM_FLPOLICY_IGNORE);
-                Stream::set_splitter(lwssn, true);
-            }
-
-            if ( srod.direction & SSN_DIR_FROM_CLIENT )
-            {
-                tcpssn->client.set_flush_policy(STREAM_FLPOLICY_IGNORE);
-                Stream::set_splitter(lwssn, false);
-            }
-        }
-        else
-        {
-            // FIXIT-M PAF need to instantiate service splitter?
-            // FIXIT-M PAF need to check for ips / on-data
-            if ( srod.direction & SSN_DIR_FROM_SERVER )
-            {
-                tcpssn->server.set_flush_policy(STREAM_FLPOLICY_ON_ACK);
-                Stream::set_splitter(lwssn, true, new AtomSplitter(true));
-            }
-
-            if ( srod.direction & SSN_DIR_FROM_CLIENT )
-            {
-                tcpssn->client.set_flush_policy(STREAM_FLPOLICY_ON_ACK);
-                Stream::set_splitter(lwssn, false, new AtomSplitter(false));
-            }
-        }
-
-        if (srod.fastpath)
-        {
-            /* Turn off inspection */
-            lwssn->ssn_state.ignore_direction |= srod.direction;
-            DetectionEngine::disable_all(pkt);
-
-            /* TBD: Set TF_FORCE_FLUSH ? */
-        }
+        /* Turn off inspection */
+        flow->ssn_state.ignore_direction |= srod.direction;
+        DetectionEngine::disable_all(pkt);
+
+        /* TBD: Set TF_FORCE_FLUSH ? */
     }
 
     if (srod.alert)
@@ -290,6 +275,8 @@ TEST_CASE("IPS Stream Reassemble", "[ips_stream_reassemble][stream_tcp]")
     ReassembleModule* reassembler = ( ReassembleModule* )ips_stream_reassemble->mod_ctor();
     REQUIRE( reassembler != nullptr );
 
+    TcpNormalizerFactory::initialize();
+
     Flow* flow = new Flow;
     Packet* pkt = get_syn_packet(flow);
     Cursor cursor(pkt);
@@ -319,6 +306,7 @@ TEST_CASE("IPS Stream Reassemble", "[ips_stream_reassemble][stream_tcp]")
     }
 #endif
     release_packet(pkt);
+    TcpNormalizerFactory::term();
     delete flow;
     ips_stream_reassemble->mod_dtor(reassembler);
 }
diff --git a/src/stream/tcp/segment_overlap_editor.cc b/src/stream/tcp/segment_overlap_editor.cc
deleted file mode 100644 (file)
index c6ddcd7..0000000
+++ /dev/null
@@ -1,481 +0,0 @@
-//--------------------------------------------------------------------------
-// Copyright (C) 2015-2024 Cisco and/or its affiliates. All rights reserved.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License Version 2 as published
-// by the Free Software Foundation.  You may not use, modify or distribute
-// this program under any other version of the GNU General Public License.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License along
-// with this program; if not, write to the Free Software Foundation, Inc.,
-// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-//--------------------------------------------------------------------------
-
-// segment_overlap_editor.cc author davis mcpherson <davmcphe@cisco.com>
-// Created on: Oct 11, 2015
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "segment_overlap_editor.h"
-
-#include "detection/detection_engine.h"
-#include "log/messages.h"
-
-#include "tcp_module.h"
-#include "tcp_normalizers.h"
-#include "tcp_session.h"
-
-using namespace snort;
-
-void SegmentOverlapState::init_sos(TcpSession* ssn, StreamPolicy pol)
-{
-    session = ssn;
-    reassembly_policy = pol;
-
-    seglist.reset();
-
-    seglist_base_seq = 0;
-    seg_count = 0;
-    seg_bytes_total = 0;
-    seg_bytes_logical = 0;
-    total_bytes_queued = 0;
-    total_segs_queued = 0;
-    overlap_count = 0;
-
-    tsd = nullptr;
-    left = nullptr;
-    right = nullptr;
-    rdata = nullptr;
-
-    seq = 0;
-    seq_end = 0;
-    len = 0;
-    overlap = 0;
-    slide = 0;
-    trunc_len = 0;
-    rsize = 0;
-    rseq = 0;
-    keep_segment = true;
-
-    tcp_ips_data = Normalize_GetMode(NORM_TCP_IPS);
-}
-
-void SegmentOverlapState::init_soe(
-    TcpSegmentDescriptor& tsd, TcpSegmentNode* left, TcpSegmentNode* right)
-{
-    this->tsd = &tsd;
-    this->left = left;
-    this->right = right;
-
-    seq = tsd.get_seq();
-    seq_end = tsd.get_end_seq();
-    len = tsd.get_len();
-
-    overlap = 0;
-    slide = 0;
-    trunc_len = 0;
-
-    rdata = tsd.get_pkt()->data;
-    rsize = tsd.get_len();
-    rseq = tsd.get_seq();
-
-    keep_segment = true;
-}
-
-bool SegmentOverlapEditor::is_segment_retransmit(
-    TcpReassemblerState& trs, bool* full_retransmit)
-{
-    // Don't want to count retransmits as overlaps or do anything
-    // else with them.  Account for retransmits of multiple PDUs
-    // in one segment.
-    bool* pb = (trs.sos.rseq == trs.sos.tsd->get_seq()) ? full_retransmit : nullptr;
-
-    if ( trs.sos.right->is_retransmit(trs.sos.rdata, trs.sos.rsize,
-        trs.sos.rseq, trs.sos.right->i_len, pb) )
-    {
-        trs.sos.tsd->set_retransmit_flag();
-
-        if ( !(*full_retransmit) )
-        {
-            trs.sos.rdata += trs.sos.right->i_len;
-            trs.sos.rsize -= trs.sos.right->i_len;
-            trs.sos.rseq += trs.sos.right->i_len;
-            trs.sos.seq += trs.sos.right->i_len;
-            trs.sos.left = trs.sos.right;
-            trs.sos.right = trs.sos.right->next;
-        }
-        else
-            trs.sos.rsize = 0;
-
-        if ( trs.sos.rsize == 0 )
-        {
-            // All data was retransmitted
-            snort::DetectionEngine::disable_content(trs.sos.tsd->get_pkt());
-            trs.sos.keep_segment = false;
-        }
-
-        return true;
-    }
-
-    return false;
-}
-
-void SegmentOverlapEditor::eval_left(TcpReassemblerState& trs)
-{
-    if ( trs.sos.left )
-        insert_left_overlap(trs);
-}
-
-void SegmentOverlapEditor::eval_right(TcpReassemblerState& trs)
-{
-    while ( trs.sos.right && SEQ_LT(trs.sos.right->i_seq, trs.sos.seq_end) )
-    {
-        trs.sos.trunc_len = 0;
-
-        assert(SEQ_LEQ(trs.sos.seq, trs.sos.right->i_seq));
-        trs.sos.overlap = ( int )( trs.sos.seq_end - trs.sos.right->i_seq );
-
-        // Treat sequence number overlap as a retransmission,
-        // only check right side since left side happens rarely
-        trs.sos.session->flow->call_handlers(trs.sos.tsd->get_pkt(), false);
-        if ( trs.sos.overlap < trs.sos.right->i_len )
-        {
-            if ( trs.sos.right->is_retransmit(trs.sos.rdata, trs.sos.rsize,
-                trs.sos.rseq, trs.sos.right->i_len, nullptr) )
-            {
-                // All data was retransmitted
-                trs.sos.tsd->set_retransmit_flag();
-                snort::DetectionEngine::disable_content(trs.sos.tsd->get_pkt());
-                trs.sos.keep_segment = false;
-                tcpStats.full_retransmits++;
-            }
-            else
-            {
-                tcpStats.overlaps++;
-                trs.sos.overlap_count++;
-                insert_right_overlap(trs);
-            }
-
-            break;
-        }
-        else  // Full overlap
-        {
-            bool full_retransmit = false;
-            // Don't want to count retransmits as overlaps or do anything
-            // else with them.  Account for retransmits of multiple PDUs
-            // in one segment.
-            if ( is_segment_retransmit(trs, &full_retransmit) )
-            {
-                if ( full_retransmit )
-                {
-                    tcpStats.full_retransmits++;
-                    break;
-                }
-
-                continue;
-            }
-
-            tcpStats.overlaps++;
-            trs.sos.overlap_count++;
-            insert_full_overlap(trs);
-
-            if ( trs.sos.keep_segment == false )
-                return;
-        }
-    }
-}
-
-void SegmentOverlapEditor::drop_old_segment(TcpReassemblerState& trs)
-{
-    TcpSegmentNode* drop_seg = trs.sos.right;
-    trs.sos.right = trs.sos.right->next;
-    delete_reassembly_segment(trs, drop_seg);
-}
-
-void SegmentOverlapEditor::left_overlap_keep_first(TcpReassemblerState& trs)
-{
-    // NOTE that overlap will always be less than left->size since
-    // seq is always greater than left->seq
-    assert(SEQ_GT(trs.sos.seq, trs.sos.left->i_seq));
-
-    trs.sos.overlap = trs.sos.left->i_seq + trs.sos.left->i_len - trs.sos.seq;
-
-    if ( trs.sos.len < trs.sos.overlap )
-        trs.sos.overlap = trs.sos.len;
-
-    if ( trs.sos.overlap > 0 )
-    {
-        tcpStats.overlaps++;
-        trs.sos.overlap_count++;
-
-        if ( SEQ_GT(trs.sos.left->i_seq + trs.sos.left->i_len, trs.sos.seq_end) )
-        {
-            if (trs.sos.tcp_ips_data == NORM_MODE_ON)
-            {
-                unsigned offset = trs.sos.tsd->get_seq() - (trs.sos.left->i_seq - trs.sos.left->o_offset);
-                trs.sos.tsd->rewrite_payload(0, trs.sos.left->data + offset);
-            }
-            norm_stats[PC_TCP_IPS_DATA][trs.sos.tcp_ips_data]++;
-        }
-        else
-        {
-            if ( trs.sos.tcp_ips_data == NORM_MODE_ON )
-            {
-                unsigned offset = trs.sos.tsd->get_seq() - (trs.sos.left->i_seq - trs.sos.left->o_offset);
-                unsigned length = trs.sos.left->i_seq + trs.sos.left->i_len - trs.sos.tsd->get_seq();
-                trs.sos.tsd->rewrite_payload(0, trs.sos.left->data + offset, length);
-            }
-
-            norm_stats[PC_TCP_IPS_DATA][trs.sos.tcp_ips_data]++;
-        }
-
-        trs.sos.seq += trs.sos.overlap;
-    }
-}
-
-void SegmentOverlapEditor::left_overlap_trim_first(TcpReassemblerState& trs)
-{
-    assert(SEQ_GT(trs.sos.seq, trs.sos.left->i_seq));
-
-    trs.sos.overlap = trs.sos.left->i_seq + trs.sos.left->i_len - trs.sos.seq;
-
-    if ( trs.sos.overlap > 0 )
-    {
-        tcpStats.overlaps++;
-        trs.sos.overlap_count++;
-
-        if ( SEQ_GEQ(trs.sos.left->i_seq + trs.sos.left->i_len, trs.sos.seq + trs.sos.len)  )
-        {
-            // existing packet overlaps new on both sides.  Drop the new data.
-            trs.sos.seq += trs.sos.len;
-        }
-        else
-        {
-            /* Otherwise, trim the old data accordingly */
-            trs.sos.left->c_len -= ( int16_t )trs.sos.overlap;
-            trs.sos.left->i_len -= ( int16_t )trs.sos.overlap;
-            trs.sos.seg_bytes_logical -= trs.sos.overlap;
-        }
-    }
-}
-
-void SegmentOverlapEditor::left_overlap_keep_last(TcpReassemblerState& trs)
-{
-    assert(SEQ_GT(trs.sos.seq, trs.sos.left->i_seq));
-
-    trs.sos.overlap = trs.sos.left->i_seq + trs.sos.left->i_len - trs.sos.seq;
-
-    if ( trs.sos.overlap > 0 )
-    {
-        tcpStats.overlaps++;
-        trs.sos.overlap_count++;
-
-        /* True "Last" policy" */
-        if ( SEQ_GT(trs.sos.left->i_seq + trs.sos.left->i_len, trs.sos.seq + trs.sos.len) )
-        {
-            /* New data is overlapped on both sides by existing data.  Existing data needs to be
-             * split and the new data inserted in the middle.
-             * Need to duplicate left. Adjust that seq by + (seq + len) and
-             * size by - (seq + len - left->i_seq).
-             */
-            dup_reassembly_segment(trs, trs.sos.left, &trs.sos.right);
-
-            trs.sos.left->c_len -= (int16_t)trs.sos.overlap;
-            trs.sos.left->i_len -= (int16_t)trs.sos.overlap;
-
-            trs.sos.right->i_seq = trs.sos.seq + trs.sos.len;
-            trs.sos.right->c_seq = trs.sos.right->i_seq;
-            uint16_t delta = (int16_t)(trs.sos.right->i_seq - trs.sos.left->i_seq);
-            trs.sos.right->c_len -= delta;
-            trs.sos.right->i_len -= delta;
-            trs.sos.right->offset += delta;
-            trs.sos.right->o_offset += delta;
-
-            trs.sos.seg_bytes_logical -= trs.sos.len;
-        }
-        else
-        {
-            trs.sos.left->c_len -= (int16_t)trs.sos.overlap;
-            trs.sos.left->i_len -= (int16_t)trs.sos.overlap;
-            trs.sos.seg_bytes_logical -= trs.sos.overlap;
-        }
-    }
-}
-
-void SegmentOverlapEditor::right_overlap_truncate_existing(TcpReassemblerState& trs)
-{
-    if ( SEQ_EQ(trs.sos.right->i_seq, trs.sos.seq) &&
-        ( trs.sos.reassembly_policy != StreamPolicy::OS_LAST ) )
-    {
-        trs.sos.seq += trs.sos.overlap;
-    }
-    else
-    {
-        /* partial overlap */
-        trs.sos.right->i_seq += trs.sos.overlap;
-        trs.sos.right->c_seq = trs.sos.right->i_seq;
-        trs.sos.right->offset += trs.sos.overlap;
-        trs.sos.right->o_offset += trs.sos.overlap;
-        trs.sos.right->c_len -= (int16_t)trs.sos.overlap;
-        trs.sos.right->i_len -= ( int16_t )trs.sos.overlap;
-        trs.sos.seg_bytes_logical -= trs.sos.overlap;
-        trs.sos.total_bytes_queued -= trs.sos.overlap;
-    }
-}
-
-void SegmentOverlapEditor::right_overlap_truncate_new(TcpReassemblerState& trs)
-{
-    if (trs.sos.tcp_ips_data == NORM_MODE_ON)
-    {
-        unsigned offset = trs.sos.right->i_seq - trs.sos.tsd->get_seq();
-        unsigned length = trs.sos.tsd->get_seq() + trs.sos.tsd->get_len() - trs.sos.right->i_seq;
-        trs.sos.tsd->rewrite_payload(offset, trs.sos.right->data + trs.sos.right->o_offset, length);
-    }
-
-    norm_stats[PC_TCP_IPS_DATA][trs.sos.tcp_ips_data]++;
-    trs.sos.trunc_len = trs.sos.overlap;
-}
-
-// REASSEMBLY_POLICY_FIRST:
-// REASSEMBLY_POLICY_VISTA:
-void SegmentOverlapEditor::full_right_overlap_truncate_new(TcpReassemblerState& trs)
-{
-
-    if ( trs.sos.tcp_ips_data == NORM_MODE_ON )
-    {
-        unsigned offset = trs.sos.right->i_seq - trs.sos.tsd->get_seq();
-
-        if ( !offset && zwp_data_mismatch(trs, *trs.sos.tsd, trs.sos.right->i_len))
-        {
-            trs.tracker->normalizer.session_blocker(*trs.sos.tsd);
-            trs.sos.keep_segment = false;
-            return;
-        }
-
-        trs.sos.tsd->rewrite_payload(offset, trs.sos.right->data + trs.sos.right->o_offset, trs.sos.right->i_len);
-    }
-
-    norm_stats[PC_TCP_IPS_DATA][trs.sos.tcp_ips_data]++;
-
-    if ( SEQ_EQ(trs.sos.right->i_seq, trs.sos.seq) )
-    {
-        /* Overlap is greater than or equal to right->size
-         * slide gets set before insertion */
-        trs.sos.seq += trs.sos.right->i_len;
-        trs.sos.left = trs.sos.right;
-        trs.sos.right = trs.sos.right->next;
-    }
-    else
-    {
-        // seq is less than right->i_seq,  set trunc length and slide
-        //  and insert chunk before current right segment...
-        trs.sos.trunc_len = trs.sos.overlap;
-        trs.sos.slide = trs.sos.seq - trs.sos.tsd->get_seq();
-        add_reassembly_segment(trs, *trs.sos.tsd, trs.sos.len, trs.sos.slide,
-            trs.sos.trunc_len, trs.sos.seq, trs.sos.left);
-
-        // Set seq to end of right since overlap was greater than or equal to right->size and
-        // inserted seq has been truncated to beginning of right and reset trunc length to 0
-        // since we may fall out of loop if next right is null
-        trs.sos.seq = trs.sos.right->i_seq + trs.sos.right->i_len;
-        trs.sos.left = trs.sos.right;
-        trs.sos.right = trs.sos.right->next;
-        trs.sos.trunc_len = 0;
-    }
-}
-
-// REASSEMBLY_POLICY_WINDOWS:
-// REASSEMBLY_POLICY_WINDOWS2K3:
-// REASSEMBLY_POLICY_BSD:
-// REASSEMBLY_POLICY_MACOS:
-void SegmentOverlapEditor::full_right_overlap_os1(TcpReassemblerState& trs)
-{
-    if ( SEQ_GEQ(trs.sos.seq_end, trs.sos.right->i_seq + trs.sos.right->i_len) and
-        SEQ_LT(trs.sos.seq, trs.sos.right->i_seq) )
-    {
-        drop_old_segment(trs);
-    }
-    else
-        full_right_overlap_truncate_new(trs);
-}
-
-// REASSEMBLY_POLICY_LINUX:
-// REASSEMBLY_POLICY_HPUX10:
-// REASSEMBLY_POLICY_IRIX:
-void SegmentOverlapEditor::full_right_overlap_os2(TcpReassemblerState& trs)
-{
-    if ( SEQ_GEQ(trs.sos.seq_end, trs.sos.right->i_seq + trs.sos.right->i_len) and
-        SEQ_LT(trs.sos.seq, trs.sos.right->i_seq) )
-    {
-        drop_old_segment(trs);
-    }
-    else if ( SEQ_GT(trs.sos.seq_end, trs.sos.right->i_seq + trs.sos.right->i_len) and
-        SEQ_EQ(trs.sos.seq, trs.sos.right->i_seq) )
-    {
-        drop_old_segment(trs);
-    }
-    else
-        full_right_overlap_truncate_new(trs);
-}
-
-// REASSEMBLY_POLICY_HPUX11:
-// REASSEMBLY_POLICY_SOLARIS:
-void SegmentOverlapEditor::full_right_overlap_os3(TcpReassemblerState& trs)
-{
-    // If this packet is wholly overlapping and the same size as a previous one and we have not
-    // received the one immediately preceding, we take the FIRST.
-    if ( SEQ_EQ(trs.sos.right->i_seq, trs.sos.seq) && (trs.sos.right->i_len == trs.sos.len)
-        && (trs.sos.left && !SEQ_EQ(trs.sos.left->i_seq + trs.sos.left->i_len, trs.sos.seq)) )
-    {
-        trs.sos.trunc_len = trs.sos.right->i_len;
-        trs.sos.rdata += trs.sos.right->i_len;
-        trs.sos.rsize -= trs.sos.right->i_len;
-        trs.sos.rseq += trs.sos.right->i_len;
-        trs.sos.seq += trs.sos.right->i_len;
-        trs.sos.left = trs.sos.right;
-        trs.sos.right = trs.sos.right->next;
-    }
-    else
-        drop_old_segment(trs);
-}
-
-//  REASSEMBLY_POLICY_OLD_LINUX:
-//  REASSEMBLY_POLICY_LAST:
-void SegmentOverlapEditor::full_right_overlap_os4(TcpReassemblerState& trs)
-{ drop_old_segment(trs); }
-
-void SegmentOverlapEditor::full_right_overlap_os5(TcpReassemblerState& trs)
-{
-    full_right_overlap_truncate_new(trs);
-}
-
-bool SegmentOverlapEditor::zwp_data_mismatch(
-    TcpReassemblerState& trs, TcpSegmentDescriptor& tsd, uint32_t overlap)
-{
-    if ( overlap == MAX_ZERO_WIN_PROBE_LEN
-        and trs.sos.right->i_seq == trs.tracker->normalizer.get_zwp_seq()
-        and (trs.sos.right->data[0] != tsd.get_pkt()->data[0]) )
-    {
-        return tsd.is_nap_policy_inline();
-    }
-
-    return false;
-}
-
-void SegmentOverlapEditor::print(TcpReassemblerState& trs)
-{
-    LogMessage("    seglist_base_seq:   %X\n", trs.sos.seglist_base_seq);
-    LogMessage("    seglist head:       %p\n", (void*)trs.sos.seglist.head);
-    LogMessage("    seglist tail:       %p\n", (void*)trs.sos.seglist.tail);
-    LogMessage("    seglist current:    %p\n", (void*)trs.sos.seglist.cur_rseg);
-    LogMessage("    seg_count:          %d\n", trs.sos.seg_count);
-    LogMessage("    seg_bytes_total:    %d\n", trs.sos.seg_bytes_total);
-    LogMessage("    seg_bytes_logical:  %d\n", trs.sos.seg_bytes_logical);
-}
diff --git a/src/stream/tcp/segment_overlap_editor.h b/src/stream/tcp/segment_overlap_editor.h
deleted file mode 100644 (file)
index 1901c3a..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-//--------------------------------------------------------------------------
-// Copyright (C) 2015-2024 Cisco and/or its affiliates. All rights reserved.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License Version 2 as published
-// by the Free Software Foundation.  You may not use, modify or distribute
-// this program under any other version of the GNU General Public License.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License along
-// with this program; if not, write to the Free Software Foundation, Inc.,
-// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-//--------------------------------------------------------------------------
-
-// segment_overlap_editor.h author davis mcpherson <davmcphe@cisco.com>
-// Created on: Oct 11, 2015
-
-#ifndef SEGMENT_OVERLAP_EDITOR_H
-#define SEGMENT_OVERLAP_EDITOR_H
-
-#include <vector>
-
-#include "normalize/norm_stats.h"
-#include "stream/paf.h"
-#include "stream/stream.h"
-#include "tcp_segment_node.h"
-
-class TcpSession;
-class TcpStreamTracker;
-
-struct SegmentOverlapState
-{
-    TcpSession* session;
-    TcpSegmentDescriptor* tsd;
-    TcpSegmentNode* left;
-    TcpSegmentNode* right;
-    const uint8_t* rdata;
-
-    TcpSegmentList seglist;
-    uint32_t seglist_base_seq;      /* seq of first queued segment */
-    uint32_t seg_count;             /* number of current queued segments */
-    uint32_t seg_bytes_total;       /* total bytes currently queued */
-    uint32_t seg_bytes_logical;     /* logical bytes queued (total - overlaps) */
-    uint32_t total_bytes_queued;    /* total bytes queued (life of session) */
-    uint32_t total_segs_queued;     /* number of segments queued (life) */
-    uint32_t overlap_count;         /* overlaps encountered */
-
-    uint32_t seq;
-    uint32_t seq_end;
-    uint32_t rseq;
-
-    int32_t overlap;
-    int32_t slide;
-    int32_t trunc_len;
-
-    uint16_t len;
-    uint16_t rsize;
-    int8_t tcp_ips_data;
-    StreamPolicy reassembly_policy;
-
-    bool keep_segment;
-
-    ~SegmentOverlapState()
-    { seglist.reset(); }
-
-    void init_sos(TcpSession*, StreamPolicy);
-    void init_soe(TcpSegmentDescriptor& tsd, TcpSegmentNode* left, TcpSegmentNode* right);
-};
-
-struct StreamAlertInfo : snort::AlertInfo
-{
-    StreamAlertInfo(uint32_t gid_, uint32_t sid_, uint32_t seq_num_ = 0, uint32_t id_ = 0, uint32_t ts_ = 0)
-        : snort::AlertInfo(gid_, sid_, id_, ts_), seq(seq_num_)
-    {}
-
-    uint32_t seq;
-};
-
-struct TcpReassemblerState
-{
-    SegmentOverlapState sos;
-    TcpStreamTracker* tracker;
-    uint32_t flush_count;   // number of flushed queued segments
-    uint32_t xtradata_mask; // extra data available to log
-    std::vector<StreamAlertInfo> alerts;
-    uint8_t ignore_dir;
-    uint8_t packet_dir;
-    bool server_side;
-    PAF_State paf_state;
-};
-
-class SegmentOverlapEditor
-{
-protected:
-    SegmentOverlapEditor() = default;
-    virtual ~SegmentOverlapEditor() = default;
-
-    void eval_left(TcpReassemblerState&);
-    void eval_right(TcpReassemblerState&);
-
-    virtual bool is_segment_retransmit(TcpReassemblerState&, bool*);
-    virtual void drop_old_segment(TcpReassemblerState&);
-    virtual bool zwp_data_mismatch(TcpReassemblerState&, TcpSegmentDescriptor&, uint32_t);
-
-    virtual void left_overlap_keep_first(TcpReassemblerState&);
-    virtual void left_overlap_trim_first(TcpReassemblerState&);
-    virtual void left_overlap_keep_last(TcpReassemblerState&);
-    virtual void right_overlap_truncate_existing(TcpReassemblerState&);
-    virtual void right_overlap_truncate_new(TcpReassemblerState&);
-    virtual void full_right_overlap_truncate_new(TcpReassemblerState&);
-    virtual void full_right_overlap_os1(TcpReassemblerState&);
-    virtual void full_right_overlap_os2(TcpReassemblerState&);
-    virtual void full_right_overlap_os3(TcpReassemblerState&);
-    virtual void full_right_overlap_os4(TcpReassemblerState&);
-    virtual void full_right_overlap_os5(TcpReassemblerState&);
-
-    virtual void insert_left_overlap(TcpReassemblerState&) = 0;
-    virtual void insert_right_overlap(TcpReassemblerState&) = 0;
-    virtual void insert_full_overlap(TcpReassemblerState&) = 0;
-
-    virtual void add_reassembly_segment(
-        TcpReassemblerState&, TcpSegmentDescriptor&, uint16_t, uint32_t,
-        uint32_t, uint32_t, TcpSegmentNode*) = 0;
-
-    virtual void dup_reassembly_segment(TcpReassemblerState&, TcpSegmentNode*, TcpSegmentNode**) = 0;
-    virtual int delete_reassembly_segment(TcpReassemblerState&, TcpSegmentNode*) = 0;
-    virtual void print(TcpReassemblerState&);
-};
-
-#endif
index f8b3374a1650767c8e6fa383abc64bdca67a9627..cb9c177d42877feae7db1e0ba3e6d9d796587a6d 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "tcp_ha.h"
 #include "tcp_module.h"
+#include "tcp_overlap_resolver.h"
 #include "tcp_session.h"
-#include "tcp_reassemblers.h"
 #include "tcp_state_machine.h"
 
 using namespace snort;
@@ -112,14 +112,14 @@ static void tcp_dtor(Inspector* p)
 static void stream_tcp_pinit()
 {
     TcpStateMachine::initialize();
-    TcpReassemblerFactory::initialize();
+    TcpOverlapResolverFactory::initialize();
     TcpNormalizerFactory::initialize();
 }
 
 static void stream_tcp_pterm()
 {
     TcpStateMachine::term();
-    TcpReassemblerFactory::term();
+    TcpOverlapResolverFactory::term();
     TcpNormalizerFactory::term();
 }
 
diff --git a/src/stream/tcp/tcp_alerts.cc b/src/stream/tcp/tcp_alerts.cc
new file mode 100644 (file)
index 0000000..bf01d5a
--- /dev/null
@@ -0,0 +1,108 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-2024 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// tcp_alerts.cc author davis mcpherson <davmcphe@cisco.com>
+// Created on: Nov 7, 2023
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cassert>
+
+#include "tcp_alerts.h"
+
+#include "detection/context_switcher.h"
+
+#include "tcp_session.h"
+
+using namespace snort;
+
+static void purge_alerts_callback_ackd(IpsContext *c)
+{
+    TcpSession *session = (TcpSession*) c->packet->flow->session;
+
+    if (c->packet->is_from_server())
+        session->client.tcp_alerts.purge_alerts(c->packet->flow);
+    else
+        session->server.tcp_alerts.purge_alerts(c->packet->flow);
+}
+
+static void purge_alerts_callback_ips(IpsContext *c)
+{
+    TcpSession *session = (TcpSession*) c->packet->flow->session;
+
+    if (c->packet->is_from_server())
+        session->server.tcp_alerts.purge_alerts(c->packet->flow);
+    else
+        session->client.tcp_alerts.purge_alerts(c->packet->flow);
+}
+
+bool TcpAlerts::add_alert(uint32_t gid, uint32_t sid)
+{
+    assert(
+        alerts.size()
+            <= (uint32_t )(get_ips_policy()->rules_loaded + get_ips_policy()->rules_shared));
+
+    if (!this->check_alerted(gid, sid))
+        alerts.emplace_back(gid, sid);
+
+    return true;
+}
+
+bool TcpAlerts::check_alerted(uint32_t gid, uint32_t sid)
+{
+    return std::any_of(alerts.cbegin(), alerts.cend(), [gid, sid](const StreamAlertInfo &alert)
+    {   return alert.gid == gid && alert.sid == sid;});
+}
+
+int TcpAlerts::update_alert(uint32_t gid, uint32_t sid, uint32_t event_id, uint32_t event_second)
+{
+    // FIXIT-M comparison of seq_num is wrong, compare value is always 0, should be seq_num of wire packet
+    uint32_t seq_num = 0;
+
+    auto it = std::find_if(alerts.begin(), alerts.end(),
+        [gid, sid, seq_num](const StreamAlertInfo &alert)
+        {   return alert.gid == gid && alert.sid == sid && SEQ_EQ(alert.seq, seq_num);});
+    if (it != alerts.end())
+    {
+        (*it).event_id = event_id;
+        (*it).event_second = event_second;
+        return 0;
+    }
+
+    return -1;
+}
+
+void TcpAlerts::purge_alerts(Flow* flow)
+{
+    for (auto &alert : alerts)
+        Stream::log_extra_data(flow, xtradata_mask, alert);
+
+    if (!flow->is_suspended())
+        alerts.clear();
+}
+
+void TcpAlerts::purge_alerts(Packet& last_pdu, bool ips_enabled)
+{
+    if ( ips_enabled )
+        last_pdu.context->register_post_callback(purge_alerts_callback_ips);
+    else
+        last_pdu.context->register_post_callback(purge_alerts_callback_ackd);
+}
+
diff --git a/src/stream/tcp/tcp_alerts.h b/src/stream/tcp/tcp_alerts.h
new file mode 100644 (file)
index 0000000..1139453
--- /dev/null
@@ -0,0 +1,73 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2024 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// tcp_alerts.h author davis mcpherson <davmcphe@cisco.com>
+// Created on: Jul 31, 2015
+
+#ifndef TCP_ALERTS_H
+#define TCP_ALERTS_H
+
+#include <cstdint>
+
+#include "protocols/packet.h"
+#include "stream/stream.h"
+
+struct StreamAlertInfo: snort::AlertInfo
+{
+    StreamAlertInfo(uint32_t gid_, uint32_t sid_, uint32_t seq_num_ = 0, uint32_t id_ = 0,
+        uint32_t ts_ = 0) : snort::AlertInfo(gid_, sid_, id_, ts_), seq(seq_num_)
+    { }
+
+    uint32_t seq;
+};
+
+class TcpAlerts
+{
+public:
+    TcpAlerts() = default;
+
+    void clear()
+    {
+        xtradata_mask = 0;
+        alerts.clear();
+    }
+
+    bool add_alert(uint32_t gid, uint32_t sid);
+    bool check_alerted(uint32_t gid, uint32_t sid);
+    int update_alert(uint32_t gid, uint32_t sid, uint32_t event_id, uint32_t event_second);
+    void purge_alerts(snort::Flow* flow);
+    void purge_alerts(snort::Packet& last_pdu, bool ips_enabled);
+
+    void set_xtradata_mask(uint32_t mask)
+    {
+        xtradata_mask = mask;
+    }
+
+    uint32_t get_xtradata_mask() const
+    {
+        return xtradata_mask;
+    }
+
+private:
+
+    uint32_t xtradata_mask = 0; // extra data available to log
+    std::vector<StreamAlertInfo> alerts;
+
+};
+
+#endif
index 4bb27b4298f966be596bbb041b8ccf0a398a3956..c40ab61f59ea0a854ffeaecd58104b66214b2b10 100644 (file)
@@ -49,9 +49,6 @@ struct Packet;
 #define PAWS_WINDOW         60
 #define PAWS_24DAYS         2073600         /* 24 days in seconds */
 
-#define STREAM_UNALIGNED       0
-#define STREAM_ALIGNED         1
-
 #define STREAM_DEFAULT_MAX_SMALL_SEG_SIZE 0    /* disabled */
 #define STREAM_DEFAULT_CONSEC_SMALL_SEGS 0     /* disabled */
 
index 4681baa63d70f2cc1c5bf32317fcd1daf1b0305d..fde1c11c1ed74fbbb56fc2493e32b0d842834169 100644 (file)
 
 #include "tcp_normalizer.h"
 
-#include "stream/stream.h"
+#include "detection/detection_engine.h"
 #include "packet_io/packet_tracer.h"
+#include "stream/stream.h"
+#include "trace/trace.h"
 #include "trace/trace_api.h"
-#include "tcp_module.h"
-#include "tcp_stream_session.h"
+
+#include "tcp_session.h"
 #include "tcp_stream_tracker.h"
 
 using namespace snort;
@@ -78,7 +80,7 @@ TcpNormalizer::NormStatus TcpNormalizer::apply_normalizations(
                 tcpStats.zero_win_probes++;
                 set_zwp_seq(tns, seq);
                 log_drop_reason(tns, tsd, inline_mode, "stream", 
-                "Normalizer: Maximum Zero Window Probe length supported at a time is 1 byte\n");
+                    "Normalizer: Maximum Zero Window Probe length supported at a time is 1 byte\n");
                 trim_win_payload(tns, tsd, MAX_ZERO_WIN_PROBE_LEN, inline_mode);
             }
         }
index a50397b0a38deada338471466ffc997eae5eee19..1d00559438486058e1733c941d96d90c57270a1d 100644 (file)
 #include "normalize/norm_stats.h"
 #include "protocols/tcp_options.h"
 
-class TcpStreamSession;
+class TcpSession;
 class TcpStreamTracker;
 class TcpSegmentDescriptor;
 class TcpNormalizer;
 
 struct TcpNormalizerState
 {
-    TcpStreamSession* session = nullptr;
+    TcpSession* session = nullptr;
     TcpStreamTracker* tracker = nullptr;
     TcpStreamTracker* peer_tracker = nullptr;
     TcpNormalizer* prev_norm = nullptr;
@@ -90,7 +90,8 @@ public:
     virtual int handle_repeated_syn(State&, TcpSegmentDescriptor&) = 0;
     virtual uint16_t set_urg_offset(State&, const snort::tcp::TCPHdr* tcph, uint16_t dsize);
     virtual void set_zwp_seq(State&, uint32_t seq);
-    virtual void log_drop_reason(State&, const TcpSegmentDescriptor&, bool inline_mode, const char *issuer, const std::string& log);
+    virtual void log_drop_reason(State&, const TcpSegmentDescriptor&, bool inline_mode,
+        const char *issuer, const std::string& log);
     virtual bool is_keep_alive_probe(State&, const TcpSegmentDescriptor&);
 
     static void reset_stats();
index fafe0a6ef6c658af44587b0e9e7c4ccbce25b47f..2271ffd0c0197d0f4a8e7e51273e917278568fc0 100644 (file)
@@ -27,7 +27,7 @@
 
 #include "tcp_module.h"
 #include "tcp_segment_descriptor.h"
-#include "tcp_stream_session.h"
+#include "tcp_session.h"
 #include "tcp_stream_tracker.h"
 
 using namespace snort;
@@ -223,7 +223,7 @@ public:
 
 static inline int handle_repeated_syn_mswin(
     TcpStreamTracker* talker, TcpStreamTracker* listener,
-    const TcpSegmentDescriptor& tsd, TcpStreamSession* session)
+    const TcpSegmentDescriptor& tsd, TcpSession* session)
 {
     /* Windows has some strange behavior here.  If the sequence of the reset is the
      * next expected sequence, it Resets.  Otherwise it ignores the 2nd SYN.
@@ -239,7 +239,7 @@ static inline int handle_repeated_syn_mswin(
 }
 
 static inline int handle_repeated_syn_bsd(
-    TcpStreamTracker* talker, const TcpSegmentDescriptor& tsd, TcpStreamSession* session)
+    TcpStreamTracker* talker, const TcpSegmentDescriptor& tsd, TcpSession* session)
 {
     /* If its not a retransmission of the actual SYN... RESET */
     if ( !SEQ_EQ(tsd.get_seq(), talker->get_iss()) )
@@ -466,23 +466,14 @@ TcpNormalizer::NormStatus TcpNormalizerProxy::apply_normalizations(
     return NORM_OK;
 }
 
-bool TcpNormalizerProxy::validate_rst(
-    TcpNormalizerState&, TcpSegmentDescriptor&)
-{
-    return true;
-}
+bool TcpNormalizerProxy::validate_rst(TcpNormalizerState&, TcpSegmentDescriptor&)
+{ return true; }
 
-int TcpNormalizerProxy::handle_paws(
-    TcpNormalizerState&, TcpSegmentDescriptor&)
-{
-    return ACTION_NOTHING;
-}
+int TcpNormalizerProxy::handle_paws(TcpNormalizerState&, TcpSegmentDescriptor&)
+{ return ACTION_NOTHING; }
 
-int TcpNormalizerProxy::handle_repeated_syn(
-    TcpNormalizerState&, TcpSegmentDescriptor&)
-{
-    return ACTION_NOTHING;
-}
+int TcpNormalizerProxy::handle_repeated_syn(TcpNormalizerState&, TcpSegmentDescriptor&)
+{ return ACTION_NOTHING; }
 
 TcpNormalizer::NormStatus TcpNormalizerMissed3whs::apply_normalizations(
     TcpNormalizerState&, TcpSegmentDescriptor&, uint32_t, bool)
@@ -503,21 +494,16 @@ bool TcpNormalizerMissed3whs::validate_rst(
     return true;
 }
 
-int TcpNormalizerMissed3whs::handle_paws(
-    TcpNormalizerState& tns, TcpSegmentDescriptor& tsd) 
-{
-    UNUSED(tsd); 
-    UNUSED(tns);
-    return ACTION_NOTHING;
-}
+int TcpNormalizerMissed3whs::handle_paws(TcpNormalizerState&, TcpSegmentDescriptor&)
+{ return ACTION_NOTHING; }
 
 int TcpNormalizerMissed3whs::handle_repeated_syn(
-    TcpNormalizerState& tns, TcpSegmentDescriptor& tsd)
+               TcpNormalizerState& tns, TcpSegmentDescriptor& tsd)
 {
     return tns.prev_norm->handle_repeated_syn(tns, tsd);
 }
 
-void TcpNormalizerPolicy::init(StreamPolicy os, TcpStreamSession* ssn, TcpStreamTracker* trk, TcpStreamTracker* peer)
+void TcpNormalizerPolicy::init(StreamPolicy os, TcpSession* ssn, TcpStreamTracker* trk, TcpStreamTracker* peer)
 {
     if ( os == StreamPolicy::MISSED_3WHS and os == tns.os_policy)
         tns.prev_norm = TcpNormalizerFactory::get_instance(StreamPolicy::OS_DEFAULT);
index 46f1443708f7916d0c074df4a21cba144a83a2d4..a52315da7ca48212e938c2bea4bc6f08b8533446 100644 (file)
@@ -24,8 +24,8 @@
 
 #include "stream/tcp/tcp_normalizer.h"
 
-class TcpStreamSession;
-class TcpStreamSession;
+class TcpSession;
+class TcpSession;
 
 class TcpNormalizerFactory
 {
@@ -46,7 +46,7 @@ public:
     TcpNormalizerPolicy() = default;
     ~TcpNormalizerPolicy() = default;
 
-    void init(StreamPolicy os, TcpStreamSession* ssn, TcpStreamTracker* trk, TcpStreamTracker* peer);
+    void init(StreamPolicy os, TcpSession* ssn, TcpStreamTracker* trk, TcpStreamTracker* peer);
     void reset()
     { init(StreamPolicy::OS_DEFAULT, nullptr, nullptr, nullptr); }
 
@@ -101,7 +101,8 @@ public:
     void set_zwp_seq(uint32_t seq)
     { return norm->set_zwp_seq(tns, seq); }
 
-    void log_drop_reason(const TcpSegmentDescriptor& tsd, bool inline_mode, const char *issuer, const std::string& log)
+    void log_drop_reason(const TcpSegmentDescriptor& tsd, bool inline_mode,
+        const char *issuer, const std::string& log)
     { return norm->log_drop_reason(tns, tsd, inline_mode, issuer, log); }
 
     bool is_keep_alive_probe(const TcpSegmentDescriptor& tsd)
diff --git a/src/stream/tcp/tcp_overlap_resolver.cc b/src/stream/tcp/tcp_overlap_resolver.cc
new file mode 100644 (file)
index 0000000..ebb3886
--- /dev/null
@@ -0,0 +1,728 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-2024 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// tcp_overlap_resolver.cc author davis mcpherson <davmcphe@cisco.com>
+// Created on: Oct 11, 2015
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "tcp_overlap_resolver.h"
+
+#include "detection/detection_engine.h"
+
+#include "tcp_module.h"
+#include "tcp_normalizers.h"
+#include "tcp_segment_node.h"
+#include "tcp_session.h"
+
+using namespace snort;
+
+TcpOverlapState::TcpOverlapState(TcpReassemblySegments& seglist)
+    : seglist(seglist)
+{
+    tcp_ips_data = Normalize_GetMode(NORM_TCP_IPS);
+}
+
+void TcpOverlapState::init(TcpSegmentDescriptor& tsd)
+{
+    int32_t dist_head = 0, dist_tail = 0;
+
+    if ( seglist.head && seglist.tail )
+    {
+        if ( SEQ_GT(tsd.get_seq(), seglist.head->start_seq()) )
+            dist_head = tsd.get_seq() - seglist.head->start_seq();
+        else
+            dist_head = seglist.head->start_seq() - tsd.get_seq();
+
+        if ( SEQ_GT(tsd.get_seq(), seglist.tail->start_seq()) )
+            dist_tail = tsd.get_seq() - seglist.tail->start_seq();
+        else
+            dist_tail = seglist.tail->start_seq() - tsd.get_seq();
+    }
+
+    left = right = nullptr;
+    if ( dist_head < dist_tail )
+    {
+        right = seglist.head;
+        while ( right and SEQ_LT(right->start_seq(), tsd.get_seq()) )
+        {
+            left = right;
+            right = right->next;
+        }
+    }
+    else
+    {
+        left = seglist.tail;
+        while ( left and SEQ_GEQ(left->start_seq(), tsd.get_seq()) )
+        {
+            right = left;
+            left = left->prev;
+        }
+    }
+
+    this->tsd = &tsd;
+    seq = tsd.get_seq();
+    seq_end = tsd.get_end_seq();
+    len = tsd.get_len();
+
+    overlap = 0;
+    slide = 0;
+    trunc_len = 0;
+
+    rdata = tsd.get_pkt()->data;
+    rsize = tsd.get_len();
+    rseq = tsd.get_seq();
+
+    keep_segment = true;
+}
+
+bool TcpOverlapResolver::is_segment_retransmit(TcpOverlapState& tos, bool* full_retransmit)
+{
+    // Don't want to count retransmits as overlaps or do anything
+    // else with them.  Account for retransmits of multiple PDUs
+    // in one segment.
+    bool* pb = (tos.rseq == tos.tsd->get_seq()) ? full_retransmit : nullptr;
+
+    if ( tos.right->is_retransmit(tos.rdata, tos.rsize,
+        tos.rseq, tos.right->length, pb) )
+    {
+        tos.tsd->set_retransmit_flag();
+
+        if ( !(*full_retransmit) )
+        {
+            tos.rdata += tos.right->length;
+            tos.rsize -= tos.right->length;
+            tos.rseq += tos.right->length;
+            tos.slide += tos.right->length;
+            tos.left = tos.right;
+            tos.right = tos.right->next;
+        }
+        else
+            tos.rsize = 0;
+
+        if ( tos.rsize == 0 )
+        {
+            // All data was retransmitted
+            snort::DetectionEngine::disable_content(tos.tsd->get_pkt());
+            tos.keep_segment = false;
+        }
+
+        return true;
+    }
+
+    return false;
+}
+
+void TcpOverlapResolver::eval_left(TcpOverlapState& tos)
+{
+    if ( tos.left )
+        insert_left_overlap(tos);
+}
+
+void TcpOverlapResolver::eval_right(TcpOverlapState& tos)
+{
+    while ( tos.right && SEQ_LT(tos.right->start_seq(), tos.seq_end) )
+    {
+        tos.trunc_len = 0;
+
+        assert(SEQ_LEQ(tos.slide_seq(), tos.right->start_seq()));
+        tos.overlap = ( int )( tos.seq_end - tos.right->start_seq() );
+
+        // Treat sequence number overlap as a retransmission,
+        // only check right side since left side happens rarely
+        tos.seglist.session->flow->call_handlers(tos.tsd->get_pkt(), false);
+        if ( tos.overlap < tos.right->length )
+        {
+            if ( tos.right->is_retransmit(tos.rdata, tos.rsize,
+                tos.rseq, tos.right->length, nullptr) )
+            {
+                // All data was retransmitted
+                tos.tsd->set_retransmit_flag();
+                snort::DetectionEngine::disable_content(tos.tsd->get_pkt());
+                tos.keep_segment = false;
+                tcpStats.full_retransmits++;
+            }
+            else
+            {
+                tcpStats.overlaps++;
+                tos.seglist.overlap_count++;
+                insert_right_overlap(tos);
+            }
+
+            break;
+        }
+        else  // Full overlap
+        {
+            bool full_retransmit = false;
+            // Don't want to count retransmits as overlaps or do anything
+            // else with them.  Account for retransmits of multiple PDUs
+            // in one segment.
+            if ( is_segment_retransmit(tos, &full_retransmit) )
+            {
+                if ( full_retransmit )
+                {
+                    tcpStats.full_retransmits++;
+                    break;
+                }
+                continue;
+            }
+
+            tcpStats.overlaps++;
+            tos.seglist.overlap_count++;
+            insert_full_overlap(tos);
+
+            if ( !tos.keep_segment )
+                return;
+        }
+    }
+}
+
+void TcpOverlapResolver::drop_old_segment(TcpOverlapState& tos)
+{
+    TcpSegmentNode* drop_seg = tos.right;
+    tos.right = tos.right->next;
+    tos.seglist.delete_reassembly_segment(drop_seg);
+}
+
+void TcpOverlapResolver::left_overlap_keep_first(TcpOverlapState& tos)
+{
+    // NOTE that overlap will always be less than left->size since
+    // seq is always greater than left->seq
+    assert(SEQ_GT(tos.seq, tos.left->start_seq()));
+
+    tos.overlap = tos.left->next_seq() - tos.seq;
+    if ( tos.len < tos.overlap )
+        tos.overlap = tos.len;
+
+    if ( tos.overlap > 0 )
+    {
+        tcpStats.overlaps++;
+        tos.seglist.overlap_count++;
+
+        if ( SEQ_GT(tos.left->next_seq(), tos.seq_end) )
+        {
+            if (tos.tcp_ips_data == NORM_MODE_ON)
+            {
+               unsigned offset = tos.tsd->get_seq() - tos.left->start_seq();
+                tos.tsd->rewrite_payload(0, tos.left->payload() + offset);
+            }
+            norm_stats[PC_TCP_IPS_DATA][tos.tcp_ips_data]++;
+        }
+        else
+        {
+            if ( tos.tcp_ips_data == NORM_MODE_ON )
+            {
+                unsigned offset = tos.tsd->get_seq() - tos.left->start_seq();
+                unsigned length = tos.left->next_seq() - tos.tsd->get_seq();
+                tos.tsd->rewrite_payload(0, tos.left->payload() + offset, length);
+            }
+
+            norm_stats[PC_TCP_IPS_DATA][tos.tcp_ips_data]++;
+        }
+
+        tos.slide = tos.overlap;
+    }
+}
+
+void TcpOverlapResolver::left_overlap_trim_first(TcpOverlapState& tos)
+{
+    assert(SEQ_GT(tos.seq, tos.left->start_seq()));
+
+    tos.overlap =  tos.left->next_seq() - tos.seq;
+    if ( tos.overlap > 0 )
+    {
+        tcpStats.overlaps++;
+        tos.seglist.overlap_count++;
+
+        if ( SEQ_GEQ(tos.left->next_seq(), tos.seq + tos.len)  )
+        {
+            // existing packet overlaps new on both sides.  Drop the new data.
+            tos.slide += tos.len;
+        }
+        else
+        {
+            /* Otherwise, trim the old data accordingly */
+            tos.left->length -= ( int16_t )tos.overlap;
+            tos.seglist.seg_bytes_logical -= tos.overlap;
+        }
+    }
+}
+
+void TcpOverlapResolver::left_overlap_keep_last(TcpOverlapState& tos)
+{
+    assert(SEQ_GT(tos.seq, tos.left->seq));
+
+    tos.overlap = tos.left->next_seq() - tos.seq;
+    if ( tos.overlap > 0 )
+    {
+        tcpStats.overlaps++;
+        tos.seglist.overlap_count++;
+
+        /* True "Last" policy" */
+        if (SEQ_GT(tos.left->next_seq(), tos.seq + tos.len) )
+        {
+            /* New data is overlapped on both sides by existing data.  Existing data needs to be
+             * split and the new data inserted in the middle.
+             * Need to duplicate left. Adjust that seq by + (seq + len) and
+             * size by - (seq + len - left->start_seq()).
+             */
+            tos.seglist.dup_reassembly_segment(tos.left, &tos.right);
+
+            tos.left->length -= tos.overlap;
+            uint16_t delta = tos.seq_end - tos.left->start_seq();
+            tos.right->length -= delta;
+            tos.right->offset += delta;
+            tos.seglist.seg_bytes_logical -= tos.len;
+        }
+        else
+        {
+            tos.left->length -= (int16_t)tos.overlap;
+            tos.seglist.seg_bytes_logical -= tos.overlap;
+        }
+    }
+}
+
+void TcpOverlapResolver::right_overlap_truncate_existing(TcpOverlapState& tos)
+{
+    if ( SEQ_EQ(tos.right->start_seq(), tos.slide_seq()) )
+    {
+        tos.slide += tos.overlap;
+    }
+    else
+    {
+        /* partial overlap */
+        tos.right->offset += tos.overlap;
+        tos.right->length -= ( int16_t )tos.overlap;
+        tos.seglist.seg_bytes_logical -= tos.overlap;
+    }
+}
+
+void TcpOverlapResolver::right_overlap_truncate_new(TcpOverlapState& tos)
+{
+    if (tos.tcp_ips_data == NORM_MODE_ON)
+    {
+        unsigned offset = tos.right->start_seq() - tos.tsd->get_seq();
+        unsigned length = tos.tsd->get_seq() + tos.tsd->get_len() - tos.right->start_seq();
+        tos.tsd->rewrite_payload(offset, tos.right->payload(), length);
+    }
+
+    norm_stats[PC_TCP_IPS_DATA][tos.tcp_ips_data]++;
+    tos.trunc_len = tos.overlap;
+}
+
+// REASSEMBLY_POLICY_FIRST:
+// REASSEMBLY_POLICY_VISTA:
+void TcpOverlapResolver::full_right_overlap_truncate_new(TcpOverlapState& tos)
+{
+
+    if ( tos.tcp_ips_data == NORM_MODE_ON )
+    {
+        unsigned offset = tos.right->start_seq() - tos.tsd->get_seq();
+        if ( !offset && zwp_data_mismatch(tos, *tos.tsd, tos.right->length))
+        {
+            tos.seglist.tracker->normalizer.session_blocker(*tos.tsd);
+            tos.keep_segment = false;
+            return;
+        }
+
+        tos.tsd->rewrite_payload(offset, tos.right->payload(), tos.right->length);
+    }
+
+    norm_stats[PC_TCP_IPS_DATA][tos.tcp_ips_data]++;
+
+    if ( SEQ_EQ(tos.right->start_seq(), tos.slide_seq()) )
+    {
+        // Overlap is greater than or equal to right->size slide gets set before insertion
+        tos.slide += tos.right->length;
+        tos.left = tos.right;
+        tos.right = tos.right->next;
+    }
+    else
+    {
+        // seq is less than right->start_seq(),  set trunc length and slide
+        //  and insert chunk before current right segment...
+        tos.trunc_len = tos.overlap;
+        tos.seglist.add_reassembly_segment(*tos.tsd, tos.len, tos.slide,
+            tos.trunc_len, tos.seq, tos.left);
+
+        // adjust slide and trunc_len and move to next node to the right...
+        tos.slide += tos.right->next_seq() - tos.slide_seq();
+        tos.trunc_len = 0;
+        tos.left = tos.right;
+        tos.right = tos.right->next;
+    }
+}
+
+// REASSEMBLY_POLICY_WINDOWS:
+// REASSEMBLY_POLICY_WINDOWS2K3:
+// REASSEMBLY_POLICY_BSD:
+// REASSEMBLY_POLICY_MACOS:
+void TcpOverlapResolver::full_right_overlap_os1(TcpOverlapState& tos)
+{
+    if ( SEQ_GEQ(tos.seq_end, tos.right->next_seq()) and
+        SEQ_LT(tos.slide_seq(), tos.right->start_seq()) )
+    {
+        drop_old_segment(tos);
+    }
+    else
+        full_right_overlap_truncate_new(tos);
+}
+
+// REASSEMBLY_POLICY_LINUX:
+// REASSEMBLY_POLICY_HPUX10:
+// REASSEMBLY_POLICY_IRIX:
+void TcpOverlapResolver::full_right_overlap_os2(TcpOverlapState& tos)
+{
+    if ( SEQ_GEQ(tos.seq_end, tos.right->next_seq()) and
+        SEQ_LT(tos.slide_seq(), tos.right->start_seq()) )
+    {
+        drop_old_segment(tos);
+    }
+    else if ( SEQ_GT(tos.seq_end, tos.right->next_seq()) and
+        SEQ_EQ(tos.slide_seq(), tos.right->start_seq()) )
+    {
+        drop_old_segment(tos);
+    }
+    else
+        full_right_overlap_truncate_new(tos);
+}
+
+// REASSEMBLY_POLICY_HPUX11:
+// REASSEMBLY_POLICY_SOLARIS:
+void TcpOverlapResolver::full_right_overlap_os3(TcpOverlapState& tos)
+{
+    // If this packet is wholly overlapping and the same size as a previous one and we have not
+    // received the one immediately preceding, we take the FIRST.
+    if ( SEQ_EQ(tos.right->start_seq(), tos.seq) && (tos.right->length == tos.len)
+        && (tos.left && !SEQ_EQ(tos.left->next_seq(), tos.seq)) )
+    {
+        right_overlap_truncate_new(tos);
+
+        tos.rdata += tos.right->length;
+        tos.rsize -= tos.right->length;
+        tos.rseq += tos.right->length;
+        tos.left = tos.right;
+        tos.right = tos.right->next;
+    }
+    else
+        drop_old_segment(tos);
+}
+
+//  REASSEMBLY_POLICY_OLD_LINUX:
+//  REASSEMBLY_POLICY_LAST:
+void TcpOverlapResolver::full_right_overlap_os4(TcpOverlapState& tos)
+{ drop_old_segment(tos); }
+
+void TcpOverlapResolver::full_right_overlap_os5(TcpOverlapState& tos)
+{
+    full_right_overlap_truncate_new(tos);
+}
+
+bool TcpOverlapResolver::zwp_data_mismatch(TcpOverlapState& tos, TcpSegmentDescriptor& tsd, uint32_t overlap)
+{
+    if ( overlap == MAX_ZERO_WIN_PROBE_LEN
+        and tos.right->start_seq() == tos.seglist.tracker->normalizer.get_zwp_seq()
+        and (tos.right->data[0] != tsd.get_pkt()->data[0]) )
+    {
+        return tsd.is_nap_policy_inline();
+    }
+
+    return false;
+}
+
+class TcpOverlapResolverFirst : public TcpOverlapResolver
+{
+public:
+    TcpOverlapResolverFirst()
+    { overlap_policy = StreamPolicy::OS_FIRST; }
+
+private:
+    void insert_left_overlap(TcpOverlapState& tos) override
+    { left_overlap_keep_first(tos); }
+
+    void insert_right_overlap(TcpOverlapState& tos) override
+    { right_overlap_truncate_new(tos); }
+
+    void insert_full_overlap(TcpOverlapState& tos) override
+    { full_right_overlap_os5(tos); }
+};
+
+class TcpOverlapResolverLast : public TcpOverlapResolver
+{
+public:
+    TcpOverlapResolverLast()
+    { overlap_policy = StreamPolicy::OS_LAST; }
+
+private:
+    void right_overlap_truncate_existing(TcpOverlapState& tos) override
+     {
+         tos.right->offset += tos.overlap;
+         tos.right->length -= ( int16_t )tos.overlap;
+         tos.seglist.seg_bytes_logical -= tos.overlap;
+     }
+
+    void insert_left_overlap(TcpOverlapState& tos) override
+    { left_overlap_keep_last(tos); }
+
+    void insert_right_overlap(TcpOverlapState& tos) override
+    { right_overlap_truncate_existing(tos); }
+
+    void insert_full_overlap(TcpOverlapState& tos) override
+    { full_right_overlap_os4(tos); }
+};
+
+class TcpOverlapResolverLinux : public TcpOverlapResolver
+{
+public:
+    TcpOverlapResolverLinux()
+    { overlap_policy = StreamPolicy::OS_LINUX; }
+
+private:
+    void insert_left_overlap(TcpOverlapState& tos) override
+    { left_overlap_keep_first(tos); }
+
+    void insert_right_overlap(TcpOverlapState& tos) override
+    { right_overlap_truncate_existing(tos); }
+
+    void insert_full_overlap(TcpOverlapState& tos) override
+    { full_right_overlap_os2(tos); }
+};
+
+class TcpOverlapResolverOldLinux : public TcpOverlapResolver
+{
+public:
+    TcpOverlapResolverOldLinux()
+    { overlap_policy = StreamPolicy::OS_OLD_LINUX; }
+
+private:
+    void insert_left_overlap(TcpOverlapState& tos) override
+    { left_overlap_keep_first(tos); }
+
+    void insert_right_overlap(TcpOverlapState& tos) override
+    { right_overlap_truncate_existing(tos); }
+
+    void insert_full_overlap(TcpOverlapState& tos) override
+    { full_right_overlap_os4(tos); }
+};
+
+class TcpOverlapResolverBSD : public TcpOverlapResolver
+{
+public:
+    TcpOverlapResolverBSD()
+    { overlap_policy = StreamPolicy::OS_BSD; }
+
+private:
+    void insert_left_overlap(TcpOverlapState& tos) override
+    { left_overlap_keep_first(tos); }
+
+    void insert_right_overlap(TcpOverlapState& tos) override
+    { right_overlap_truncate_existing(tos); }
+
+    void insert_full_overlap(TcpOverlapState& tos) override
+    { full_right_overlap_os1(tos); }
+};
+
+class TcpOverlapResolverMacOS : public TcpOverlapResolver
+{
+public:
+    TcpOverlapResolverMacOS()
+    { overlap_policy = StreamPolicy::OS_MACOS; }
+
+private:
+    void insert_left_overlap(TcpOverlapState& tos) override
+    { left_overlap_keep_first(tos); }
+
+    void insert_right_overlap(TcpOverlapState& tos) override
+    { right_overlap_truncate_existing(tos); }
+
+    void insert_full_overlap(TcpOverlapState& tos) override
+    { full_right_overlap_os1(tos); }
+};
+
+class TcpOverlapResolverSolaris : public TcpOverlapResolver
+{
+public:
+    TcpOverlapResolverSolaris()
+    { overlap_policy = StreamPolicy::OS_SOLARIS; }
+
+private:
+    void insert_left_overlap(TcpOverlapState& tos) override
+    { left_overlap_trim_first(tos); }
+
+    void insert_right_overlap(TcpOverlapState& tos) override
+    { right_overlap_truncate_new(tos); }
+
+    void insert_full_overlap(TcpOverlapState& tos) override
+    { full_right_overlap_os3(tos); }
+};
+
+class TcpOverlapResolverIrix : public TcpOverlapResolver
+{
+public:
+    TcpOverlapResolverIrix()
+    { overlap_policy = StreamPolicy::OS_IRIX; }
+
+private:
+    void insert_left_overlap(TcpOverlapState& tos) override
+    { left_overlap_keep_first(tos);  }
+
+    void insert_right_overlap(TcpOverlapState& tos) override
+    { right_overlap_truncate_existing(tos); }
+
+    void insert_full_overlap(TcpOverlapState& tos) override
+    { full_right_overlap_os2(tos); }
+};
+
+class TcpOverlapResolverHpux11 : public TcpOverlapResolver
+{
+public:
+    TcpOverlapResolverHpux11()
+    { overlap_policy = StreamPolicy::OS_HPUX11; }
+
+private:
+    void insert_left_overlap(TcpOverlapState& tos) override
+    { left_overlap_trim_first(tos); }
+
+    void insert_right_overlap(TcpOverlapState& tos) override
+    { right_overlap_truncate_new(tos); }
+
+    void insert_full_overlap(TcpOverlapState& tos) override
+    { full_right_overlap_os3(tos); }
+};
+
+class TcpOverlapResolverHpux10 : public TcpOverlapResolver
+{
+public:
+    TcpOverlapResolverHpux10()
+    { overlap_policy = StreamPolicy::OS_HPUX10; }
+
+private:
+    void insert_left_overlap(TcpOverlapState& tos) override
+    { left_overlap_keep_first(tos); }
+
+    void insert_right_overlap(TcpOverlapState& tos) override
+    { right_overlap_truncate_existing(tos); }
+
+    void insert_full_overlap(TcpOverlapState& tos) override
+    { full_right_overlap_os2(tos); }
+};
+
+class TcpOverlapResolverWindows : public TcpOverlapResolver
+{
+public:
+    TcpOverlapResolverWindows()
+    { overlap_policy = StreamPolicy::OS_WINDOWS; }
+
+private:
+    void insert_left_overlap(TcpOverlapState& tos) override
+    { left_overlap_keep_first(tos); }
+
+    void insert_right_overlap(TcpOverlapState& tos) override
+    { right_overlap_truncate_existing(tos); }
+
+    void insert_full_overlap(TcpOverlapState& tos) override
+    { full_right_overlap_os1(tos); }
+};
+
+class TcpOverlapResolverWindows2K3 : public TcpOverlapResolver
+{
+public:
+    TcpOverlapResolverWindows2K3()
+    { overlap_policy = StreamPolicy::OS_WINDOWS2K3; }
+
+private:
+    void insert_left_overlap(TcpOverlapState& tos) override
+    { left_overlap_keep_first(tos); }
+
+    void insert_right_overlap(TcpOverlapState& tos) override
+    { right_overlap_truncate_existing(tos); }
+
+    void insert_full_overlap(TcpOverlapState& tos) override
+    { full_right_overlap_os1(tos); }
+};
+
+class TcpOverlapResolverVista : public TcpOverlapResolver
+{
+public:
+    TcpOverlapResolverVista()
+    { overlap_policy = StreamPolicy::OS_VISTA; }
+
+private:
+    void insert_left_overlap(TcpOverlapState& tos) override
+    { left_overlap_keep_first(tos); }
+
+    void insert_right_overlap(TcpOverlapState& tos) override
+    { right_overlap_truncate_new(tos); }
+
+    void insert_full_overlap(TcpOverlapState& tos) override
+    { full_right_overlap_os5 (tos); }
+};
+
+class TcpOverlapResolverProxy : public TcpOverlapResolverFirst
+{
+public:
+    TcpOverlapResolverProxy()
+    { overlap_policy = StreamPolicy::OS_PROXY; }
+
+private:
+    void insert_left_overlap(TcpOverlapState& tos) override
+    { left_overlap_keep_first(tos); }
+
+    void insert_right_overlap(TcpOverlapState& tos) override
+    { right_overlap_truncate_new(tos); }
+
+    void insert_full_overlap(TcpOverlapState& tos) override
+    { full_right_overlap_os5(tos); }
+};
+
+TcpOverlapResolver* TcpOverlapResolverFactory::overlap_resolvers[StreamPolicy::OS_END_OF_LIST];
+
+void TcpOverlapResolverFactory::initialize()
+{
+    overlap_resolvers[StreamPolicy::OS_FIRST] = new TcpOverlapResolverFirst;
+    overlap_resolvers[StreamPolicy::OS_LAST] = new TcpOverlapResolverLast;
+    overlap_resolvers[StreamPolicy::OS_LINUX] = new TcpOverlapResolverLinux;
+    overlap_resolvers[StreamPolicy::OS_OLD_LINUX] = new TcpOverlapResolverOldLinux;
+    overlap_resolvers[StreamPolicy::OS_BSD] = new TcpOverlapResolverBSD;
+    overlap_resolvers[StreamPolicy::OS_MACOS] = new TcpOverlapResolverMacOS;
+    overlap_resolvers[StreamPolicy::OS_SOLARIS] = new TcpOverlapResolverSolaris;
+    overlap_resolvers[StreamPolicy::OS_IRIX] = new TcpOverlapResolverIrix;
+    overlap_resolvers[StreamPolicy::OS_HPUX11] = new TcpOverlapResolverHpux11;
+    overlap_resolvers[StreamPolicy::OS_HPUX10] = new TcpOverlapResolverHpux10;
+    overlap_resolvers[StreamPolicy::OS_WINDOWS] = new TcpOverlapResolverWindows;
+    overlap_resolvers[StreamPolicy::OS_WINDOWS2K3] = new TcpOverlapResolverWindows2K3;
+    overlap_resolvers[StreamPolicy::OS_VISTA] = new TcpOverlapResolverVista;
+    overlap_resolvers[StreamPolicy::OS_PROXY] = new TcpOverlapResolverProxy;
+}
+
+void TcpOverlapResolverFactory::term()
+{
+    for ( auto sp = StreamPolicy::OS_FIRST; sp <= StreamPolicy::OS_PROXY; sp++ )
+        delete overlap_resolvers[sp];
+}
+
+TcpOverlapResolver* TcpOverlapResolverFactory::get_instance(StreamPolicy os_policy)
+{
+    NormMode tcp_ips_data = Normalize_GetMode(NORM_TCP_IPS);
+    StreamPolicy sp = (tcp_ips_data == NORM_MODE_ON) ? StreamPolicy::OS_FIRST : os_policy;
+
+    assert( sp <= StreamPolicy::OS_PROXY );
+    return overlap_resolvers[sp];
+}
diff --git a/src/stream/tcp/tcp_overlap_resolver.h b/src/stream/tcp/tcp_overlap_resolver.h
new file mode 100644 (file)
index 0000000..5668f0f
--- /dev/null
@@ -0,0 +1,120 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-2024 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// tcp_overlap_resolver.h author davis mcpherson <davmcphe@cisco.com>
+// Created on: Oct 11, 2015
+
+#ifndef TCP_OVERLAP_RESOLVER_H
+#define TCP_OVERLAP_RESOLVER_H
+
+#include <vector>
+
+#include "normalize/norm_stats.h"
+#include "stream/stream.h"
+
+#include "tcp_defs.h"
+
+class TcpReassemblySegments;
+class TcpSegmentDescriptor;
+class TcpSegmentNode;
+class TcpSession;
+class TcpStreamTracker;
+
+class TcpOverlapState
+{
+public:
+    TcpOverlapState(TcpReassemblySegments& seglist);
+    ~TcpOverlapState() = default;
+
+    void init(TcpSegmentDescriptor&);
+
+    uint32_t slide_seq() const
+    { return seq + slide; }
+
+    TcpReassemblySegments& seglist;
+    TcpSegmentDescriptor* tsd = nullptr;
+
+    TcpSegmentNode* left = nullptr;
+    TcpSegmentNode* right = nullptr;
+    const uint8_t* rdata = nullptr;
+
+    uint32_t seq = 0;
+    uint32_t seq_end = 0;
+    uint32_t rseq = 0;
+
+    int32_t overlap = 0;
+    int32_t slide = 0;
+    int32_t trunc_len = 0;
+
+    uint16_t len = 0;
+    uint16_t rsize = 0;
+    int8_t tcp_ips_data = 0;
+
+    bool keep_segment = true;
+};
+
+class TcpOverlapResolver
+{
+public:
+    TcpOverlapResolver() = default;
+    virtual ~TcpOverlapResolver() = default;
+
+    void eval_left(TcpOverlapState&);
+    void eval_right(TcpOverlapState&);
+
+    StreamPolicy get_overlap_policy()
+    { return overlap_policy; }
+
+protected:
+    virtual bool is_segment_retransmit(TcpOverlapState&, bool*);
+    virtual void drop_old_segment(TcpOverlapState&);
+    virtual bool zwp_data_mismatch(TcpOverlapState&, TcpSegmentDescriptor&, uint32_t);
+
+    virtual void left_overlap_keep_first(TcpOverlapState&);
+    virtual void left_overlap_trim_first(TcpOverlapState&);
+    virtual void left_overlap_keep_last(TcpOverlapState&);
+    virtual void right_overlap_truncate_existing(TcpOverlapState&);
+    virtual void right_overlap_truncate_new(TcpOverlapState&);
+    virtual void full_right_overlap_truncate_new(TcpOverlapState&);
+    virtual void full_right_overlap_os1(TcpOverlapState&);
+    virtual void full_right_overlap_os2(TcpOverlapState&);
+    virtual void full_right_overlap_os3(TcpOverlapState&);
+    virtual void full_right_overlap_os4(TcpOverlapState&);
+    virtual void full_right_overlap_os5(TcpOverlapState&);
+
+    virtual void insert_left_overlap(TcpOverlapState&) = 0;
+    virtual void insert_right_overlap(TcpOverlapState&) = 0;
+    virtual void insert_full_overlap(TcpOverlapState&) = 0;
+
+    StreamPolicy overlap_policy = StreamPolicy::OS_DEFAULT;
+};
+
+class TcpOverlapResolverFactory
+{
+public:
+    static void initialize();
+    static void term();
+    static TcpOverlapResolver* get_instance(StreamPolicy);
+
+private:
+    TcpOverlapResolverFactory() = delete;
+
+    static TcpOverlapResolver* overlap_resolvers[StreamPolicy::OS_END_OF_LIST];
+};
+
+#endif
index 9b4ab398ea7b0bc77f861004843ad96593317bfa..03d117dc2b21ce89edd9129e0dbb6ad37ea49926 100644 (file)
 #include "packet_io/packet_tracer.h"
 #include "profiler/profiler.h"
 #include "protocols/packet_manager.h"
+#include "stream/stream_splitter.h"
 #include "time/packet_time.h"
 
 #include "tcp_module.h"
 #include "tcp_normalizers.h"
+#include "tcp_segment_node.h"
 #include "tcp_session.h"
-#include "tcp_stream_tracker.h"
 
 using namespace snort;
 
-static THREAD_LOCAL Packet* last_pdu = nullptr;
-
-static void purge_alerts_callback_ackd(IpsContext* c)
+void TcpReassembler::init(bool server, StreamSplitter* ss)
 {
-    TcpSession* session = (TcpSession*)c->packet->flow->session;
-
-    if ( c->packet->is_from_server() )
-        session->client.reassembler.purge_alerts();
+    splitter = ss;
+    paf.paf_setup(ss);
+    if ( seglist.cur_rseg )
+        seglist.cur_sseg = seglist.cur_rseg;
     else
-        session->server.reassembler.purge_alerts();
-}
+        seglist.cur_sseg = seglist.head;
 
-static void purge_alerts_callback_ips(IpsContext* c)
-{
-    TcpSession* session = (TcpSession*)c->packet->flow->session;
+    server_side = server;
 
-    if ( c->packet->is_from_server() )
-        session->server.reassembler.purge_alerts();
+    if ( server_side )
+    {
+        ignore_dir = SSN_DIR_FROM_CLIENT;
+        packet_dir = PKT_FROM_CLIENT;
+    }
     else
-        session->client.reassembler.purge_alerts();
-}
-
-bool TcpReassembler::is_segment_pending_flush(const TcpReassemblerState& trs) const
-{
-    return ( get_pending_segment_count(trs, 1) > 0 );
-}
-
-uint32_t TcpReassembler::get_pending_segment_count(const TcpReassemblerState& trs, unsigned max) const
-{
-    uint32_t n = trs.sos.seg_count - trs.flush_count;
-
-    if ( !n || max == 1 )
-        return n;
-
-    n = 0;
-    const TcpSegmentNode* tsn = trs.sos.seglist.head;
-    while ( tsn )
     {
-        if ( tsn->c_len && SEQ_LT(tsn->c_seq, trs.tracker->r_win_base) )
-            n++;
-
-        if ( max && n == max )
-            return n;
-
-        tsn = tsn->next;
+        ignore_dir = SSN_DIR_FROM_SERVER;
+        packet_dir = PKT_FROM_SERVER;
     }
-
-    return n;
-}
-
-bool TcpReassembler::next_no_gap(const TcpSegmentNode& tsn)
-{
-    return tsn.next and (tsn.next->i_seq == tsn.i_seq + tsn.i_len);
-}
-
-bool TcpReassembler::next_no_gap_c(const TcpSegmentNode& tsn)
-{
-    return tsn.next and (tsn.next->c_seq == tsn.c_seq + tsn.c_len);
-}
-
-bool TcpReassembler::next_acked_no_gap_c(const TcpSegmentNode& tsn, const TcpReassemblerState& trs)
-{
-    return tsn.next and (tsn.next->c_seq == tsn.c_seq + tsn.c_len)
-        and SEQ_LT(tsn.next->c_seq, trs.tracker->r_win_base);
 }
 
-bool TcpReassembler::fin_no_gap(const TcpSegmentNode& tsn, const TcpReassemblerState& trs)
+bool TcpReassembler::fin_no_gap(const TcpSegmentNode& tsn)
 {
-    return trs.tracker->fin_seq_status >= TcpStreamTracker::FIN_WITH_SEQ_SEEN
-        and SEQ_GEQ(tsn.i_seq + tsn.i_len, trs.tracker->get_fin_i_seq());
+    return tracker.fin_seq_status >= FIN_WITH_SEQ_SEEN
+        and SEQ_GEQ(tsn.next_seq(), tracker.get_fin_i_seq());
 }
 
-bool TcpReassembler::fin_acked_no_gap(const TcpSegmentNode& tsn, const TcpReassemblerState& trs)
+bool TcpReassembler::fin_acked_no_gap(const TcpSegmentNode& tsn)
 {
-    return trs.tracker->fin_seq_status >= TcpStreamTracker::FIN_WITH_SEQ_ACKED
-        and SEQ_GEQ(tsn.i_seq + tsn.i_len, trs.tracker->get_fin_i_seq());
-}
-
-void TcpReassembler::update_next(TcpReassemblerState& trs, const TcpSegmentNode& tsn)
-{
-    trs.sos.seglist.cur_rseg = next_no_gap(tsn) ?  tsn.next : nullptr;
-    if ( trs.sos.seglist.cur_rseg )
-        trs.sos.seglist.cur_rseg->c_seq = trs.sos.seglist.cur_rseg->i_seq;
+    return tracker.fin_seq_status >= FIN_WITH_SEQ_ACKED
+        and SEQ_GEQ(tsn.next_seq(), tracker.get_fin_i_seq());
 }
 
 // If we are skipping seglist hole, update tsn so that we can purge
-void TcpReassembler::update_skipped_bytes(uint32_t remaining_bytes, TcpReassemblerState& trs)
+void TcpReassembler::update_skipped_bytes(uint32_t remaining_bytes)
 {
     TcpSegmentNode* tsn;
 
-    while ( remaining_bytes and (tsn = trs.sos.seglist.cur_rseg) )
+    while ( remaining_bytes and (tsn = seglist.cur_rseg) )
     {
-        auto bytes_skipped = ( tsn->c_len <= remaining_bytes ) ? tsn->c_len : remaining_bytes;
+        auto bytes_skipped = ( tsn->unscanned() <= remaining_bytes ) ? tsn->unscanned() : remaining_bytes;
 
         remaining_bytes -= bytes_skipped;
-        tsn->update_reassembly_cursor(bytes_skipped);
+        tsn->advance_cursor(bytes_skipped);
 
-        if ( !tsn->c_len )
+        if ( !tsn->unscanned() )
         {
-            trs.flush_count++;
-            update_next(trs, *tsn);
+            seglist.flush_count++;
+            seglist.update_next(tsn);
         }
     }
 }
 
-int TcpReassembler::delete_reassembly_segment(TcpReassemblerState& trs, TcpSegmentNode* tsn)
-{
-    int ret;
-    assert(tsn);
-
-    trs.sos.seglist.remove(tsn);
-    trs.sos.seg_bytes_total -= tsn->i_len;
-    trs.sos.seg_bytes_logical -= tsn->i_len;
-    ret = tsn->i_len;
-
-    if ( !tsn->c_len )
-    {
-        tcpStats.segs_used++;
-        trs.flush_count--;
-    }
-
-    if ( trs.sos.seglist.cur_sseg == tsn )
-        trs.sos.seglist.cur_sseg = tsn->next;
-
-    if ( trs.sos.seglist.cur_rseg == tsn )
-        update_next(trs, *tsn);
-
-    tsn->term();
-    trs.sos.seg_count--;
-
-    return ret;
-}
-
-void TcpReassembler::queue_reassembly_segment(
-    TcpReassemblerState& trs, TcpSegmentNode* prev, TcpSegmentNode* tsn)
-{
-    trs.sos.seglist.insert(prev, tsn);
-
-    if ( !trs.sos.seglist.cur_sseg )
-        trs.sos.seglist.cur_sseg = tsn;
-    else if ( SEQ_LT(tsn->c_seq, trs.sos.seglist.cur_sseg->c_seq) )
-    {
-        trs.sos.seglist.cur_sseg = tsn;
-        if ( SEQ_LT(tsn->c_seq, trs.sos.seglist_base_seq) )
-            trs.sos.seglist_base_seq = tsn->c_seq;
-
-        if ( trs.sos.seglist.cur_rseg && SEQ_LT(tsn->c_seq, trs.sos.seglist.cur_rseg->c_seq) )
-            trs.sos.seglist.cur_rseg = tsn;
-    }
-
-    trs.sos.seg_count++;
-    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(
-    TcpReassemblerState&, TcpSegmentNode* tail, const TcpSegmentDescriptor& tsd)
-{
-    if ( SEQ_EQ(tsd.get_seq(), tail->i_seq + tail->i_len) )
-        return true;
-
-    return false;
-}
-
-void TcpReassembler::add_reassembly_segment(
-    TcpReassemblerState& trs, TcpSegmentDescriptor& tsd, uint16_t len, uint32_t slide,
-    uint32_t trunc_len, uint32_t seq, TcpSegmentNode* left)
-{
-    const int32_t new_size = len - slide - trunc_len;
-    assert(new_size >= 0);
-
-    // if trimming will delete all data, don't insert this segment in the queue
-    if ( new_size <= 0 )
-    {
-        tcpStats.payload_fully_trimmed++;
-        trs.tracker->normalizer.trim_win_payload(tsd);
-        return;
-    }
-
-    // FIXIT-L don't allocate overlapped part
-    TcpSegmentNode* const tsn = TcpSegmentNode::init(tsd);
-
-    tsn->offset = slide;
-    tsn->o_offset = slide;
-    tsn->c_len = (uint16_t)new_size;
-    tsn->i_len = (uint16_t)new_size;
-    tsn->i_seq = tsn->c_seq = seq;
-    tsn->ts = tsd.get_timestamp();
-
-    // FIXIT-M the urgent ptr handling is broken... urg_offset could be set here but currently
-    // not actually referenced anywhere else.  In 2.9.7 the FlushStream function did reference
-    // this field but that code has been lost... urg ptr handling needs to be reviewed and fixed
-    // tsn->urg_offset = trs.tracker->normalizer.set_urg_offset(tsd.get_tcph(), tsd.get_seg_len());
-
-    queue_reassembly_segment(trs, left, tsn);
-
-    trs.sos.seg_bytes_logical += tsn->c_len;
-    trs.sos.total_bytes_queued += tsn->c_len;
-    tsd.set_packet_flags(PKT_STREAM_INSERT);
-}
-
-void TcpReassembler::dup_reassembly_segment(
-    TcpReassemblerState& trs, TcpSegmentNode* left, TcpSegmentNode** retSeg)
-{
-    TcpSegmentNode* tsn = TcpSegmentNode::init(*left);
-    tcpStats.segs_split++;
-
-    // twiddle the values for overlaps
-    tsn->c_len = left->c_len;
-    tsn->i_seq = tsn->c_seq = left->i_seq;
-    queue_reassembly_segment(trs, left, tsn);
-
-    *retSeg = tsn;
-}
-
-bool TcpReassembler::add_alert(TcpReassemblerState& trs, uint32_t gid, uint32_t sid)
-{
-    assert(trs.alerts.size() <=
-        (uint32_t)(get_ips_policy()->rules_loaded + get_ips_policy()->rules_shared));
-
-    if (!this->check_alerted(trs, gid, sid))
-        trs.alerts.emplace_back(gid, sid);
-
-    return true;
-}
-
-bool TcpReassembler::check_alerted(TcpReassemblerState& trs, uint32_t gid, uint32_t sid)
-{
-    return std::any_of(trs.alerts.cbegin(), trs.alerts.cend(),
-        [gid, sid](const StreamAlertInfo& alert){ return alert.gid == gid && alert.sid == sid; });
-}
-
-int TcpReassembler::update_alert(TcpReassemblerState& trs, uint32_t gid, uint32_t sid,
-    uint32_t event_id, uint32_t event_second)
-{
-    // FIXIT-M comparison of seq_num is wrong, compare value is always 0, should be seq_num of wire packet
-    uint32_t seq_num = 0;
-
-    auto it = std::find_if(trs.alerts.begin(), trs.alerts.end(),
-        [gid, sid, seq_num](const StreamAlertInfo& alert)
-        { return alert.gid == gid && alert.sid == sid && SEQ_EQ(alert.seq, seq_num); });
-    if (it != trs.alerts.end())
-    {
-        (*it).event_id = event_id;
-        (*it).event_second = event_second;
-        return 0;
-    }
-
-    return -1;
-}
-
-void TcpReassembler::purge_alerts(TcpReassemblerState& trs)
-{
-    Flow* flow = trs.sos.session->flow;
-
-    for ( auto& alert : trs.alerts )
-        Stream::log_extra_data(flow, trs.xtradata_mask, alert);
-
-    if ( !flow->is_suspended() )
-        trs.alerts.clear();
-}
-
-void TcpReassembler::purge_to_seq(TcpReassemblerState& trs, uint32_t flush_seq)
+void TcpReassembler::purge_to_seq(uint32_t flush_seq)
 {
-    assert( trs.sos.seglist.head );
-    uint32_t last_ts = 0;
-
-    TcpSegmentNode* tsn = trs.sos.seglist.head;
-    while ( tsn && SEQ_LT(tsn->i_seq, flush_seq))
-    {
-        if ( tsn->c_len )
-            break;
-
-        TcpSegmentNode* dump_me = tsn;
-        tsn = tsn->next;
-        if (dump_me->ts > last_ts)
-            last_ts = dump_me->ts;
-
-        delete_reassembly_segment(trs, dump_me);
-    }
-
-    if ( SEQ_LT(trs.tracker->rcv_nxt, flush_seq) )
-        trs.tracker->rcv_nxt = flush_seq;
+    seglist.purge_flushed_segments(flush_seq);
 
     if ( last_pdu )
     {
-        if ( trs.tracker->normalizer.is_tcp_ips_enabled() )
-            last_pdu->context->register_post_callback(purge_alerts_callback_ips);
-        else
-            last_pdu->context->register_post_callback(purge_alerts_callback_ackd);
-
+        tracker.tcp_alerts.purge_alerts(*last_pdu, tracker.normalizer.is_tcp_ips_enabled());
         last_pdu = nullptr;
     }
     else
-        purge_alerts(trs);
-
-    if ( trs.sos.seglist.head == nullptr )
-        trs.sos.seglist.tail = nullptr;
-
-    /* Update the "last" time stamp seen from the other side
-     * to be the most recent timestamp (largest) that was removed
-     * from the queue.  This will ensure that as we go forward,
-     * last timestamp is the highest one that we had stored and
-     * purged and handle the case when packets arrive out of order,
-     * such as:
-     * P1: seq 10, length 10, timestamp 10
-     * P3: seq 30, length 10, timestamp 30
-     * P2: seq 20, length 10, timestamp 20
-     *
-     * Without doing it this way, the timestamp would be 20.  With
-     * the next packet to arrive (P4, seq 40), the ts_last value
-     * wouldn't be updated for the talker in ProcessTcp() since that
-     * code specifically looks for the NEXT sequence number.
-     */
-    if ( last_ts )
-    {
-        if ( trs.server_side )
-        {
-            int32_t delta = last_ts - trs.sos.session->client.get_ts_last();
-            if ( delta > 0 )
-                trs.sos.session->client.set_ts_last(last_ts);
-        }
-       else
-        {
-            int32_t delta = last_ts - trs.sos.session->server.get_ts_last();
-            if ( delta > 0 )
-                trs.sos.session->server.set_ts_last(last_ts);
-        }
-    }
+        tracker.tcp_alerts.purge_alerts(seglist.session->flow);
 }
 
 // must only purge flushed and acked bytes we may flush partial segments
@@ -385,34 +116,31 @@ void TcpReassembler::purge_to_seq(TcpReassemblerState& trs, uint32_t flush_seq)
 // part of a segment
 // * FIXIT-L need flag to mark any reassembled packets that have a gap
 //   (if we reassemble such)
-void TcpReassembler::purge_flushed_ackd(TcpReassemblerState& trs)
+void TcpReassembler::purge_flushed_ackd()
 {
-    TcpSegmentNode* tsn = trs.sos.seglist.head;
-    uint32_t seq;
-
-    if (!trs.sos.seglist.head)
+    if ( !seglist.head )
         return;
 
-    seq = trs.sos.seglist.head->i_seq;
-
-    while ( tsn && !tsn->c_len )
+    uint32_t seq = seglist.head->start_seq();
+    TcpSegmentNode* tsn = seglist.head;
+    while ( tsn && !tsn->unscanned() )
     {
-        uint32_t end = tsn->i_seq + tsn->i_len;
+        uint32_t end = tsn->next_seq();
 
-        if ( SEQ_GT(end, trs.tracker->r_win_base) )
+        if ( SEQ_GT(end, tracker.r_win_base) )
             break;
 
         seq = end;
         tsn = tsn->next;
     }
 
-    if ( seq != trs.sos.seglist.head->i_seq )
-        purge_to_seq(trs, seq);
+    if ( !SEQ_EQ(seq, seglist.head->start_seq()) )
+        purge_to_seq(seq);
 }
 
-void TcpReassembler::show_rebuilt_packet(const TcpReassemblerState& trs, Packet* pkt)
+void TcpReassembler::show_rebuilt_packet(Packet* pkt)
 {
-    if ( trs.sos.session->tcp_config->flags & STREAM_CONFIG_SHOW_PACKETS )
+    if ( seglist.session->tcp_config->flags & STREAM_CONFIG_SHOW_PACKETS )
     {
         // FIXIT-L setting conf here is required because this is called before context start
         pkt->context->conf = SnortConfig::get_conf();
@@ -421,28 +149,28 @@ void TcpReassembler::show_rebuilt_packet(const TcpReassemblerState& trs, Packet*
     }
 }
 
-int TcpReassembler::flush_data_segments(TcpReassemblerState& trs, uint32_t flush_len, Packet* pdu)
+int TcpReassembler::flush_data_segments(uint32_t flush_len, Packet* pdu)
 {
     uint32_t flags = PKT_PDU_HEAD;
-    uint32_t to_seq = trs.sos.seglist.cur_rseg->c_seq + flush_len;
+
+    uint32_t to_seq = seglist.cur_rseg->scan_seq() + flush_len;
     uint32_t remaining_bytes = flush_len;
     uint32_t total_flushed = 0;
 
     while ( remaining_bytes )
     {
-        TcpSegmentNode* tsn = trs.sos.seglist.cur_rseg;
-        unsigned bytes_to_copy = ( tsn->c_len <= remaining_bytes ) ? tsn->c_len : remaining_bytes;
+        TcpSegmentNode* tsn = seglist.cur_rseg;
+        unsigned bytes_to_copy = ( tsn->unscanned() <= remaining_bytes ) ? tsn->unscanned() : remaining_bytes;
 
         remaining_bytes -= bytes_to_copy;
         if ( !remaining_bytes )
             flags |= PKT_PDU_TAIL;
         else
-            assert( bytes_to_copy >= tsn->c_len );
+            assert( bytes_to_copy >= tsn->unscanned() );
 
         unsigned bytes_copied = 0;
-        const StreamBuffer sb = trs.tracker->get_splitter()->reassemble(
-            trs.sos.session->flow, flush_len, total_flushed, tsn->payload(),
-            bytes_to_copy, flags, bytes_copied);
+        const StreamBuffer sb = splitter->reassemble(seglist.session->flow, flush_len, total_flushed,
+            tsn->paf_data(), bytes_to_copy, flags, bytes_copied);
 
         if ( sb.data )
         {
@@ -451,48 +179,41 @@ int TcpReassembler::flush_data_segments(TcpReassemblerState& trs, uint32_t flush
         }
 
         total_flushed += bytes_copied;
-        tsn->update_reassembly_cursor(bytes_copied);
+        tsn->advance_cursor(bytes_copied);
         flags = 0;
 
-        if ( !tsn->c_len )
+        if ( !tsn->unscanned() )
         {
-            trs.flush_count++;
-            update_next(trs, *tsn);
+            seglist.flush_count++;
+            seglist.update_next(tsn);
         }
 
         /* Check for a gap/missing packet */
         // FIXIT-L FIN may be in to_seq causing bogus gap counts.
-        if ( tsn->is_packet_missing(to_seq) or trs.paf_state.paf == StreamSplitter::SKIP )
+        if ( tsn->is_packet_missing(to_seq) or paf.state == StreamSplitter::SKIP )
         {
             // FIXIT-H // assert(false); find when this scenario happens
             // FIXIT-L this is suboptimal - better to exclude fin from to_seq
-            if ( !trs.tracker->is_fin_seq_set() or
-                SEQ_LEQ(to_seq, trs.tracker->get_fin_final_seq()) )
+            if ( !tracker.is_fin_seq_set() or
+                SEQ_LEQ(to_seq, tracker.get_fin_final_seq()) )
             {
-                trs.tracker->set_tf_flags(TF_MISSING_PKT);
+                tracker.set_tf_flags(TF_MISSING_PKT);
             }
             break;
         }
 
-        if ( sb.data || !trs.sos.seglist.cur_rseg )
+        if ( sb.data || !seglist.cur_rseg )
             break;
     }
 
-    if ( trs.paf_state.paf == StreamSplitter::SKIP )
-        update_skipped_bytes(remaining_bytes, trs);
+    if ( paf.state == StreamSplitter::SKIP )
+        update_skipped_bytes(remaining_bytes);
 
     return total_flushed;
 }
 
-static inline bool both_splitters_aborted(Flow* flow)
-{
-    uint32_t both_splitters_yoinked = (SSNFLAG_ABORT_CLIENT | SSNFLAG_ABORT_SERVER);
-    return (flow->get_session_flags() & both_splitters_yoinked) == both_splitters_yoinked;
-}
-
 // FIXIT-L consolidate encode format, update, and this into new function?
-void TcpReassembler::prep_pdu(
-    TcpReassemblerState&, Flow* flow, Packet* p, uint32_t pkt_flags, Packet* pdu)
+void TcpReassembler::prep_pdu(Flow* flow, Packet* p, uint32_t pkt_flags, Packet* pdu)
 {
     pdu->ptrs.set_pkt_type(PktType::PDU);
     pdu->proto_bits |= PROTO_BIT__TCP;
@@ -539,17 +260,16 @@ void TcpReassembler::prep_pdu(
     }
 }
 
-Packet* TcpReassembler::initialize_pdu(
-    TcpReassemblerState& trs, Packet* p, uint32_t pkt_flags, struct timeval tv)
+Packet* TcpReassembler::initialize_pdu(Packet* p, uint32_t pkt_flags, struct timeval tv)
 {
     // partial flushes already set the pdu for http_inspect splitter processing
     Packet* pdu = p->was_set() ? p : DetectionEngine::set_next_packet(p);
 
     EncodeFlags enc_flags = 0;
     DAQ_PktHdr_t pkth;
-    trs.sos.session->get_packet_header_foo(&pkth, p->pkth, pkt_flags);
+    seglist.session->get_packet_header_foo(&pkth, p->pkth, pkt_flags);
     PacketManager::format_tcp(enc_flags, p, pdu, PSEUDO_PKT_TCP, &pkth, pkth.opaque);
-    prep_pdu(trs, trs.sos.session->flow, p, pkt_flags, pdu);
+    prep_pdu(seglist.session->flow, p, pkt_flags, pdu);
     assert(pdu->pkth == pdu->context->pkth);
     pdu->context->pkth->ts = tv;
     pdu->dsize = 0;
@@ -569,21 +289,20 @@ Packet* TcpReassembler::initialize_pdu(
 }
 
 // flush a seglist up to the given point, generate a pseudopacket, and fire it thru the system.
-int TcpReassembler::flush_to_seq(
-    TcpReassemblerState& trs, uint32_t bytes, Packet* p, uint32_t pkt_flags)
+int TcpReassembler::flush_to_seq(uint32_t bytes, Packet* p, uint32_t pkt_flags)
 {
-    assert( p && trs.sos.seglist.cur_rseg);
+    assert( p && seglist.cur_rseg);
 
-    trs.tracker->clear_tf_flags(TF_MISSING_PKT | TF_MISSING_PREV_PKT);
+    tracker.clear_tf_flags(TF_MISSING_PKT | TF_MISSING_PREV_PKT);
 
-    TcpSegmentNode* tsn = trs.sos.seglist.cur_rseg;
-    assert( trs.sos.seglist_base_seq == tsn->c_seq);
+    TcpSegmentNode* tsn = seglist.cur_rseg;
+    assert( seglist.seglist_base_seq == tsn->scan_seq());
 
-    Packet* pdu = initialize_pdu(trs, p, pkt_flags, tsn->tv);
-    int32_t flushed_bytes = flush_data_segments(trs, bytes, pdu);
+    Packet* pdu = initialize_pdu(p, pkt_flags, tsn->tv);
+    int32_t flushed_bytes = flush_data_segments(bytes, pdu);
     assert( flushed_bytes );
 
-    trs.sos.seglist_base_seq += flushed_bytes;
+    seglist.seglist_base_seq += flushed_bytes;
 
     if ( pdu->data )
     {
@@ -592,7 +311,7 @@ int TcpReassembler::flush_to_seq(
         else
             pdu->packet_flags |= ( PKT_REBUILT_STREAM | PKT_STREAM_EST );
 
-        show_rebuilt_packet(trs, pdu);
+        show_rebuilt_packet(pdu);
         tcpStats.rebuilt_packets++;
         tcpStats.rebuilt_bytes += flushed_bytes;
 
@@ -603,7 +322,7 @@ int TcpReassembler::flush_to_seq(
         else
             last_pdu = nullptr;
 
-        trs.tracker->finalize_held_packet(p);
+        tracker.finalize_held_packet(p);
     }
     else
     {
@@ -612,36 +331,34 @@ int TcpReassembler::flush_to_seq(
     }
 
     // FIXIT-L abort should be by PAF callback only since recovery may be possible
-    if ( trs.tracker->get_tf_flags() & TF_MISSING_PKT )
+    if ( tracker.get_tf_flags() & TF_MISSING_PKT )
     {
-        trs.tracker->set_tf_flags(TF_MISSING_PREV_PKT | TF_PKT_MISSED);
-        trs.tracker->clear_tf_flags(TF_MISSING_PKT);
+        tracker.set_tf_flags(TF_MISSING_PREV_PKT | TF_PKT_MISSED);
+        tracker.clear_tf_flags(TF_MISSING_PKT);
         tcpStats.gaps++;
     }
     else
-        trs.tracker->clear_tf_flags(TF_MISSING_PREV_PKT);
+        tracker.clear_tf_flags(TF_MISSING_PREV_PKT);
 
     return flushed_bytes;
 }
 
-// flush a seglist up to the given point, generate a pseudopacket, and fire it thru the system.
-int TcpReassembler::do_zero_byte_flush(TcpReassemblerState& trs, Packet* p, uint32_t pkt_flags)
+int TcpReassembler::do_zero_byte_flush(Packet* p, uint32_t pkt_flags)
 {
     unsigned bytes_copied = 0;
 
-    const StreamBuffer sb = trs.tracker->get_splitter()->reassemble(
-        trs.sos.session->flow, 0, 0, nullptr, 0, (PKT_PDU_HEAD | PKT_PDU_TAIL), bytes_copied);
+    const StreamBuffer sb = splitter->reassemble(seglist.session->flow, 0, 0,
+        nullptr, 0, (PKT_PDU_HEAD | PKT_PDU_TAIL), bytes_copied);
 
      if ( sb.data )
      {
-        Packet* pdu = initialize_pdu(trs, p, pkt_flags, p->pkth->ts);
+        Packet* pdu = initialize_pdu(p, pkt_flags, p->pkth->ts);
         /* setup the pseudopacket payload */
         pdu->data = sb.data;
         pdu->dsize = sb.length;
         pdu->packet_flags |= (PKT_REBUILT_STREAM | PKT_STREAM_EST | PKT_PDU_HEAD | PKT_PDU_TAIL);
 
-        trs.flush_count++;
-        show_rebuilt_packet(trs, pdu);
+        show_rebuilt_packet(pdu);
 
         DetectionEngine de;
         de.inspect(pdu);
@@ -650,128 +367,101 @@ int TcpReassembler::do_zero_byte_flush(TcpReassemblerState& trs, Packet* p, uint
      return bytes_copied;
 }
 
-// get the footprint for the current trs.sos.seglist, the difference
+// get the footprint for the current seglist, the difference
 // between our base sequence and the last ack'd sequence we received
 
-uint32_t TcpReassembler::get_q_footprint(TcpReassemblerState& trs)
+uint32_t TcpReassembler::get_q_footprint()
 {
     int32_t footprint = 0;
     int32_t sequenced = 0;
 
-    if ( SEQ_GT(trs.tracker->r_win_base, trs.sos.seglist_base_seq) )
-        footprint = trs.tracker->r_win_base - trs.sos.seglist_base_seq;
+    if ( SEQ_GT(tracker.r_win_base, seglist.seglist_base_seq) )
+        footprint = tracker.r_win_base - seglist.seglist_base_seq;
 
     if ( footprint )
-        sequenced = get_q_sequenced(trs);
+        sequenced = get_q_sequenced();
 
     return ( footprint > sequenced ) ? sequenced : footprint;
 }
 
 // FIXIT-P get_q_sequenced() performance could possibly be
-// boosted by tracking sequenced bytes as trs.sos.seglist is updated
+// boosted by tracking sequenced bytes as seglist is updated
 // to avoid the while loop, etc. below.
 
-uint32_t TcpReassembler::get_q_sequenced(TcpReassemblerState& trs)
+uint32_t TcpReassembler::get_q_sequenced()
 {
-    TcpSegmentNode* tsn = trs.sos.seglist.cur_rseg;
+    TcpSegmentNode* tsn = seglist.cur_rseg;
 
     if ( !tsn )
     {
-        tsn = trs.sos.seglist.head;
+        tsn = seglist.head;
 
-        if ( !tsn || SEQ_LT(trs.tracker->r_win_base, tsn->c_seq) )
+        if ( !tsn || SEQ_LT(tracker.r_win_base, tsn->scan_seq()) )
             return 0;
 
-        trs.sos.seglist.cur_rseg = tsn;
+        seglist.cur_rseg = tsn;
     }
 
     uint32_t len = 0;
-    const uint32_t limit = trs.tracker->get_splitter()->max();
-    while ( len < limit and next_no_gap(*tsn) )
+    const uint32_t limit = splitter->max();
+    while ( len < limit and tsn->next_no_gap() )
     {
-        if ( !tsn->c_len )
-            trs.sos.seglist.cur_rseg = tsn->next;
+
+        if ( !tsn->unscanned() )
+            seglist.cur_rseg = tsn->next;
         else
-            len += tsn->c_len;
+            len += tsn->unscanned();
 
         tsn = tsn->next;
     }
-    if ( tsn->c_len )
-        len += tsn->c_len;
+    if ( tsn->unscanned() )
+        len += tsn->unscanned();
 
-    trs.sos.seglist_base_seq = trs.sos.seglist.cur_rseg->c_seq;
+    seglist.seglist_base_seq = seglist.cur_rseg->scan_seq();
 
     return len;
 }
 
-bool TcpReassembler::is_q_sequenced(TcpReassemblerState& trs)
+bool TcpReassembler::is_q_sequenced()
 {
-    TcpSegmentNode* tsn = trs.sos.seglist.cur_rseg;
+    TcpSegmentNode* tsn = seglist.cur_rseg;
 
     if ( !tsn )
     {
-        tsn = trs.sos.seglist.head;
-
-        if ( !tsn || SEQ_LT(trs.tracker->r_win_base, tsn->c_seq) )
+        tsn = seglist.head;
+        if ( !tsn || SEQ_LT(tracker.r_win_base, tsn->scan_seq()) )
             return false;
 
-        trs.sos.seglist.cur_rseg = tsn;
+        seglist.cur_rseg = tsn;
     }
 
-    while ( next_no_gap(*tsn) )
+    while ( tsn->next_no_gap() )
     {
-        if ( tsn->c_len )
+        if ( tsn->unscanned() )
             break;
 
-        tsn = trs.sos.seglist.cur_rseg = tsn->next;
-    }
-
-    trs.sos.seglist_base_seq = tsn->c_seq;
-
-    return (tsn->c_len != 0);
-}
-
-int TcpReassembler::flush_stream(
-    TcpReassemblerState& trs, Packet* p, uint32_t dir, bool final_flush)
-{
-    // this is not always redundant; stream_reassemble rule option causes trouble
-    if ( !trs.tracker->is_reassembly_enabled() )
-        return 0;
-
-    if ( trs.sos.session->flow->two_way_traffic()
-            or (trs.tracker->get_tcp_state() == TcpStreamTracker::TCP_MID_STREAM_RECV) )
-    {
-        uint32_t bytes = 0;
-
-        if ( trs.tracker->normalizer.is_tcp_ips_enabled() )
-            bytes = get_q_sequenced(trs);  // num bytes in pre-ack mode
-        else
-            bytes = get_q_footprint(trs);  // num bytes in post-ack mode
-
-        if ( bytes )
-            return flush_to_seq(trs, bytes, p, dir);
+        tsn = seglist.cur_rseg = tsn->next;
     }
 
-    if ( final_flush )
-        return do_zero_byte_flush(trs, p, dir);
+    seglist.seglist_base_seq = tsn->scan_seq();
 
-    return 0;
+    return (tsn->unscanned() != 0);
 }
 
-void TcpReassembler::final_flush(TcpReassemblerState& trs, Packet* p, uint32_t dir)
+void TcpReassembler::final_flush(Packet* p, uint32_t dir)
 {
-    trs.tracker->set_tf_flags(TF_FORCE_FLUSH);
+    tracker.set_tf_flags(TF_FORCE_FLUSH);
 
-    if ( flush_stream(trs, p, dir, true) )
+    if ( flush_stream(p, dir, true) )
     {
-        if ( trs.server_side )
+        if ( server_side )
             tcpStats.server_cleanups++;
         else
             tcpStats.client_cleanups++;
 
-        purge_flushed_ackd(trs);
+        purge_flushed_ackd();
     }
-    trs.tracker->clear_tf_flags(TF_FORCE_FLUSH);
+    tracker.clear_tf_flags(TF_FORCE_FLUSH);
 }
 
 static Packet* get_packet(Flow* flow, uint32_t flags, bool c2s)
@@ -813,724 +503,107 @@ static Packet* get_packet(Flow* flow, uint32_t flags, bool c2s)
     return p;
 }
 
-void TcpReassembler::finish_and_final_flush(
-    TcpReassemblerState& trs, Flow* flow, bool clear, Packet* p)
-{
-    bool pending = clear and paf_initialized(&trs.paf_state)
-        and trs.tracker->splitter_finish(flow);
-
-    if ( pending and !(flow->ssn_state.ignore_direction & trs.ignore_dir) )
-        final_flush(trs, p, trs.packet_dir);
-}
-
-// Call this only from outside reassembly.
-void TcpReassembler::flush_queued_segments(
-    TcpReassemblerState& trs, Flow* flow, bool clear, const Packet* p)
-{
-    if ( p )
-    {
-       finish_and_final_flush(trs, flow, clear, const_cast<Packet*>(p));
-    }
-    else
-    {
-        Packet* pdu = get_packet(flow, trs.packet_dir, trs.server_side);
-
-        bool pending = clear and paf_initialized(&trs.paf_state);
-        if ( pending )
-        {
-            DetectionEngine de;
-            pending = trs.tracker->splitter_finish(flow);
-        }
-
-        if ( pending and !(flow->ssn_state.ignore_direction & trs.ignore_dir) )
-            final_flush(trs, pdu, trs.packet_dir);
-    }
-}
-
-// see scan_data_post_ack() for details
-// the key difference is that we operate on forward moving data
-// because we don't wait until it is acknowledged
-int32_t TcpReassembler::scan_data_pre_ack(TcpReassemblerState& trs, uint32_t* flags, Packet* p)
+bool TcpReassembler::splitter_finish(snort::Flow* flow)
 {
-    assert(trs.sos.session->flow == p->flow);
-
-    int32_t ret_val = FINAL_FLUSH_HOLD;
-
-    if ( SEQ_GT(trs.sos.seglist.head->c_seq, trs.sos.seglist_base_seq) )
-        return ret_val;
-
-    if ( !trs.sos.seglist.cur_rseg )
-        trs.sos.seglist.cur_rseg = trs.sos.seglist.cur_sseg;
-
-    if ( !is_q_sequenced(trs) )
-        return ret_val;
-
-    TcpSegmentNode* tsn = trs.sos.seglist.cur_sseg;
-    uint32_t total = tsn->c_seq - trs.sos.seglist_base_seq;
+    if (!splitter)
+        return true;
 
-    ret_val = FINAL_FLUSH_OK;
-    while ( tsn && *flags )
+    if (!splitter_finish_flag)
     {
-        total += tsn->c_len;
-
-        uint32_t end = tsn->c_seq + tsn->c_len;
-        uint32_t pos = paf_position(&trs.paf_state);
-
-        if ( paf_initialized(&trs.paf_state) && SEQ_LEQ(end, pos) )
-        {
-            if ( !next_no_gap(*tsn) )
-            {
-                ret_val = FINAL_FLUSH_HOLD;
-                break;
-            }
-
-            tsn = tsn->next;
-            continue;
-        }
-
-        if ( next_no_gap_c(*tsn) )
-            *flags |= PKT_MORE_TO_FLUSH;
-        else
-            *flags &= ~PKT_MORE_TO_FLUSH;
-        int32_t flush_pt = paf_check(
-            trs.tracker->get_splitter(), &trs.paf_state, p, tsn->payload(),
-            tsn->c_len, total, tsn->c_seq, flags);
-
-        if (flush_pt >= 0)
-        {
-            trs.sos.seglist.cur_sseg = tsn;
-            update_rcv_nxt(trs, *tsn);
-            return flush_pt;
-        }
-
-        if (!next_no_gap(*tsn) || (trs.paf_state.paf == StreamSplitter::STOP))
-        {
-            if ( !(next_no_gap(*tsn) || fin_no_gap(*tsn, trs)) )
-                ret_val = FINAL_FLUSH_HOLD;
-            break;
-        }
-
-        tsn = tsn->next;
+        splitter_finish_flag = true;
+        return splitter->finish(flow);
     }
-
-    trs.sos.seglist.cur_sseg = tsn;
-
-    if ( tsn )
-        update_rcv_nxt(trs, *tsn);
-
-    return ret_val;
+    // there shouldn't be any un-flushed data beyond this point,
+    // returning false here, discards it
+    return false;
 }
 
-static inline void fallback(TcpStreamTracker& trk, bool server_side)
+void TcpReassembler::finish_and_final_flush(Flow* flow, bool clear, Packet* p)
 {
-#ifndef NDEBUG
-    StreamSplitter* splitter = trk.get_splitter();
-    assert(splitter);
+    bool pending = clear and paf.paf_initialized() and splitter_finish(flow);
 
-    // FIXIT-L: consolidate these 3
-    bool to_server = splitter->to_server();
-    assert(server_side == to_server && server_side == !trk.client_tracker);
-#endif
-
-    trk.set_splitter(new AtomSplitter(server_side));
-    tcpStats.partial_fallbacks++;
+    if ( pending and !(flow->ssn_state.ignore_direction & ignore_dir) )
+        final_flush(p, packet_dir);
 }
 
-void TcpReassembler::fallback(TcpStreamTracker& tracker, bool server_side)
+// Call this only from outside reassembly.
+void TcpReassembler::flush_queued_segments(Flow* flow, bool clear, Packet* p)
 {
-    ::fallback(tracker, server_side);
-
-    Flow* flow = tracker.session->flow;
-    if ( server_side )
-        flow->set_session_flags(SSNFLAG_ABORT_SERVER);
-    else
-        flow->set_session_flags(SSNFLAG_ABORT_CLIENT);
-
-    if ( flow->gadget and both_splitters_aborted(flow) )
+    if ( p )
     {
-        flow->clear_gadget();
-
-        if (flow->clouseau)
-            flow->clear_clouseau();
-
-        tcpStats.inspector_fallbacks++;
+        finish_and_final_flush(flow, clear, p);
     }
-}
-
-bool TcpReassembler::segment_within_seglist_window(TcpReassemblerState& trs, TcpSegmentDescriptor& tsd)
-{
-    if ( !trs.sos.seglist.head )
-        return true;
-
-    // Left side
-    uint32_t start;
-    if ( SEQ_LT(trs.sos.seglist_base_seq, trs.sos.seglist.head->i_seq) )
-        start = trs.sos.seglist_base_seq;
     else
-        start = trs.sos.seglist.head->i_seq;
-
-    if ( SEQ_LEQ(tsd.get_end_seq(), start) )
-        return false;
-
-    // Right side
-    uint32_t end = (trs.sos.seglist.tail->i_seq + trs.sos.seglist.tail->i_len);
-    if ( SEQ_GEQ(tsd.get_seq(), end) )
-        return false;
-
-    return true;
-}
-
-void TcpReassembler::check_first_segment_hole(TcpReassemblerState& trs)
-{
-    if ( SEQ_LT(trs.sos.seglist_base_seq, trs.sos.seglist.head->i_seq) )
-        {
-            trs.sos.seglist_base_seq = trs.sos.seglist.head->i_seq;
-            trs.tracker->rcv_nxt = trs.tracker->r_win_base;
-            trs.paf_state.paf = StreamSplitter::START;
-        }
-}
-
-void TcpReassembler::update_rcv_nxt(TcpReassemblerState& trs, TcpSegmentNode& tsn)
-{
-    uint32_t temp = (tsn.i_seq + tsn.i_len);
-
-    if ( SEQ_GT(temp, trs.tracker->rcv_nxt) )
-        trs.tracker->rcv_nxt = temp;
-}
-
-bool TcpReassembler::has_seglist_hole(TcpReassemblerState& trs, TcpSegmentNode& tsn, PAF_State& ps,
-    uint32_t& total, uint32_t& flags)
-{
-    if ( !tsn.prev or SEQ_GEQ(tsn.prev->c_seq + tsn.prev->c_len, tsn.c_seq) or
-        SEQ_GEQ(tsn.c_seq, trs.tracker->r_win_base) )
-        {
-            check_first_segment_hole(trs);
-            return false;
-        }
-
-    // safety - prevent seq + total < seq
-    if ( total > 0x7FFFFFFF )
-        total = 0x7FFFFFFF;
-
-    if ( !ps.tot )
-        flags |= PKT_PDU_HEAD;
-
-    return true;
-}
-
-void TcpReassembler::purge_segments_left_of_hole(TcpReassemblerState& trs, const TcpSegmentNode* end_tsn)
-{
-    uint32_t packets_skipped = 0;
-
-    TcpSegmentNode* cur_tsn = trs.sos.seglist.head;
-    do
-    {
-        TcpSegmentNode* drop_tsn = cur_tsn;
-        cur_tsn = cur_tsn->next;
-        delete_reassembly_segment(trs, drop_tsn);
-        ++packets_skipped;
-    } while( cur_tsn and cur_tsn != end_tsn );
-
-    if (PacketTracer::is_active())
-        PacketTracer::log("Stream: Skipped %u packets before seglist hole)\n", packets_skipped);
-}
-
-void TcpReassembler::reset_asymmetric_flow_reassembly(TcpReassemblerState& trs)
-{
-    TcpSegmentNode* tsn = trs.sos.seglist.head;
-    // if there is a hole at the beginning, skip it...
-    if ( SEQ_GT(tsn->i_seq, trs.sos.seglist_base_seq) )
     {
-        trs.sos.seglist_base_seq = tsn->i_seq;
-        if (PacketTracer::is_active())
-            PacketTracer::log("Stream: Skipped hole at beginning of the seglist\n");
-    }
+        Packet* pdu = get_packet(flow, packet_dir, server_side);
 
-    while ( tsn )
-    {
-        if ( tsn->next and SEQ_GT(tsn->next->i_seq, tsn->i_seq + tsn->i_len) )
+        bool pending = clear and paf.paf_initialized();
+        if ( pending )
         {
-            tsn = tsn->next;
-            purge_segments_left_of_hole(trs, tsn);
-            trs.sos.seglist_base_seq = trs.sos.seglist.head->i_seq;
+            DetectionEngine de;
+            pending = splitter_finish(flow);
         }
-       else
-            tsn = tsn->next;
-    }
 
-    if ( trs.tracker->is_splitter_paf() )
-        fallback(*trs.tracker, trs.server_side);
-    else
-        paf_reset(&trs.paf_state);
+        if ( pending and !(flow->ssn_state.ignore_direction & ignore_dir) )
+            final_flush(pdu, packet_dir);
+    }
 }
 
-void TcpReassembler::skip_midstream_pickup_seglist_hole(TcpReassemblerState& trs, TcpSegmentDescriptor& tsd)
-{
-    uint32_t ack = tsd.get_ack();
-
-    TcpSegmentNode* tsn = trs.sos.seglist.head;
-    while ( tsn )
-    {
-        if ( SEQ_GEQ( tsn->i_seq + tsn->i_len, ack) )
-            break;
-
-        if ( tsn->next and SEQ_GT(tsn->next->i_seq, tsn->i_seq + tsn->i_len) )
-        {
-            tsn = tsn->next;
-            purge_segments_left_of_hole(trs, tsn);
-            trs.sos.seglist_base_seq = trs.sos.seglist.head->i_seq;
-        }
-        else if ( !tsn->next and SEQ_LT(tsn->i_seq + tsn->i_len, ack) )
-        {
-            tsn = tsn->next;
-            purge_segments_left_of_hole(trs, tsn);
-            trs.sos.seglist_base_seq = ack;
-        }
-        else
-            tsn = tsn->next;
-    }
 
-    tsn = trs.sos.seglist.head;
-    if ( tsn )
+void TcpReassembler::check_first_segment_hole()
+{
+    if ( SEQ_LT(seglist.seglist_base_seq, seglist.head->start_seq()) )
     {
-        paf_initialize(&trs.paf_state, tsn->i_seq);
-
-        while ( next_no_gap(*tsn) )
-            tsn = tsn->next;
-        trs.tracker->rcv_nxt = tsn->i_seq + tsn->i_len;
+        seglist.seglist_base_seq = seglist.head->start_seq();
+        seglist.advance_rcv_nxt();
+        paf.state = StreamSplitter::START;
     }
-    else
-        trs.tracker->rcv_nxt = ack;
 }
 
-bool  TcpReassembler::flush_on_asymmetric_flow(const TcpReassemblerState &trs, uint32_t flushed, snort::Packet *p)
+uint32_t TcpReassembler::perform_partial_flush(Flow* flow, Packet*& p)
 {
-    bool asymmetric = flushed && trs.sos.seg_count && !p->flow->two_way_traffic() && !p->ptrs.tcph->is_syn();
-    if ( asymmetric )
-    {
-        TcpStreamTracker::TcpState peer = trs.tracker->session->get_peer_state(trs.tracker);
-        asymmetric = ( peer == TcpStreamTracker::TCP_SYN_SENT || peer == TcpStreamTracker::TCP_SYN_RECV
-            || peer == TcpStreamTracker::TCP_MID_STREAM_SENT );
-    }
-
-    return asymmetric;
+    p = get_packet(flow, packet_dir, server_side);
+    return perform_partial_flush(p);
 }
 
-// iterate over trs.sos.seglist and scan all new acked bytes
-// - new means not yet scanned
-// - must use trs.sos.seglist data (not packet) since this packet may plug a
-//   hole and enable paf scanning of following segments
-// - if we reach a flush point
-//   - return bytes to flush if data available (must be acked)
-//   - return zero if not yet received or received but not acked
-// - if we reach a skip point
-//   - jump ahead and resume scanning any available data
-// - must stop if we reach a gap
-// - one segment may lead to multiple checks since
-//   it may contain multiple encapsulated PDUs
-// - if we partially scan a segment we must save state so we
-//   know where we left off and can resume scanning the remainder
-int32_t TcpReassembler::scan_data_post_ack(TcpReassemblerState& trs, uint32_t* flags, Packet* p)
+// No error checking here, so the caller must ensure that p, p->flow are not null.
+uint32_t TcpReassembler::perform_partial_flush(Packet* p, uint32_t flushed)
 {
-    assert(trs.sos.session->flow == p->flow);
-
-    int32_t ret_val = FINAL_FLUSH_HOLD;
-
-    if ( !trs.sos.seglist.cur_sseg || SEQ_GEQ(trs.sos.seglist_base_seq, trs.tracker->r_win_base) )
-        return ret_val ;
-
-    if ( !trs.sos.seglist.cur_rseg )
-        trs.sos.seglist.cur_rseg = trs.sos.seglist.cur_sseg;
-
-    StreamSplitter* splitter = trs.tracker->get_splitter();
-
-    uint32_t total = 0;
-    TcpSegmentNode* tsn = trs.sos.seglist.cur_sseg;
-    if ( paf_initialized(&trs.paf_state) )
+    if ( splitter->init_partial_flush(p->flow) )
     {
-        uint32_t end_seq = tsn->c_seq + tsn->c_len;
-        if ( SEQ_EQ(end_seq, paf_position(&trs.paf_state)) )
-        {
-            total = end_seq - trs.sos.seglist_base_seq;
-            tsn = tsn->next;
-        }
-        else
-            total = tsn->c_seq - trs.sos.seglist.cur_rseg->c_seq;
-    }
-
-    ret_val = FINAL_FLUSH_OK;
-    while (tsn && *flags && SEQ_LT(tsn->c_seq, trs.tracker->r_win_base))
-    {
-        // only flush acked data that fits in pdu reassembly buffer...
-        uint32_t end = tsn->c_seq + tsn->c_len;
-        uint32_t flush_len;
-        int32_t flush_pt;
-
-        if ( SEQ_GT(end, trs.tracker->r_win_base))
-            flush_len = trs.tracker->r_win_base - tsn->c_seq;
-        else
-            flush_len = tsn->c_len;
-
-        if ( next_acked_no_gap_c(*tsn, trs) )
-            *flags |= PKT_MORE_TO_FLUSH;
-        else
-            *flags &= ~PKT_MORE_TO_FLUSH;
-
-        if ( has_seglist_hole(trs, *tsn, trs.paf_state, total, *flags) )
-        {
-            if (!paf_initialized(&trs.paf_state))
-                flush_pt = flush_len;
-            else
-                flush_pt = total;
-            
-            trs.paf_state.paf = StreamSplitter::SKIP;
-        } 
-        else
-        {
-            total += flush_len;
-            flush_pt = paf_check(splitter, &trs.paf_state, p, tsn->payload(),
-                flush_len, total, tsn->c_seq, flags);
-        }
-
-        // Get splitter from tracker as paf check may change it.
-        splitter = trs.tracker->get_splitter();
-        trs.sos.seglist.cur_sseg = tsn;
-
-        if ( flush_pt >= 0 )
-        {
-            trs.sos.seglist_base_seq = trs.sos.seglist.cur_rseg->c_seq;
-            return flush_pt;
-        }
-
-        if (flush_len < tsn->c_len || (splitter->is_paf() and !next_no_gap(*tsn)) ||
-            (trs.paf_state.paf == StreamSplitter::STOP))
+        flushed += flush_stream(p, packet_dir, false);
+        paf.paf_jump(flushed);
+        tcpStats.partial_flushes++;
+        tcpStats.partial_flush_bytes += flushed;
+        if ( seglist.seg_count )
         {
-            if ( !(next_no_gap(*tsn) || fin_acked_no_gap(*tsn, trs)) )
-                ret_val = FINAL_FLUSH_HOLD;
-            break;
+            purge_to_seq(seglist.head->start_seq() + flushed);
+            tracker.r_win_base = seglist.seglist_base_seq;
         }
-
-        tsn = tsn->next;
     }
-
-    return ret_val;
+    return flushed;
 }
 
 // we are on a FIN, the data has been scanned, it has no gaps,
 // but somehow we are waiting for more data - do final flush here
 // FIXIT-M this convoluted expression needs some refactoring to simplify
-bool TcpReassembler::final_flush_on_fin(const TcpReassemblerState &trs, int32_t flush_amt, Packet *p)
+bool TcpReassembler::final_flush_on_fin(int32_t flush_amt, Packet *p, FinSeqNumStatus fin_status)
 {
-    return trs.tracker->fin_seq_status >= TcpStreamTracker::FIN_WITH_SEQ_SEEN
+    return tracker.fin_seq_status >= fin_status
         && -1 <= flush_amt && flush_amt <= 0
-        && trs.paf_state.paf == StreamSplitter::SEARCH
+        && paf.state == StreamSplitter::SEARCH
         && !p->flow->searching_for_service();
 }
 
-int TcpReassembler::flush_on_data_policy(TcpReassemblerState& trs, Packet* p)
-{
-    uint32_t flushed = 0;
-    last_pdu = nullptr;
-
-    switch ( trs.tracker->get_flush_policy() )
-    {
-    case STREAM_FLPOLICY_IGNORE:
-        return 0;
-
-    case STREAM_FLPOLICY_ON_ACK:
-        break;
-
-    case STREAM_FLPOLICY_ON_DATA:
-        if ( trs.sos.seglist.head )
-        {
-            uint32_t flags;
-            int32_t flush_amt;
-            do
-            {
-                flags = trs.packet_dir;
-                flush_amt = scan_data_pre_ack(trs, &flags, p);
-                if ( flush_amt <= 0 )
-                    break;
-
-                flushed += flush_to_seq(trs, flush_amt, p, flags);
-            }  while ( trs.sos.seglist.head and !p->flow->is_inspection_disabled() );
-
-            if ( (trs.paf_state.paf == StreamSplitter::ABORT) && trs.tracker->is_splitter_paf() )
-            {
-                fallback(*trs.tracker, trs.server_side);
-                return flush_on_data_policy(trs, p);
-            }
-            else if ( final_flush_on_fin(trs, flush_amt, p) )
-                finish_and_final_flush(trs, p->flow, true, p);
-        }
-        break;
-    }
-
-    if ( !trs.sos.seglist.head )
-        return flushed;
-
-    if ( trs.tracker->is_retransmit_of_held_packet(p) )
-        flushed = perform_partial_flush(trs, p, flushed);
-
-    if ( flush_on_asymmetric_flow(trs, flushed, p) )
-    {
-            purge_to_seq(trs, trs.sos.seglist.head->i_seq + flushed);
-            trs.tracker->r_win_base = trs.sos.seglist_base_seq;
-            tcpStats.flush_on_asymmetric_flow++;
-    }
-
-    return flushed;
-}
-
-void TcpReassembler::skip_seglist_hole(TcpReassemblerState& trs, Packet* p, uint32_t flags,
-    int32_t flush_amt)
-{
-    if ( trs.tracker->is_splitter_paf() )
-    {
-        if ( flush_amt > 0 )
-            update_skipped_bytes(flush_amt, trs);
-        fallback(*trs.tracker, trs.server_side);
-    }
-    else
-    {
-        if ( flush_amt > 0 )
-            flush_to_seq(trs, flush_amt, p, flags);
-        trs.paf_state.paf = StreamSplitter::START;
-    }
-
-    if ( trs.sos.seglist.head )
-    {
-        if ( flush_amt > 0 )
-            purge_to_seq(trs, trs.sos.seglist_base_seq + flush_amt);
-        trs.sos.seglist_base_seq = trs.sos.seglist.head->c_seq;
-    }
-    else
-        trs.sos.seglist_base_seq = trs.tracker->r_win_base;
-
-    trs.tracker->rcv_nxt = trs.tracker->r_win_base;
-    trs.sos.seglist.cur_rseg = trs.sos.seglist.head;
-}
-
-int TcpReassembler::flush_on_ack_policy(TcpReassemblerState& trs, Packet* p)
-{
-    uint32_t flushed = 0;
-    last_pdu = nullptr;
-
-    switch ( trs.tracker->get_flush_policy() )
-    {
-    case STREAM_FLPOLICY_IGNORE:
-        return 0;
-
-    case STREAM_FLPOLICY_ON_ACK:
-    {
-        int32_t flush_amt;
-        uint32_t flags;
-
-        do
-        {
-            flags = trs.packet_dir;
-            flush_amt = scan_data_post_ack(trs, &flags, p);
-            if ( flush_amt <= 0 or trs.paf_state.paf == StreamSplitter::SKIP )
-                break;
-
-            // for consistency with other cases, should return total
-            // but that breaks flushing pipelined pdus
-            flushed += flush_to_seq(trs, flush_amt, p, flags);
-            assert( flushed );
-
-            // ideally we would purge just once after this loop but that throws off base
-            if ( trs.sos.seglist.head )
-                purge_to_seq(trs, trs.sos.seglist_base_seq);
-        }
-        while ( trs.sos.seglist.head and !p->flow->is_inspection_disabled() );
-
-        if ( (trs.paf_state.paf == StreamSplitter::ABORT) && trs.tracker->is_splitter_paf() )
-        {
-            fallback(*trs.tracker, trs.server_side);
-            return flush_on_ack_policy(trs, p);
-        }
-        else if ( trs.paf_state.paf == StreamSplitter::SKIP )
-        {
-            skip_seglist_hole(trs, p, flags, flush_amt);
-            return flush_on_ack_policy(trs, p);
-        }
-        else if ( -1 <= flush_amt and flush_amt <= 0 and
-            trs.paf_state.paf == StreamSplitter::SEARCH and
-            trs.tracker->fin_seq_status == TcpStreamTracker::FIN_WITH_SEQ_ACKED and
-            !p->flow->searching_for_service() )
-        {
-            // we are acknowledging a FIN, the data has been scanned, it has no gaps,
-            // but somehow we are waiting for more data - do final flush here
-            finish_and_final_flush(trs, p->flow, true, p);
-        }
-    }
-    break;
-
-    case STREAM_FLPOLICY_ON_DATA:
-        purge_flushed_ackd(trs);
-        break;
-    }
-
-    return flushed;
-}
-
-void TcpReassembler::purge_segment_list(TcpReassemblerState& trs)
-{
-    trs.sos.seglist.reset();
-    trs.sos.seg_count = 0;
-    trs.sos.seg_bytes_total = 0;
-    trs.sos.seg_bytes_logical = 0;
-    trs.flush_count = 0;
-}
-
-void TcpReassembler::insert_segment_in_empty_seglist(
-    TcpReassemblerState& trs, TcpSegmentDescriptor& tsd)
-{
-    uint32_t overlap = 0;
-
-    if ( SEQ_GT(trs.sos.seglist_base_seq, tsd.get_seq()) )
-    {
-        overlap = trs.sos.seglist_base_seq - tsd.get_seq();
-        if ( overlap >= tsd.get_len() )
-            return;
-    }
-
-    add_reassembly_segment(
-        trs, tsd, tsd.get_len(), overlap, 0, tsd.get_seq() + overlap, nullptr);
-}
-
-void TcpReassembler::init_overlap_editor(
-    TcpReassemblerState& trs, TcpSegmentDescriptor& tsd)
+bool  TcpReassembler::flush_on_asymmetric_flow(uint32_t flushed, snort::Packet *p)
 {
-    TcpSegmentNode* left = nullptr, *right = nullptr, *tsn = nullptr;
-    int32_t dist_head = 0, dist_tail = 0;
-
-    if ( trs.sos.seglist.head && trs.sos.seglist.tail )
-    {
-        if ( SEQ_GT(tsd.get_seq(), trs.sos.seglist.head->i_seq) )
-            dist_head = tsd.get_seq() - trs.sos.seglist.head->i_seq;
-        else
-            dist_head = trs.sos.seglist.head->i_seq - tsd.get_seq();
-
-        if ( SEQ_GT(tsd.get_seq(), trs.sos.seglist.tail->i_seq) )
-            dist_tail = tsd.get_seq() - trs.sos.seglist.tail->i_seq;
-        else
-            dist_tail = trs.sos.seglist.tail->i_seq - tsd.get_seq();
-    }
-
-    if ( SEQ_LEQ(dist_head, dist_tail) )
-    {
-        for ( tsn = trs.sos.seglist.head; tsn; tsn = tsn->next )
-        {
-            right = tsn;
-            if ( SEQ_GEQ(right->i_seq, tsd.get_seq() ) )
-                break;
-
-            left = right;
-        }
-
-        if ( tsn == nullptr )
-            right = nullptr;
-    }
-    else
-    {
-        for ( tsn = trs.sos.seglist.tail; tsn; tsn = tsn->prev )
-        {
-            left = tsn;
-            if ( SEQ_LT(left->i_seq, tsd.get_seq() ) )
-                break;
-
-            right = left;
-        }
-
-        if (tsn == nullptr)
-            left = nullptr;
-    }
-
-    trs.sos.init_soe(tsd, left, right);
-}
-
-void TcpReassembler::insert_segment_in_seglist(
-    TcpReassemblerState& trs, TcpSegmentDescriptor& tsd)
-{
-    // NORM fast tracks are in sequence - no norms
-    if ( trs.sos.seglist.tail && is_segment_fasttrack(trs, trs.sos.seglist.tail, tsd) )
-    {
-        /* segment fit cleanly at the end of the segment list */
-        add_reassembly_segment(
-            trs, tsd, tsd.get_len(), 0, 0, tsd.get_seq(), trs.sos.seglist.tail);
-        return;
-    }
-
-    init_overlap_editor(trs, tsd);
-    eval_left(trs);
-    eval_right(trs);
-
-    if ( trs.sos.keep_segment )
-    {
-        if ( !trs.sos.left and trs.sos.right and
-            paf_initialized(&trs.paf_state) and SEQ_GT(trs.paf_state.pos, tsd.get_seq()) )
-        {
-            return;
-        }
-
-        // slide is current seq number - initial seq number unless all data
-        // truncated from right, then slide is 0
-        if ( trs.sos.len - trs.sos.trunc_len > 0 )
-            trs.sos.slide = trs.sos.seq - tsd.get_seq();
-        else
-            trs.sos.slide = 0;
-
-        add_reassembly_segment(
-            trs, tsd, trs.sos.len, trs.sos.slide, trs.sos.trunc_len, trs.sos.seq, trs.sos.left);
-    }
-}
-
-void TcpReassembler::queue_packet_for_reassembly(
-    TcpReassemblerState& trs, TcpSegmentDescriptor& tsd)
-{
-    if ( trs.sos.seg_count == 0 )
-    {
-        insert_segment_in_empty_seglist(trs, tsd);
-        return;
-    }
-
-    if ( SEQ_GT(trs.tracker->r_win_base, tsd.get_seq() ) )
+    bool asymmetric = flushed && seglist.seg_count && !p->flow->two_way_traffic() && !p->ptrs.tcph->is_syn();
+    if ( asymmetric )
     {
-        const int32_t offset = trs.tracker->r_win_base - tsd.get_seq();
-
-        if ( offset < tsd.get_len() )
-        {
-            tsd.slide_segment_in_rcv_window(offset);
-            insert_segment_in_seglist(trs, tsd);
-            tsd.slide_segment_in_rcv_window(-offset);
-        }
+        TcpStreamTracker::TcpState peer = tracker.session->get_peer_state(&tracker);
+        asymmetric = ( peer == TcpStreamTracker::TCP_SYN_SENT || peer == TcpStreamTracker::TCP_SYN_RECV
+            || peer == TcpStreamTracker::TCP_MID_STREAM_SENT );
     }
-    else
-        insert_segment_in_seglist(trs, tsd);
-}
 
-uint32_t TcpReassembler::perform_partial_flush(TcpReassemblerState& trs, Flow* flow, Packet*& p)
-{
-    p = get_packet(flow, trs.packet_dir, trs.server_side);
-    return perform_partial_flush(trs, p);
-}
-
-// No error checking here, so the caller must ensure that p, p->flow and context
-// are not null.
-uint32_t TcpReassembler::perform_partial_flush(TcpReassemblerState& trs, Packet* p, uint32_t flushed)
-{
-    if ( trs.tracker->get_splitter()->init_partial_flush(p->flow) )
-    {
-        flushed += flush_stream(trs, p, trs.packet_dir, false);
-        paf_jump(&trs.paf_state, flushed);
-        tcpStats.partial_flushes++;
-        tcpStats.partial_flush_bytes += flushed;
-        if ( trs.sos.seg_count )
-        {
-            purge_to_seq(trs, trs.sos.seglist.head->i_seq + flushed);
-            trs.tracker->r_win_base = trs.sos.seglist_base_seq;
-        }
-    }
-    return flushed;
+    return asymmetric;
 }
index c96d9d0bc1502ec470f1a1c009ab2c6de9bbc192..c7c03a96081bcf50795969384706269021a3f243 100644 (file)
 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 //--------------------------------------------------------------------------
 
-// tcp_reassembly.h author davis mcpherson <davmcphe@cisco.com>
+// tcp_reassembler.h author davis mcpherson <davmcphe@cisco.com>
 // Created on: Jul 31, 2015
 
 #ifndef TCP_REASSEMBLER_H
 #define TCP_REASSEMBLER_H
 
+
+#include <cstdint>
+
+#include "flow/flow.h"
+#include "protocols/packet.h"
+#include "stream/pafng.h"
 #include "stream/stream.h"
 
-#include "segment_overlap_editor.h"
+#include "tcp_reassembly_segments.h"
 
-class TcpReassembler : public SegmentOverlapEditor
+namespace snort
+{
+    class StreamSplitter;
+}
+
+class TcpSegmentDescriptor;
+class TcpSegmentNode;
+class TcpStreamTracker;
+enum FinSeqNumStatus : uint8_t;
+
+class TcpReassembler
 {
 public:
 
@@ -36,95 +52,113 @@ public:
         FINAL_FLUSH_OK = -1
     };
 
-    virtual void queue_packet_for_reassembly(TcpReassemblerState&, TcpSegmentDescriptor&);
-    virtual void purge_segment_list(TcpReassemblerState&);
-    virtual void purge_flushed_ackd(TcpReassemblerState&);
-    virtual int flush_stream(
-        TcpReassemblerState&, snort::Packet* p, uint32_t dir, bool final_flush = false);
-    virtual void flush_queued_segments(
-        TcpReassemblerState&, snort::Flow* flow, bool clear, const snort::Packet* = nullptr);
-    void finish_and_final_flush(
-        TcpReassemblerState&, snort::Flow* flow, bool clear, snort::Packet*);
-    virtual bool is_segment_pending_flush(const TcpReassemblerState&) const;
-    virtual int flush_on_data_policy(TcpReassemblerState&, snort::Packet*);
-    virtual int flush_on_ack_policy(TcpReassemblerState&, snort::Packet*);
-    virtual bool add_alert(TcpReassemblerState&, uint32_t gid, uint32_t sid);
-    virtual bool check_alerted(TcpReassemblerState&, uint32_t gid, uint32_t sid);
-    virtual int update_alert(TcpReassemblerState&, uint32_t gid, uint32_t sid,
-        uint32_t event_id, uint32_t event_second);
-    virtual void purge_alerts(TcpReassemblerState&);
-    virtual bool segment_within_seglist_window(TcpReassemblerState&, TcpSegmentDescriptor&);
-    void reset_asymmetric_flow_reassembly(TcpReassemblerState&);
-    void skip_midstream_pickup_seglist_hole(TcpReassemblerState&, TcpSegmentDescriptor&);
-    void initialize_paf(TcpReassemblerState& trs)
+    TcpReassembler(TcpStreamTracker& trk, TcpReassemblySegments& seglist)
+        : tracker(trk), seglist(seglist)
+    { }
+
+    virtual ~TcpReassembler()
+    { }
+
+    virtual void init(bool server, snort::StreamSplitter* ss);
+
+    virtual int eval_flush_policy_on_ack(snort::Packet*) = 0;
+    virtual int eval_flush_policy_on_data(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*);
+    uint32_t perform_partial_flush(snort::Flow*, snort::Packet*&);
+    void purge_flushed_ackd();
+
+    void release_splitter()
+    { splitter = nullptr; }
+
+    snort::StreamSplitter* get_splitter()
+     { return splitter; }
+
+    bool is_splitter_paf() const
+    { return splitter && splitter->is_paf(); }
+
+    bool segment_already_scanned(uint32_t seq)
+    {
+        if ( paf.paf_initialized() and SEQ_GT(paf.pos, seq) )
+            return true;
+        else
+            return false;
+    }
+
+    void initialize_paf()
     {
         // only initialize if we have a data segment queued
-        if ( !trs.sos.seglist.head )
+        if ( !seglist.head )
             return;
 
-        if ( !paf_initialized(&trs.paf_state) or SEQ_GT(trs.paf_state.seq, trs.sos.seglist.head->i_seq) )
-            paf_initialize(&trs.paf_state, trs.sos.seglist.head->i_seq);
+       if ( !paf.paf_initialized() or !SEQ_EQ(paf.seq_num, seglist.head->start_seq()) )
+            paf.paf_initialize(seglist.head->start_seq());
     }
 
-    uint32_t perform_partial_flush(TcpReassemblerState&, snort::Flow*, snort::Packet*&);
+    void reset_paf()
+    { paf.paf_reset(); }
+
+    void clear_paf()
+    { paf.paf_clear(); }
+
+    virtual FlushPolicy get_flush_policy() const = 0;
 
 protected:
-    TcpReassembler() = default;
-
-    void add_reassembly_segment(
-        TcpReassemblerState&, TcpSegmentDescriptor&, uint16_t len, uint32_t slide,
-        uint32_t trunc, uint32_t seq, TcpSegmentNode* left) override;
-
-    void dup_reassembly_segment(
-        TcpReassemblerState&, TcpSegmentNode* left, TcpSegmentNode** retSeg) override;
-    int delete_reassembly_segment(TcpReassemblerState&, TcpSegmentNode*) override;
-    virtual void insert_segment_in_empty_seglist(TcpReassemblerState&, TcpSegmentDescriptor&);
-    virtual void insert_segment_in_seglist(TcpReassemblerState&, TcpSegmentDescriptor&);
-    virtual uint32_t get_pending_segment_count(const TcpReassemblerState&, unsigned max) const;
-    int trim_delete_reassembly_segment(TcpReassemblerState&, TcpSegmentNode*, uint32_t flush_seq);
-    void queue_reassembly_segment(TcpReassemblerState&, TcpSegmentNode* prev, TcpSegmentNode*);
-    void init_overlap_editor(TcpReassemblerState&, TcpSegmentDescriptor&);
-    bool is_segment_fasttrack
-        (TcpReassemblerState&, TcpSegmentNode* tail, const TcpSegmentDescriptor&);
-    void show_rebuilt_packet(const TcpReassemblerState&, snort::Packet*);
-    int flush_data_segments(TcpReassemblerState&, uint32_t flush_len, snort::Packet* pdu);
-    void prep_pdu(
-        TcpReassemblerState&, snort::Flow*, snort::Packet*, uint32_t pkt_flags, snort::Packet*);
-    snort::Packet* initialize_pdu(
-        TcpReassemblerState&, snort::Packet*, uint32_t pkt_flags, struct timeval);
-    int flush_to_seq(TcpReassemblerState&, uint32_t bytes, snort::Packet*, uint32_t pkt_flags);
-    int do_zero_byte_flush(TcpReassemblerState&, snort::Packet*, uint32_t pkt_flags);
-    uint32_t get_q_footprint(TcpReassemblerState&);
-    uint32_t get_q_sequenced(TcpReassemblerState&);
-    bool is_q_sequenced(TcpReassemblerState&);
-    void final_flush(TcpReassemblerState&, snort::Packet*, uint32_t dir);
-    uint32_t get_reverse_packet_dir(TcpReassemblerState&, const snort::Packet*);
-    uint32_t get_forward_packet_dir(TcpReassemblerState&, const snort::Packet*);
-    int32_t scan_data_pre_ack(TcpReassemblerState&, uint32_t*, snort::Packet*);
-    void fallback(TcpStreamTracker&, bool server_side);
-    int32_t scan_data_post_ack(TcpReassemblerState&, uint32_t* flags, snort::Packet*);
-    void purge_to_seq(TcpReassemblerState&, uint32_t flush_seq);
-    void purge_segments_left_of_hole(TcpReassemblerState&, const TcpSegmentNode*);
-
-    bool next_no_gap(const TcpSegmentNode&);
-    bool next_no_gap_c(const TcpSegmentNode&);
-    bool next_acked_no_gap_c(const TcpSegmentNode&, const TcpReassemblerState&);
-    bool fin_no_gap(const TcpSegmentNode&, const TcpReassemblerState&);
-    bool fin_acked_no_gap(const TcpSegmentNode&, const TcpReassemblerState&);
-    void update_next(TcpReassemblerState&, const TcpSegmentNode&);
-    void update_skipped_bytes(uint32_t, TcpReassemblerState&);
-    void check_first_segment_hole(TcpReassemblerState&);
-    void update_rcv_nxt(TcpReassemblerState&, TcpSegmentNode&);
-    bool has_seglist_hole(TcpReassemblerState&, TcpSegmentNode&, PAF_State&, uint32_t& total,
-        uint32_t& flags);
-    void skip_seglist_hole(TcpReassemblerState&, snort::Packet*, uint32_t flags,
-        int32_t flush_amt);
-
-    uint32_t perform_partial_flush(TcpReassemblerState&, snort::Packet*, uint32_t flushed = 0);
-
-private:
-    bool final_flush_on_fin(const TcpReassemblerState&, int32_t flush_amt, snort::Packet*);
-    bool flush_on_asymmetric_flow(const TcpReassemblerState &trs, uint32_t flushed, snort::Packet *p);
+    void show_rebuilt_packet(snort::Packet*);
+    int flush_data_segments(uint32_t flush_len, snort::Packet* pdu);
+    void prep_pdu(snort::Flow*, snort::Packet*, uint32_t pkt_flags, snort::Packet*);
+    snort::Packet* initialize_pdu(snort::Packet*, uint32_t pkt_flags, struct timeval);
+    int flush_to_seq(uint32_t bytes, snort::Packet*, uint32_t pkt_flags);
+    int do_zero_byte_flush(snort::Packet*, uint32_t pkt_flags);
+    uint32_t get_q_footprint();
+    uint32_t get_q_sequenced();
+    bool is_q_sequenced();
+    void final_flush(snort::Packet*, uint32_t dir);
+    bool splitter_finish(snort::Flow* flow);
+    void purge_to_seq(uint32_t flush_seq);
+
+    bool fin_no_gap(const TcpSegmentNode&);
+    bool fin_acked_no_gap(const TcpSegmentNode&);
+    void update_skipped_bytes(uint32_t);
+    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);
+
+    ProtocolAwareFlusher paf;
+    TcpStreamTracker& tracker;
+    TcpReassemblySegments& seglist;
+    snort::StreamSplitter* splitter = nullptr;
+
+    snort::Packet* last_pdu = nullptr;
+    uint8_t ignore_dir = 0;
+    uint8_t packet_dir = 0;
+    bool server_side = true;
+    bool splitter_finish_flag = false;
+};
+
+class TcpReassemblerIgnore : public TcpReassembler
+{
+public:
+    TcpReassemblerIgnore(TcpStreamTracker& trk, TcpReassemblySegments& sl)
+        : TcpReassembler(trk, sl)
+    { }
+
+    void init(bool, snort::StreamSplitter*) override
+    { }
+
+    int eval_flush_policy_on_ack(snort::Packet*) override
+    { return 0; }
+
+    int eval_flush_policy_on_data(snort::Packet*) override
+    { return 0; }
+
+    int flush_stream(snort::Packet*, uint32_t, bool) override
+    { return 0; }
+
+    FlushPolicy get_flush_policy() const override
+    { return STREAM_FLPOLICY_IGNORE; }
 };
 
 #endif
diff --git a/src/stream/tcp/tcp_reassembler_ids.cc b/src/stream/tcp/tcp_reassembler_ids.cc
new file mode 100644 (file)
index 0000000..43d7c23
--- /dev/null
@@ -0,0 +1,259 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2024-2024 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// tcp_reassembler_ids.cc author davis mcpherson <davmcphe@cisco.com>
+// Created on: Jul 31, 2015
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "tcp_reassembler_ids.h"
+
+#include <cassert>
+
+#include "detection/detection_engine.h"
+#include "log/log.h"
+#include "main/analyzer.h"
+#include "packet_io/active.h"
+#include "profiler/profiler.h"
+#include "protocols/packet_manager.h"
+#include "time/packet_time.h"
+
+#include "tcp_module.h"
+#include "tcp_normalizers.h"
+#include "tcp_segment_node.h"
+#include "tcp_session.h"
+#include "tcp_stream_tracker.h"
+
+using namespace snort;
+
+bool TcpReassemblerIds::has_seglist_hole(TcpSegmentNode& tsn, uint32_t& total, uint32_t& flags)
+{
+    if ( !tsn.prev or SEQ_GEQ(tsn.prev->scan_seq() + tsn.prev->unscanned(), tsn.scan_seq())
+       or SEQ_GEQ(tsn.scan_seq(), tracker.r_win_base) )
+    {
+       check_first_segment_hole();
+       return false;
+    }
+
+    // safety - prevent seq + total < seq
+    if ( total > 0x7FFFFFFF )
+        total = 0x7FFFFFFF;
+
+    if ( !paf.tot )
+        flags |= PKT_PDU_HEAD;
+
+    paf.state = StreamSplitter::SKIP;
+    return true;
+}
+
+void TcpReassemblerIds::skip_seglist_hole(Packet* p, uint32_t flags, int32_t flush_amt)
+{
+    if ( is_splitter_paf() )
+    {
+        if ( flush_amt > 0 )
+            update_skipped_bytes(flush_amt);
+        tracker.fallback();
+    }
+    else
+    {
+        if ( flush_amt > 0 )
+            flush_to_seq(flush_amt, p, flags);
+        paf.state = StreamSplitter::START;
+    }
+
+    if ( seglist.head )
+    {
+        if ( flush_amt > 0 )
+            purge_to_seq(seglist.seglist_base_seq + flush_amt);
+        seglist.seglist_base_seq = seglist.head->scan_seq();
+    }
+    else
+        seglist.seglist_base_seq = tracker.r_win_base;  // FIXIT-H - do we need to set rcv_nxt here?
+
+    seglist.cur_rseg = seglist.head;
+    tracker.set_order(TcpStreamTracker::OUT_OF_SEQUENCE);
+}
+
+// iterate over seglist and scan all new acked bytes
+// - new means not yet scanned
+// - must use seglist data (not packet) since this packet may plug a
+//   hole and enable paf scanning of following segments
+// - if we reach a flush point
+//   - return bytes to flush if data available (must be acked)
+//   - return zero if not yet received or received but not acked
+// - if we reach a skip point
+//   - jump ahead and resume scanning any available data
+// - must stop if we reach a gap
+// - one segment may lead to multiple checks since
+//   it may contain multiple encapsulated PDUs
+// - if we partially scan a segment we must save state so we
+//   know where we left off and can resume scanning the remainder
+int32_t TcpReassemblerIds::scan_data_post_ack(uint32_t* flags, Packet* p)
+{
+    assert(seglist.session->flow == p->flow);
+
+    int32_t ret_val = FINAL_FLUSH_HOLD;
+
+    if ( !seglist.cur_sseg || SEQ_GEQ(seglist.seglist_base_seq, tracker.r_win_base) )
+        return ret_val ;
+
+    if ( !seglist.cur_rseg )
+        seglist.cur_rseg = seglist.cur_sseg;
+
+    uint32_t total = 0;
+    TcpSegmentNode* tsn = seglist.cur_sseg;
+    if ( paf.paf_initialized() )
+    {
+        uint32_t end_seq = tsn->scan_seq() + tsn->unscanned();
+        if ( SEQ_EQ(end_seq, paf.paf_position()) )
+        {
+            total = end_seq - seglist.seglist_base_seq;
+            tsn = tsn->next;
+        }
+        else
+            total = tsn->scan_seq() - seglist.cur_rseg->scan_seq();
+    }
+
+    ret_val = FINAL_FLUSH_OK;
+    while (tsn && *flags && SEQ_LT(tsn->scan_seq(), tracker.r_win_base))
+    {
+        // only flush acked data that fits in pdu reassembly buffer...
+        uint32_t end = tsn->scan_seq() + tsn->unscanned();
+        uint32_t flush_len;
+        int32_t flush_pt;
+
+        if ( SEQ_GT(end, tracker.r_win_base))
+            flush_len = tracker.r_win_base - tsn->scan_seq();
+        else
+            flush_len = tsn->unscanned();
+
+        if ( tsn->next_acked_no_gap(tracker.r_win_base) )
+            *flags |= PKT_MORE_TO_FLUSH;
+        else
+            *flags &= ~PKT_MORE_TO_FLUSH;
+
+        if ( has_seglist_hole(*tsn, total, *flags) )
+            flush_pt = total;
+        else
+        {
+            total += flush_len;
+            flush_pt = paf.paf_check(p, tsn->paf_data(), flush_len, total, tsn->scan_seq(), flags);
+        }
+
+        // Get splitter from tracker as paf check may change it.
+        seglist.cur_sseg = tsn;
+
+        if ( flush_pt >= 0 )
+        {
+            seglist.seglist_base_seq = seglist.cur_rseg->scan_seq();
+            return flush_pt;
+        }
+
+        if (flush_len < tsn->unscanned() || (splitter->is_paf() and !tsn->next_no_gap()) ||
+            (paf.state == StreamSplitter::STOP))
+        {
+            if ( !(tsn->next_no_gap() || fin_acked_no_gap(*tsn)) )
+                ret_val = FINAL_FLUSH_HOLD;
+            break;
+        }
+
+        tsn = tsn->next;
+    }
+
+    return ret_val;
+}
+
+int TcpReassemblerIds::eval_flush_policy_on_ack(Packet* p)
+{
+    uint32_t flushed = 0;
+    int32_t flush_amt;
+    uint32_t flags;
+
+    last_pdu = nullptr;
+
+    do
+    {
+        flags = packet_dir;
+        flush_amt = scan_data_post_ack(&flags, p);
+        if ( flush_amt <= 0 or paf.state == StreamSplitter::SKIP )
+            break;
+
+        // for consistency with other cases, should return total
+        // but that breaks flushing pipelined pdus
+        flushed += flush_to_seq(flush_amt, p, flags);
+        assert( flushed );
+
+        // ideally we would purge just once after this loop but that throws off base
+        if ( seglist.head )
+            purge_to_seq(seglist.seglist_base_seq);
+    } while ( seglist.head and !p->flow->is_inspection_disabled() );
+
+    if ( (paf.state == StreamSplitter::ABORT) && is_splitter_paf() )
+    {
+        tracker.fallback();
+        return eval_flush_policy_on_ack(p);
+    }
+    else if ( paf.state == StreamSplitter::SKIP )
+    {
+        skip_seglist_hole(p, flags, flush_amt);
+        return eval_flush_policy_on_ack(p);
+    }
+    else if ( final_flush_on_fin(flush_amt, p, FIN_WITH_SEQ_ACKED) )
+        finish_and_final_flush(p->flow, true, p);
+
+    return flushed;
+}
+
+int TcpReassemblerIds::eval_flush_policy_on_data(Packet* p)
+{
+    uint32_t flushed = 0;
+
+    if ( !seglist.head )
+        return flushed;
+
+    if ( tracker.is_retransmit_of_held_packet(p) )
+        flushed = perform_partial_flush(p, flushed);
+
+    if ( flush_on_asymmetric_flow(flushed, p) )
+    {
+        purge_to_seq(seglist.head->start_seq() + flushed);
+        tracker.r_win_base = seglist.seglist_base_seq;
+        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);
+    }
+
+    if ( final_flush )
+        return do_zero_byte_flush(p, dir);
+
+    return 0;
+}
+
diff --git a/src/stream/tcp/tcp_reassembler_ids.h b/src/stream/tcp/tcp_reassembler_ids.h
new file mode 100644 (file)
index 0000000..8d8b643
--- /dev/null
@@ -0,0 +1,61 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2024-2024 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// tcp_reassembler_ids.h author davis mcpherson <davmcphe@cisco.com>
+// Created on: Jul 31, 2015
+
+#ifndef TCP_REASSEMBLER_IDS_H
+#define TCP_REASSEMBLER_IDS_H
+
+#include <cstdint>
+
+#include "protocols/packet.h"
+#include "stream/stream.h"
+
+#include "tcp_reassembler.h"
+#include "tcp_reassembly_segments.h"
+
+class TcpSegmentDescriptor;
+class TcpSegmentNode;
+
+class TcpReassemblerIds : public TcpReassembler
+{
+public:
+
+
+    TcpReassemblerIds(TcpStreamTracker& trk, TcpReassemblySegments& sl)
+        : TcpReassembler(trk, sl)
+    { }
+
+    ~TcpReassemblerIds() override
+    { }
+
+    int eval_flush_policy_on_ack(snort::Packet*) override;
+    int eval_flush_policy_on_data(snort::Packet*) override;
+    int flush_stream(snort::Packet*, uint32_t dir, bool final_flush = false) override;
+
+    FlushPolicy get_flush_policy() const override
+    { return STREAM_FLPOLICY_ON_ACK; }
+
+private:
+    int32_t scan_data_post_ack(uint32_t* flags, snort::Packet*);
+    bool has_seglist_hole(TcpSegmentNode&, uint32_t& total, uint32_t& flags);
+    void skip_seglist_hole(snort::Packet*, uint32_t flags, int32_t flush_amt);
+};
+
+#endif
diff --git a/src/stream/tcp/tcp_reassembler_ips.cc b/src/stream/tcp/tcp_reassembler_ips.cc
new file mode 100644 (file)
index 0000000..3b474ba
--- /dev/null
@@ -0,0 +1,181 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2024-2024 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// tcp_reassembler_ips.cc author davis mcpherson <davmcphe@cisco.com>
+// Created on: Jul 31, 2015
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "tcp_reassembler_ips.h"
+
+#include <cassert>
+
+#include "detection/detection_engine.h"
+#include "log/log.h"
+#include "main/analyzer.h"
+#include "packet_io/active.h"
+#include "profiler/profiler.h"
+#include "protocols/packet_manager.h"
+#include "time/packet_time.h"
+
+#include "tcp_module.h"
+#include "tcp_normalizers.h"
+#include "tcp_segment_node.h"
+#include "tcp_session.h"
+#include "tcp_stream_tracker.h"
+
+using namespace snort;
+
+// see scan_data_post_ack() for details
+// the key difference is that we operate on forward moving data
+// because we don't wait until it is acknowledged
+int32_t TcpReassemblerIps::scan_data_pre_ack(uint32_t* flags, Packet* p)
+{
+    assert(seglist.session->flow == p->flow);
+
+    int32_t ret_val = FINAL_FLUSH_HOLD;
+
+    if ( SEQ_GT(seglist.head->scan_seq(), seglist.seglist_base_seq) )
+        return ret_val;
+
+    if ( !seglist.cur_rseg )
+        seglist.cur_rseg = seglist.cur_sseg;
+
+    if ( !is_q_sequenced() )
+        return ret_val;
+
+    TcpSegmentNode* tsn = seglist.cur_sseg;
+    uint32_t total = tsn->scan_seq() - seglist.seglist_base_seq;
+
+    ret_val = FINAL_FLUSH_OK;
+    while ( tsn && *flags )
+    {
+        total += tsn->unscanned();
+
+        uint32_t end = tsn->scan_seq() + tsn->unscanned();
+        uint32_t pos = paf.paf_position();
+
+        if ( paf.paf_initialized() && SEQ_LEQ(end, pos) )
+        {
+            if ( !tsn->next_no_gap() )
+            {
+                ret_val = FINAL_FLUSH_HOLD;
+                break;
+            }
+
+            tsn = tsn->next;
+            continue;
+        }
+
+        if ( tsn->next_no_gap() )
+            *flags |= PKT_MORE_TO_FLUSH;
+        else
+            *flags &= ~PKT_MORE_TO_FLUSH;
+        int32_t flush_pt = paf.paf_check(p, tsn->paf_data(), tsn->unscanned(),
+            total, tsn->scan_seq(), flags);
+
+        if (flush_pt >= 0)
+        {
+            seglist.cur_sseg = tsn;
+            return flush_pt;
+        }
+
+        if (!tsn->next_no_gap() || (paf.state == StreamSplitter::STOP))
+        {
+            if ( !(tsn->next_no_gap() || fin_no_gap(*tsn)) )
+                ret_val = FINAL_FLUSH_HOLD;
+            break;
+        }
+
+        tsn = tsn->next;
+    }
+
+    seglist.cur_sseg = tsn;
+    
+    return ret_val;
+}
+
+int TcpReassemblerIps::eval_flush_policy_on_ack(Packet*)
+{
+    purge_flushed_ackd();
+
+    return 0;
+}
+
+int TcpReassemblerIps::eval_flush_policy_on_data(Packet* p)
+{
+    uint32_t flushed = 0;
+    last_pdu = nullptr;
+
+    if ( seglist.head )
+    {
+        uint32_t flags;
+        int32_t flush_amt;
+        do
+        {
+            flags = packet_dir;
+            flush_amt = scan_data_pre_ack(&flags, p);
+            if ( flush_amt <= 0 )
+                break;
+
+            flushed += flush_to_seq(flush_amt, p, flags);
+        } while ( seglist.head and !p->flow->is_inspection_disabled() );
+
+        if ( (paf.state == StreamSplitter::ABORT) && is_splitter_paf() )
+        {
+            tracker.fallback();
+            return eval_flush_policy_on_data(p);
+        }
+        else if ( final_flush_on_fin(flush_amt, p, FIN_WITH_SEQ_SEEN) )
+            finish_and_final_flush(p->flow, true, p);
+    }
+
+    if ( !seglist.head )
+        return flushed;
+
+    if ( tracker.is_retransmit_of_held_packet(p) )
+        flushed = perform_partial_flush(p, flushed);
+
+    if ( flush_on_asymmetric_flow(flushed, p) )
+    {
+        purge_to_seq(seglist.head->start_seq() + flushed);
+        tracker.r_win_base = seglist.seglist_base_seq;
+        tcpStats.flush_on_asymmetric_flow++;
+    }
+
+    return flushed;
+}
+
+int TcpReassemblerIps::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_sequenced();  // num bytes in pre-ack mode
+        if ( bytes )
+            return flush_to_seq(bytes, p, dir);
+    }
+
+    if ( final_flush )
+        return do_zero_byte_flush(p, dir);
+
+    return 0;
+}
+
diff --git a/src/stream/tcp/tcp_reassembler_ips.h b/src/stream/tcp/tcp_reassembler_ips.h
new file mode 100644 (file)
index 0000000..bf21acf
--- /dev/null
@@ -0,0 +1,58 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2024-2024 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// tcp_reassembler_ips.h author davis mcpherson <davmcphe@cisco.com>
+// Created on: Jul 31, 2015
+
+#ifndef TCP_REASSEMBLER_IPS_H
+#define TCP_REASSEMBLER_IPS_H
+
+#include <cstdint>
+
+#include "flow/flow.h"
+#include "protocols/packet.h"
+#include "stream/stream.h"
+
+#include "tcp_reassembler.h"
+#include "tcp_reassembly_segments.h"
+
+class TcpSegmentDescriptor;
+class TcpSegmentNode;
+
+class TcpReassemblerIps : public TcpReassembler
+{
+public:
+    TcpReassemblerIps(TcpStreamTracker& trk, TcpReassemblySegments& sl)
+        : TcpReassembler(trk, sl)
+    { }
+
+    ~TcpReassemblerIps() override
+    { }
+
+    int eval_flush_policy_on_ack(snort::Packet*) override;
+    int eval_flush_policy_on_data(snort::Packet*) override;
+    int flush_stream(snort::Packet*, uint32_t dir, bool final_flush = false) override;
+
+    FlushPolicy get_flush_policy() const override
+    { return STREAM_FLPOLICY_ON_DATA; }
+
+private:
+    int32_t scan_data_pre_ack(uint32_t*, snort::Packet*);
+};
+
+#endif
diff --git a/src/stream/tcp/tcp_reassemblers.cc b/src/stream/tcp/tcp_reassemblers.cc
deleted file mode 100644 (file)
index cf72eca..0000000
+++ /dev/null
@@ -1,330 +0,0 @@
-//--------------------------------------------------------------------------
-// Copyright (C) 2015-2024 Cisco and/or its affiliates. All rights reserved.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License Version 2 as published
-// by the Free Software Foundation.  You may not use, modify or distribute
-// this program under any other version of the GNU General Public License.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License along
-// with this program; if not, write to the Free Software Foundation, Inc.,
-// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-//--------------------------------------------------------------------------
-
-// tcp_reassemblers.cc author davis mcpherson <davmcphe@cisco.com>
-// Created on: Oct 9, 2015
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "tcp_reassemblers.h"
-
-#include "tcp_defs.h"
-#include "tcp_stream_tracker.h"
-
-class TcpReassemblerFirst : public TcpReassembler
-{
-public:
-    TcpReassemblerFirst() = default;
-
-private:
-    void insert_left_overlap(TcpReassemblerState& trs) override
-    { left_overlap_keep_first(trs); }
-
-    void insert_right_overlap(TcpReassemblerState& trs) override
-    { right_overlap_truncate_new(trs); }
-
-    void insert_full_overlap(TcpReassemblerState& trs) override
-    { full_right_overlap_os5(trs); }
-};
-
-class TcpReassemblerLast : public TcpReassembler
-{
-public:
-    TcpReassemblerLast() = default;
-
-private:
-    void insert_left_overlap(TcpReassemblerState& trs) override
-    { left_overlap_keep_last(trs); }
-
-    void insert_right_overlap(TcpReassemblerState& trs) override
-    { right_overlap_truncate_existing(trs); }
-
-    void insert_full_overlap(TcpReassemblerState& trs) override
-    { full_right_overlap_os4(trs); }
-};
-
-class TcpReassemblerLinux : public TcpReassembler
-{
-public:
-    TcpReassemblerLinux() = default;
-
-private:
-    void insert_left_overlap(TcpReassemblerState& trs) override
-    { left_overlap_keep_first(trs); }
-
-    void insert_right_overlap(TcpReassemblerState& trs) override
-    { right_overlap_truncate_existing(trs); }
-
-    void insert_full_overlap(TcpReassemblerState& trs) override
-    { full_right_overlap_os2(trs); }
-};
-
-class TcpReassemblerOldLinux : public TcpReassembler
-{
-public:
-    TcpReassemblerOldLinux() = default;
-
-private:
-    void insert_left_overlap(TcpReassemblerState& trs) override
-    { left_overlap_keep_first(trs); }
-
-    void insert_right_overlap(TcpReassemblerState& trs) override
-    { right_overlap_truncate_existing(trs); }
-
-    void insert_full_overlap(TcpReassemblerState& trs) override
-    { full_right_overlap_os4(trs); }
-};
-
-class TcpReassemblerBSD : public TcpReassembler
-{
-public:
-    TcpReassemblerBSD() = default;
-
-private:
-    void insert_left_overlap(TcpReassemblerState& trs) override
-    { left_overlap_keep_first(trs); }
-
-    void insert_right_overlap(TcpReassemblerState& trs) override
-    { right_overlap_truncate_existing(trs); }
-
-    void insert_full_overlap(TcpReassemblerState& trs) override
-    { full_right_overlap_os1(trs); }
-};
-
-class TcpReassemblerMacOS : public TcpReassembler
-{
-public:
-    TcpReassemblerMacOS() = default;
-
-private:
-    void insert_left_overlap(TcpReassemblerState& trs) override
-    { left_overlap_keep_first(trs); }
-
-    void insert_right_overlap(TcpReassemblerState& trs) override
-    { right_overlap_truncate_existing(trs); }
-
-    void insert_full_overlap(TcpReassemblerState& trs) override
-    { full_right_overlap_os1(trs); }
-};
-
-class TcpReassemblerSolaris : public TcpReassembler
-{
-public:
-    TcpReassemblerSolaris() = default;
-
-private:
-    void insert_left_overlap(TcpReassemblerState& trs) override
-    { left_overlap_trim_first(trs); }
-
-    void insert_right_overlap(TcpReassemblerState& trs) override
-    { right_overlap_truncate_new(trs); }
-
-    void insert_full_overlap(TcpReassemblerState& trs) override
-    { full_right_overlap_os3(trs); }
-};
-
-class TcpReassemblerIrix : public TcpReassembler
-{
-public:
-    TcpReassemblerIrix() = default;
-
-private:
-    void insert_left_overlap(TcpReassemblerState& trs) override
-    { left_overlap_keep_first(trs);  }
-
-    void insert_right_overlap(TcpReassemblerState& trs) override
-    { right_overlap_truncate_existing(trs); }
-
-    void insert_full_overlap(TcpReassemblerState& trs) override
-    { full_right_overlap_os2(trs); }
-};
-
-class TcpReassemblerHpux11 : public TcpReassembler
-{
-public:
-    TcpReassemblerHpux11() = default;
-
-private:
-    void insert_left_overlap(TcpReassemblerState& trs) override
-    { left_overlap_trim_first(trs); }
-
-    void insert_right_overlap(TcpReassemblerState& trs) override
-    { right_overlap_truncate_new(trs); }
-
-    void insert_full_overlap(TcpReassemblerState& trs) override
-    { full_right_overlap_os3(trs); }
-};
-
-class TcpReassemblerHpux10 : public TcpReassembler
-{
-public:
-    TcpReassemblerHpux10() = default;
-
-private:
-    void insert_left_overlap(TcpReassemblerState& trs) override
-    { left_overlap_keep_first(trs); }
-
-    void insert_right_overlap(TcpReassemblerState& trs) override
-    { right_overlap_truncate_existing(trs); }
-
-    void insert_full_overlap(TcpReassemblerState& trs) override
-    { full_right_overlap_os2(trs); }
-};
-
-class TcpReassemblerWindows : public TcpReassembler
-{
-public:
-    TcpReassemblerWindows() = default;
-
-private:
-    void insert_left_overlap(TcpReassemblerState& trs) override
-    { left_overlap_keep_first(trs); }
-
-    void insert_right_overlap(TcpReassemblerState& trs) override
-    { right_overlap_truncate_existing(trs); }
-
-    void insert_full_overlap(TcpReassemblerState& trs) override
-    { full_right_overlap_os1(trs); }
-};
-
-class TcpReassemblerWindows2K3 : public TcpReassembler
-{
-public:
-    TcpReassemblerWindows2K3() = default;
-
-private:
-    void insert_left_overlap(TcpReassemblerState& trs) override
-    { left_overlap_keep_first(trs); }
-
-    void insert_right_overlap(TcpReassemblerState& trs) override
-    { right_overlap_truncate_existing(trs); }
-
-    void insert_full_overlap(TcpReassemblerState& trs) override
-    { full_right_overlap_os1(trs); }
-};
-
-class TcpReassemblerVista : public TcpReassembler
-{
-public:
-    TcpReassemblerVista() = default;
-
-private:
-    void insert_left_overlap(TcpReassemblerState& trs) override
-    { left_overlap_keep_first(trs); }
-
-    void insert_right_overlap(TcpReassemblerState& trs) override
-    { right_overlap_truncate_new(trs); }
-
-    void insert_full_overlap(TcpReassemblerState& trs) override
-    { full_right_overlap_os5 (trs); }
-};
-
-class TcpReassemblerProxy : public TcpReassemblerFirst
-{
-public:
-    TcpReassemblerProxy() = default;
-
-private:
-    void insert_left_overlap(TcpReassemblerState& trs) override
-    { left_overlap_keep_first(trs); }
-
-    void insert_right_overlap(TcpReassemblerState& trs) override
-    { right_overlap_truncate_new(trs); }
-
-    void insert_full_overlap(TcpReassemblerState& trs) override
-    { full_right_overlap_os5(trs); }
-};
-
-class TcpReassemblerMissed3whs : public TcpReassemblerFirst
-{
-public:
-    TcpReassemblerMissed3whs() = default;
-
-private:
-    void insert_left_overlap(TcpReassemblerState& trs) override
-    { left_overlap_keep_first(trs); }
-
-    void insert_right_overlap(TcpReassemblerState& trs) override
-    { right_overlap_truncate_new(trs); }
-
-    void insert_full_overlap(TcpReassemblerState& trs) override
-    { full_right_overlap_os5(trs); }
-};
-
-void TcpReassemblerPolicy::init(TcpSession* ssn, TcpStreamTracker* trk, StreamPolicy pol, bool server)
-{
-    trs.sos.init_sos(ssn, pol);
-    setup_paf();
-    trs.server_side = server;
-    trs.tracker = trk;
-
-    if ( trs.server_side )
-    {
-        trs.ignore_dir = SSN_DIR_FROM_CLIENT;
-        trs.packet_dir = PKT_FROM_CLIENT;
-    }
-    else
-    {
-        trs.ignore_dir = SSN_DIR_FROM_SERVER;
-        trs.packet_dir = PKT_FROM_SERVER;
-    }
-
-    trs.flush_count = 0;
-    trs.xtradata_mask = 0;
-    trs.alerts.clear();
-
-    reassembler = TcpReassemblerFactory::get_instance(pol);
-}
-
-void TcpReassemblerPolicy::reset()
-{ init(nullptr, nullptr, StreamPolicy::OS_DEFAULT, false); }
-
-TcpReassembler* TcpReassemblerFactory::reassemblers[StreamPolicy::OS_END_OF_LIST];
-
-void TcpReassemblerFactory::initialize()
-{
-    reassemblers[StreamPolicy::OS_FIRST] = new TcpReassemblerFirst;
-    reassemblers[StreamPolicy::OS_LAST] = new TcpReassemblerLast;
-    reassemblers[StreamPolicy::OS_LINUX] = new TcpReassemblerLinux;
-    reassemblers[StreamPolicy::OS_OLD_LINUX] = new TcpReassemblerOldLinux;
-    reassemblers[StreamPolicy::OS_BSD] = new TcpReassemblerBSD;
-    reassemblers[StreamPolicy::OS_MACOS] = new TcpReassemblerMacOS;
-    reassemblers[StreamPolicy::OS_SOLARIS] = new TcpReassemblerSolaris;
-    reassemblers[StreamPolicy::OS_IRIX] = new TcpReassemblerIrix;
-    reassemblers[StreamPolicy::OS_HPUX11] = new TcpReassemblerHpux11;
-    reassemblers[StreamPolicy::OS_HPUX10] = new TcpReassemblerHpux10;
-    reassemblers[StreamPolicy::OS_WINDOWS] = new TcpReassemblerWindows;
-    reassemblers[StreamPolicy::OS_WINDOWS2K3] = new TcpReassemblerWindows2K3;
-    reassemblers[StreamPolicy::OS_VISTA] = new TcpReassemblerVista;
-    reassemblers[StreamPolicy::OS_PROXY] = new TcpReassemblerProxy;
-    reassemblers[StreamPolicy::MISSED_3WHS] = new TcpReassemblerMissed3whs;
-}
-
-void TcpReassemblerFactory::term()
-{
-    for ( auto sp = StreamPolicy::OS_FIRST; sp < StreamPolicy::OS_END_OF_LIST; sp++ )
-        delete reassemblers[sp];
-}
-
-TcpReassembler* TcpReassemblerFactory::get_instance(StreamPolicy os_policy)
-{
-    assert( os_policy < StreamPolicy::OS_END_OF_LIST );
-    return reassemblers[os_policy];
-}
diff --git a/src/stream/tcp/tcp_reassemblers.h b/src/stream/tcp/tcp_reassemblers.h
deleted file mode 100644 (file)
index 37f1289..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-//--------------------------------------------------------------------------
-// Copyright (C) 2015-2024 Cisco and/or its affiliates. All rights reserved.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License Version 2 as published
-// by the Free Software Foundation.  You may not use, modify or distribute
-// this program under any other version of the GNU General Public License.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License along
-// with this program; if not, write to the Free Software Foundation, Inc.,
-// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-//--------------------------------------------------------------------------
-
-// tcp_reassemblers.h author davis mcpherson <davmcphe@cisco.com>
-// Created on: Oct 9, 2015
-
-#ifndef TCP_REASSEMBLERS_H
-#define TCP_REASSEMBLERS_H
-
-#include "tcp_reassembler.h"
-
-class TcpReassemblerFactory
-{
-public:
-    static void initialize();
-    static void term();
-    static TcpReassembler* get_instance(StreamPolicy);
-
-private:
-    TcpReassemblerFactory() = delete;
-
-    static TcpReassembler* reassemblers[StreamPolicy::OS_END_OF_LIST];
-};
-
-class TcpReassemblerPolicy
-{
-public:
-    TcpReassemblerPolicy() = default;
-    ~TcpReassemblerPolicy() = default;
-
-    void init(TcpSession* ssn, TcpStreamTracker* trk, StreamPolicy pol, bool server);
-    void reset();
-
-    void queue_packet_for_reassembly(TcpSegmentDescriptor& tsd)
-    { reassembler->queue_packet_for_reassembly(trs, tsd); }
-
-    bool add_alert(uint32_t gid, uint32_t sid)
-    { return reassembler->add_alert(trs, gid, sid); }
-
-    bool check_alerted(uint32_t gid, uint32_t sid)
-    { return reassembler->check_alerted(trs, gid, sid); }
-
-    int update_alert(uint32_t gid, uint32_t sid, uint32_t event_id, uint32_t event_second)
-    { return reassembler->update_alert(trs, gid, sid, event_id, event_second); }
-
-    void purge_alerts()
-    { reassembler->purge_alerts(trs); }
-
-    void purge_segment_list()
-    { reassembler->purge_segment_list(trs); }
-
-    void purge_flushed_ackd()
-    { return reassembler->purge_flushed_ackd(trs); }
-
-    int flush_stream(snort::Packet* p, uint32_t dir, bool final_flush = false)
-    { return reassembler->flush_stream(trs, p, dir, final_flush); }
-
-    void finish_and_final_flush(snort::Flow* flow, bool clear, snort::Packet* p)
-    { reassembler->finish_and_final_flush(trs, flow, clear, p); }
-
-    void flush_queued_segments(snort::Flow* flow, bool clear, const snort::Packet* p = nullptr)
-    { reassembler->flush_queued_segments(trs, flow, clear, p); }
-
-    bool is_segment_pending_flush() const
-    { return reassembler->is_segment_pending_flush(trs); }
-
-    void skip_midstream_pickup_seglist_hole(TcpSegmentDescriptor& tsd)
-    { reassembler->skip_midstream_pickup_seglist_hole(trs, tsd); }
-
-    void reset_asymmetric_flow_reassembly()
-    { reassembler->reset_asymmetric_flow_reassembly(trs); }
-
-    void initialize_paf()
-    { reassembler->initialize_paf(trs); }
-
-    int flush_on_data_policy(snort::Packet* p)
-    { return reassembler->flush_on_data_policy(trs, p); }
-
-    int flush_on_ack_policy(snort::Packet* p)
-    { return reassembler->flush_on_ack_policy(trs, p); }
-
-    void set_seglist_base_seq(uint32_t seglist_base_seq)
-    { trs.sos.seglist_base_seq = seglist_base_seq; }
-
-    uint32_t get_seglist_base_seq() const
-    { return trs.sos.seglist_base_seq; }
-
-    void set_xtradata_mask(uint32_t xtradata_mask)
-    { trs.xtradata_mask = xtradata_mask; }
-
-    uint32_t get_xtradata_mask() const
-    { return trs.xtradata_mask; }
-
-    bool data_was_queued() const
-    { return trs.sos.total_bytes_queued > 0; }
-
-    uint32_t get_seg_count() const
-    { return trs.sos.seg_count; }
-
-    uint32_t get_seg_bytes_total() const
-    { return trs.sos.seg_bytes_total; }
-
-    uint32_t get_overlap_count() const
-    { return trs.sos.overlap_count; }
-
-    void set_overlap_count(uint32_t overlap_count)
-    { trs.sos.overlap_count = overlap_count;  }
-
-    uint32_t get_flush_count() const
-    { return trs.flush_count; }
-
-    uint32_t get_seg_bytes_logical() const
-    { return trs.sos.seg_bytes_logical; }
-
-    StreamPolicy get_reassembly_policy() const
-    { return trs.sos.reassembly_policy; }
-
-    void set_norm_mode_test()
-    { trs.sos.tcp_ips_data = NORM_MODE_TEST; }
-
-    bool segment_within_seglist_window(TcpSegmentDescriptor& tsd)
-    { return reassembler->segment_within_seglist_window(trs, tsd); }
-
-    uint32_t perform_partial_flush(snort::Flow* flow, snort::Packet*& p)
-    { return reassembler->perform_partial_flush(trs, flow, p); }
-
-    void reset_paf()
-    { paf_reset(&trs.paf_state); }
-
-    void clear_paf()
-    { paf_clear(&trs.paf_state); }
-
-    void setup_paf()
-    {
-        paf_setup(&trs.paf_state);
-        if ( trs.sos.seglist.cur_rseg )
-            trs.sos.seglist.cur_sseg = trs.sos.seglist.cur_rseg;
-        else
-            trs.sos.seglist.cur_sseg = trs.sos.seglist.head;
-    }
-
-private:
-    TcpReassembler* reassembler = nullptr;
-    TcpReassemblerState trs = {};
-    friend inline void TraceSegments(const TcpReassemblerPolicy&, const snort::Packet* p);
-};
-#endif
-
diff --git a/src/stream/tcp/tcp_reassembly_segments.cc b/src/stream/tcp/tcp_reassembly_segments.cc
new file mode 100644 (file)
index 0000000..b394869
--- /dev/null
@@ -0,0 +1,450 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-2024 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// tcp_reassembly_segments.cc author davis mcpherson <davmcphe@cisco.com>
+// Created on: Oct 9, 2015
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "tcp_reassembly_segments.h"
+
+#include "log/messages.h"
+#include "packet_io/packet_tracer.h"
+#include "protocols/tcp.h"
+
+#include "tcp_module.h"
+#include "tcp_overlap_resolver.h"
+#include "tcp_segment_descriptor.h"
+#include "tcp_segment_node.h"
+#include "tcp_session.h"
+#include "tcp_stream_tracker.h"
+#include "tcp_overlap_resolver.h"
+
+using namespace snort;
+
+TcpReassemblySegments::~TcpReassemblySegments()
+{
+    delete tos;
+}
+
+void TcpReassemblySegments::init(TcpSession* ssn, TcpStreamTracker* trk, StreamPolicy pol)
+{
+    session = ssn;
+    tracker = trk;
+    overlap_resolver = TcpOverlapResolverFactory::get_instance(pol);
+    if ( tos )
+        delete tos;
+    tos = new TcpOverlapState(*this);
+}
+
+void TcpReassemblySegments::reset()
+{
+    purge();
+    seglist_base_seq = 0;
+}
+
+void TcpReassemblySegments::update_next(TcpSegmentNode* tsn)
+{
+    cur_rseg = tsn->next_no_gap() ?  tsn->next : nullptr;
+}
+
+bool TcpReassemblySegments::is_segment_pending_flush() const
+{
+    return ( get_pending_segment_count(1) > 0 );
+}
+
+uint32_t TcpReassemblySegments::get_pending_segment_count(const unsigned max) const
+{
+    uint32_t n = seg_count - flush_count;
+
+    if ( !n || max == 1 )
+        return n;
+
+    n = 0;
+    const TcpSegmentNode* tsn = head;
+    while ( tsn )
+    {
+        if ( tsn->unscanned() && SEQ_LT(tsn->scan_seq(), tracker->r_win_base) )
+            n++;
+
+        if ( max && n == max )
+            return n;
+
+        tsn = tsn->next;
+    }
+
+    return n;
+}
+
+bool TcpReassemblySegments::segment_within_seglist_window(TcpSegmentDescriptor& tsd)
+{
+    if ( !head )
+        return true;
+
+    // Left side
+    uint32_t start;
+    if ( SEQ_LT(seglist_base_seq, head->start_seq()) )
+        start = seglist_base_seq;
+    else
+        start = head->seq;
+
+    if ( SEQ_LEQ(tsd.get_end_seq(), start) )
+        return false;
+
+    // Right side
+    if ( SEQ_GEQ(tsd.get_seq(), tail->next_seq()) )
+        return false;
+
+    return true;
+}
+
+void TcpReassemblySegments::queue_reassembly_segment(TcpSegmentDescriptor& tsd)
+{
+    if ( seg_count == 0 )
+    {
+        insert_segment_in_empty_seglist(tsd);
+    }
+    else if ( SEQ_GT(tracker->r_win_base, tsd.get_seq() ) )
+    {
+        const int32_t offset = tracker->r_win_base - tsd.get_seq();
+
+        if ( offset < tsd.get_len() )
+        {
+            tsd.slide_segment_in_rcv_window(offset);
+            insert_segment_in_seglist(tsd);
+            tsd.slide_segment_in_rcv_window(-offset);
+        }
+    }
+    else
+        insert_segment_in_seglist(tsd);
+}
+
+void TcpReassemblySegments::insert_segment_in_empty_seglist(TcpSegmentDescriptor& tsd)
+{
+    uint32_t overlap = 0;
+
+    if ( SEQ_GT(seglist_base_seq,  tsd.get_seq()) )
+    {
+        overlap = seglist_base_seq - tsd.get_seq();
+        if ( overlap >= tsd.get_len() )
+            return;
+    }
+
+    add_reassembly_segment(tsd, tsd.get_len(), overlap, 0, tsd.get_seq(), nullptr);
+}
+
+bool TcpReassemblySegments::is_segment_fasttrack(TcpSegmentNode* tail, const TcpSegmentDescriptor& tsd)
+{
+    if ( SEQ_EQ(tsd.get_seq(), tail->next_seq()) )
+        return true;
+
+    return false;
+}
+
+void TcpReassemblySegments::insert_segment_in_seglist(TcpSegmentDescriptor& tsd)
+{
+    // NORM fast tracks are in sequence - no norms
+    if ( tail && is_segment_fasttrack(tail, tsd) )
+    {
+        /* segment fit cleanly at the end of the segment list */
+        add_reassembly_segment(tsd, tsd.get_len(), 0, 0, tsd.get_seq(), tail);
+        return;
+    }
+
+    tos->init(tsd);
+    overlap_resolver->eval_left(*tos);
+    overlap_resolver->eval_right(*tos);
+
+    if ( tos->keep_segment )
+    {
+        // FIXIT-L - is this skipping the add if the segment is first and already scan
+        if ( !tos->left and tos->right and tracker->reassembler->segment_already_scanned(tsd.get_seq()) )
+        {
+            return;
+        }
+
+        add_reassembly_segment(tsd, tos->len, tos->slide, tos->trunc_len, tos->seq, tos->left);
+    }
+}
+
+void TcpReassemblySegments::insert_segment_data(TcpSegmentNode* prev, TcpSegmentNode* tsn)
+{
+    insert(prev, tsn);
+
+    if ( !cur_sseg )
+        cur_sseg = tsn;
+    else if ( SEQ_LT(tsn->scan_seq(), cur_sseg->scan_seq()) )
+    {
+        cur_sseg = tsn;
+        if ( SEQ_LT(tsn->scan_seq(), seglist_base_seq) )
+            seglist_base_seq = tsn->scan_seq();
+
+        if ( cur_rseg && SEQ_LT(tsn->scan_seq(), cur_rseg->scan_seq()) )
+            cur_rseg = tsn;
+    }
+
+    // FIXIT-M - increment seg_count here?
+    seg_bytes_total += tsn->size;
+    total_segs_queued++;
+    tcpStats.segs_queued++;
+
+    if ( seg_count > tcpStats.max_segs )
+        tcpStats.max_segs = seg_count;
+
+    if ( seg_bytes_total > tcpStats.max_bytes )
+        tcpStats.max_bytes = seg_bytes_total;
+}
+
+void TcpReassemblySegments::add_reassembly_segment(TcpSegmentDescriptor& tsd, uint16_t len,
+    uint32_t slide, uint32_t trunc_len, uint32_t seq, TcpSegmentNode* left)
+{
+    const int32_t new_size = len - slide - trunc_len;
+    assert(new_size >= 0);
+
+    // if trimming will delete all data, don't insert this segment in the queue
+    if ( new_size <= 0 )
+    {
+        tcpStats.payload_fully_trimmed++;
+        tracker->normalizer.trim_win_payload(tsd);
+        return;
+    }
+
+    // FIXIT-L don't allocate overlapped part
+    TcpSegmentNode* tsn = TcpSegmentNode::init(tsd);
+
+    tsn->seq = seq;
+    tsn->offset = slide;
+    tsn->length = (uint16_t)new_size;
+    tsn->cursor = 0;
+    tsn->ts = tsd.get_timestamp();
+
+    // FIXIT-M the urgent ptr handling is broken... urg_offset could be set here but currently
+    // not actually referenced anywhere else.  In 2.9.7 the FlushStream function did reference
+    // this field but that code has been lost... urg ptr handling needs to be reviewed and fixed
+    // tsn->urg_offset = tracker->normalizer.set_urg_offset(tsd.get_tcph(), tsd.get_seg_len());
+
+    insert_segment_data(left, tsn);
+
+    seg_bytes_logical += tsn->length;
+    total_bytes_queued += tsn->size;
+    tsd.set_packet_flags(PKT_STREAM_INSERT);
+
+    if( tsd.is_packet_inorder()
+        or (SEQ_LEQ(tsn->start_seq(), tracker->get_rcv_nxt())
+            and SEQ_GEQ(tsn->next_seq(), tracker->get_rcv_nxt())) )
+        advance_rcv_nxt(tsn);
+}
+
+void TcpReassemblySegments::dup_reassembly_segment(TcpSegmentNode* left, TcpSegmentNode** retSeg)
+{
+    TcpSegmentNode* tsn = TcpSegmentNode::init(*left);
+    tcpStats.segs_split++;
+
+    // twiddle the values for overlaps
+    tsn->cursor = left->cursor;
+    tsn->seq = left->seq;
+    insert_segment_data(left, tsn);
+
+    *retSeg = tsn;
+}
+
+int TcpReassemblySegments::delete_reassembly_segment(TcpSegmentNode* tsn)
+{
+    int ret;
+    assert(tsn);
+
+    remove(tsn);
+    seg_bytes_total -= tsn->size;
+    seg_bytes_logical -= tsn->length;
+    ret = tsn->length;
+
+    if ( !tsn->unscanned() )
+    {
+        tcpStats.segs_used++;
+        flush_count--;
+    }
+
+    if ( cur_sseg == tsn )
+        cur_sseg = tsn->next;
+
+    if ( cur_rseg == tsn )
+        update_next(tsn);
+
+    tsn->term();
+
+    return ret;
+}
+
+void TcpReassemblySegments::purge_flushed_segments(uint32_t flush_seq)
+{
+    assert( head );
+    uint32_t last_ts = 0;
+
+    TcpSegmentNode* tsn = head;
+    while ( tsn && SEQ_LT(tsn->start_seq(), flush_seq))
+    {
+        if ( tsn->unscanned() )
+            break;
+
+        TcpSegmentNode* dump_me = tsn;
+        tsn = tsn->next;
+        if (dump_me->ts > last_ts)
+            last_ts = dump_me->ts;
+
+        delete_reassembly_segment(dump_me);
+    }
+
+    if ( tsn and SEQ_LT(tracker->rcv_nxt, tsn->next_seq()) )
+        advance_rcv_nxt(tsn);
+
+    /* Update the "last" time stamp seen from the other side
+     * to be the most recent timestamp (largest) that was removed
+     * from the queue.  This will ensure that as we go forward,
+     * last timestamp is the highest one that we had stored and
+     * purged and handle the case when packets arrive out of order,
+     * such as:
+     * P1: seq 10, length 10, timestamp 10
+     * P3: seq 30, length 10, timestamp 30
+     * P2: seq 20, length 10, timestamp 20
+     *
+     * Without doing it this way, the timestamp would be 20.  With
+     * the next packet to arrive (P4, seq 40), the ts_last value
+     * wouldn't be updated for the talker in ProcessTcp() since that
+     * code specifically looks for the NEXT sequence number.
+     */
+    if ( last_ts )
+    {
+        if ( tracker->client_tracker )
+        {
+            int32_t delta = last_ts - session->server.get_ts_last();
+            if ( delta > 0 )
+                session->server.set_ts_last(last_ts);
+        }
+       else
+        {
+           int32_t delta = last_ts - session->client.get_ts_last();
+           if ( delta > 0 )
+               session->client.set_ts_last(last_ts);
+        }
+    }
+}
+
+void TcpReassemblySegments::purge_segments_left_of_hole(const TcpSegmentNode* end_tsn)
+{
+    uint32_t packets_skipped = 0;
+
+    TcpSegmentNode* cur_tsn = head;
+    do
+    {
+        TcpSegmentNode* drop_tsn = cur_tsn;
+        cur_tsn = cur_tsn->next;
+        delete_reassembly_segment(drop_tsn);
+        ++packets_skipped;
+    } while( cur_tsn and cur_tsn != end_tsn );
+
+    tracker->set_order(TcpStreamTracker::OUT_OF_SEQUENCE);
+
+    if (PacketTracer::is_active())
+        PacketTracer::log("Stream: Skipped %u packets before seglist hole)\n", packets_skipped);
+}
+
+void TcpReassemblySegments::advance_rcv_nxt(TcpSegmentNode *tsn)
+{
+    if ( !tsn )
+    {
+        if ( !head )
+            return;
+        tsn = head;
+    }
+
+    while (tsn->next_no_gap())
+        tsn = tsn->next;
+    tracker->set_rcv_nxt(tsn->next_seq());
+}
+
+void TcpReassemblySegments::skip_holes()
+{
+    TcpSegmentNode* tsn = head;
+    // if there is a hole at the beginning, skip it...
+    if ( SEQ_GT(tsn->seq, seglist_base_seq) )
+    {
+        seglist_base_seq = tsn->seq;
+        tracker->set_order(TcpStreamTracker::OUT_OF_SEQUENCE);
+
+        if (PacketTracer::is_active())
+            PacketTracer::log("Stream: Skipped hole at beginning of the seglist\n");
+    }
+
+    while ( tsn )
+    {
+        if ( tsn->next and SEQ_GT(tsn->next->start_seq(), tsn->next_seq()) )
+        {
+            tsn = tsn->next;
+            purge_segments_left_of_hole(tsn);
+            seglist_base_seq = head->start_seq();
+        }
+       else
+            tsn = tsn->next;
+    }
+
+    advance_rcv_nxt();
+    tracker->set_order(TcpStreamTracker::OUT_OF_SEQUENCE);
+}
+
+void TcpReassemblySegments::skip_midstream_pickup_seglist_hole(TcpSegmentDescriptor& tsd)
+{
+    uint32_t ack = tsd.get_ack();
+
+    TcpSegmentNode* tsn = head;
+    while ( tsn )
+    {
+        if ( SEQ_GEQ( tsn->next_seq(), ack) )
+            break;
+
+        if ( tsn->next and SEQ_GT(tsn->next->start_seq(), tsn->next_seq()) )
+        {
+            tsn = tsn->next;
+            purge_segments_left_of_hole(tsn);
+            seglist_base_seq = head->start_seq();
+        }
+        else if ( !tsn->next and SEQ_LT(tsn->next_seq(), ack) )
+        {
+            tsn = tsn->next;
+            purge_segments_left_of_hole(tsn);
+            seglist_base_seq = ack;
+        }
+        else
+            tsn = tsn->next;
+    }
+
+    tsn = head;
+    if ( tsn )
+    {
+        tracker->reassembler->initialize_paf();
+        advance_rcv_nxt(tsn);
+    }
+    else
+        tracker->set_rcv_nxt(ack);
+}
+
+void TcpReassemblySegments::purge_segment_list()
+{
+    purge();
+}
diff --git a/src/stream/tcp/tcp_reassembly_segments.h b/src/stream/tcp/tcp_reassembly_segments.h
new file mode 100644 (file)
index 0000000..bff362d
--- /dev/null
@@ -0,0 +1,190 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-2024 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// tcp_reassembly_segments.h author davis mcpherson <davmcphe@cisco.com>
+// Created on: Oct 9, 2015
+
+#ifndef TCP_REASSEMBLERS_H
+#define TCP_REASSEMBLERS_H
+
+#include <cstdint>
+
+#include "tcp_defs.h"
+
+#include "tcp_segment_node.h"
+
+class TcpOverlapResolver;
+class TcpOverlapState;
+class TcpSegmentDescriptor;
+class TcpSession;
+class TcpStreamTracker;
+
+class TcpReassemblySegments
+{
+public:
+    TcpReassemblySegments() = default;
+    ~TcpReassemblySegments();
+
+    void init(TcpSession* ssn, TcpStreamTracker* trk, StreamPolicy pol);
+    void reset();
+
+    void update_next(TcpSegmentNode*);
+    bool segment_within_seglist_window(TcpSegmentDescriptor&);
+    void queue_reassembly_segment(TcpSegmentDescriptor&);
+    void add_reassembly_segment(TcpSegmentDescriptor&, uint16_t, uint32_t,
+        uint32_t, uint32_t, TcpSegmentNode*);
+    void dup_reassembly_segment(TcpSegmentNode*, TcpSegmentNode**);
+    int delete_reassembly_segment(TcpSegmentNode*);
+    void advance_rcv_nxt(TcpSegmentNode *tsn = nullptr);
+    void purge_flushed_segments(uint32_t flush_seq);
+    void purge_segments_left_of_hole(const TcpSegmentNode*);
+    void skip_holes();
+    void skip_midstream_pickup_seglist_hole(TcpSegmentDescriptor&);
+    void purge_segment_list();
+
+    bool is_segment_pending_flush() const;
+
+    void set_seglist_base_seq(uint32_t base_seq)
+    { seglist_base_seq = base_seq; }
+
+    uint32_t get_seglist_base_seq() const
+    { return seglist_base_seq; }
+
+    bool data_was_queued() const
+    { return total_bytes_queued > 0; }
+
+    uint32_t get_seg_count() const
+    { return seg_count; }
+
+    uint32_t get_seg_bytes_total() const
+    { return seg_bytes_total; }
+
+    uint32_t get_overlap_count() const
+    { return overlap_count; }
+
+    void set_overlap_count(uint32_t count)
+    { overlap_count = count;  }
+
+    uint32_t get_flush_count() const
+    { return flush_count; }
+
+    uint32_t get_seg_bytes_logical() const
+    { return seg_bytes_logical; }
+
+private:
+    void insert_segment_data(TcpSegmentNode* prev, TcpSegmentNode*);
+
+
+    void insert(TcpSegmentNode* prev, TcpSegmentNode* ss)
+    {
+        if ( prev )
+        {
+            ss->next = prev->next;
+            ss->prev = prev;
+            prev->next = ss;
+
+            if ( ss->next )
+                ss->next->prev = ss;
+            else
+                tail = ss;
+        }
+        else
+        {
+            ss->next = head;
+
+            if ( ss->next )
+                ss->next->prev = ss;
+            else
+                tail = ss;
+            head = ss;
+        }
+
+        seg_count++;
+    }
+
+    void remove(TcpSegmentNode* ss)
+    {
+        if ( ss->prev )
+            ss->prev->next = ss->next;
+        else
+            head = ss->next;
+
+        if ( ss->next )
+            ss->next->prev = ss->prev;
+        else
+            tail = ss->prev;
+
+        seg_count--;
+    }
+
+    uint32_t purge()
+    {
+        int i = 0;
+
+        while ( head )
+        {
+            i++;
+            TcpSegmentNode* dump_me = head;
+            head = head->next;
+            dump_me->term();
+        }
+
+        head = tail = cur_rseg = cur_sseg = nullptr;
+        seg_count = 0;
+        flush_count = 0;
+        seg_bytes_total = 0;
+        seg_bytes_logical = 0;
+        total_bytes_queued = 0;
+        total_segs_queued = 0;
+        overlap_count = 0;
+        return i;
+    }
+
+public:
+    TcpSegmentNode* head = nullptr;
+    TcpSegmentNode* tail = nullptr;
+
+    TcpSegmentNode* cur_rseg = nullptr;
+    TcpSegmentNode* cur_sseg = nullptr;
+
+    uint32_t seg_count = 0;             /* number of current queued segments */
+    uint32_t flush_count = 0;           /* queued segments already flushed */
+
+    uint32_t seglist_base_seq = 0;      /* seq of first queued segment */
+    uint32_t seg_bytes_total = 0;       /* total bytes currently queued */
+    uint32_t seg_bytes_logical = 0;     /* logical bytes queued (total - overlaps) */
+    uint32_t total_bytes_queued = 0;    /* total bytes queued (life of session) */
+    uint32_t total_segs_queued = 0;     /* number of segments queued (life) */
+    uint32_t overlap_count = 0;         /* overlaps encountered */
+
+    TcpSession* session = nullptr;
+    TcpStreamTracker* tracker = nullptr;
+    TcpOverlapResolver* overlap_resolver = nullptr;
+    TcpOverlapState* tos = nullptr;
+
+private:
+    void insert_segment_in_empty_seglist(TcpSegmentDescriptor&);
+    void insert_segment_in_seglist(TcpSegmentDescriptor&);
+
+    bool is_segment_fasttrack(TcpSegmentNode*, const TcpSegmentDescriptor&);
+    uint32_t get_pending_segment_count(const unsigned max) const;
+
+};
+
+#endif
+
index 5613d0808476fcf3f214fbfd9ef5e30e2d286b0d..71d95ed21e19f14b59baecdf923030308b189e0a 100644 (file)
 #include "detection/rules.h"
 #include "packet_io/packet_tracer.h"
 #include "protocols/tcp_options.h"
-#include "stream/tcp/tcp_defs.h"
-#include "stream/tcp/tcp_stream_tracker.h"
+
+#include "tcp_defs.h"
+#include "tcp_event_logger.h"
+#include "tcp_stream_tracker.h"
 
 using namespace snort;
 
index 6ba4dd128f81bea492832a5036c4bebdcec233b2..709f19981a4813bddedf4cebf2b35dac3dad447d 100644 (file)
 
 #include <cassert>
 
-#include <daq_common.h>
-
 #include "flow/flow.h"
 #include "detection/ips_context.h"
 #include "main/snort_config.h"
 #include "packet_io/active.h"
 #include "protocols/packet.h"
 #include "protocols/tcp.h"
-#include "stream/tcp/tcp_event_logger.h"
 
+class TcpEventLogger;
 class TcpStreamTracker;
 
 class TcpSegmentDescriptor
@@ -172,6 +170,16 @@ public:
     void set_talker(TcpStreamTracker& tracker)
     { talker = &tracker; }
 
+    bool is_packet_inorder() const
+    {
+        return packet_inorder;
+    }
+
+    void set_packet_inorder(bool inorder)
+    {
+        packet_inorder = inorder;
+    }
+
 private:
     snort::Flow* const flow;
     snort::Packet* const pkt;
@@ -189,6 +197,7 @@ private:
     uint16_t dst_port;
     uint32_t packet_timestamp;
     bool packet_from_client;
+    bool packet_inorder = false;
     bool meta_ack_packet = false;
 };
 
index 8836165e3f071553c352e948bacaadf6de30b1a1..2fdbbd72427d1df9660d1f028facd8106c45df13 100644 (file)
 
 #include "utils/util.h"
 
-#include "segment_overlap_editor.h"
 #include "tcp_module.h"
+#include "tcp_segment_descriptor.h"
+
+using namespace snort;
 
 #define USE_RESERVE
 #ifdef USE_RESERVE
@@ -87,12 +89,14 @@ TcpSegmentNode* TcpSegmentNode::create(
         tcpStats.mem_in_use += len;
     }
     tsn->tv = tv;
-    tsn->i_len = tsn->c_len = len;
+    tsn->length = len;
     memcpy(tsn->data, payload, len);
 
     tsn->prev = tsn->next = nullptr;
-    tsn->i_seq = tsn->c_seq = 0;
-    tsn->offset = tsn->o_offset = 0;
+
+    tsn->seq = 0;
+    tsn->offset = 0;
+    tsn->cursor = 0;
     tsn->ts = 0;
 
     return tsn;
@@ -103,9 +107,9 @@ TcpSegmentNode* TcpSegmentNode::init(const TcpSegmentDescriptor& tsd)
     return create(tsd.get_pkt()->pkth->ts, tsd.get_pkt()->data, tsd.get_len());
 }
 
-TcpSegmentNode* TcpSegmentNode::init(TcpSegmentNode& tns)
+TcpSegmentNode* TcpSegmentNode::init(TcpSegmentNode& tsn)
 {
-    return create(tns.tv, tns.payload(), tns.c_len);
+    return create(tsn.tv, tsn.payload(), tsn.length);
 }
 
 void TcpSegmentNode::term()
@@ -130,12 +134,12 @@ bool TcpSegmentNode::is_retransmit(const uint8_t* rdata, uint16_t rsize,
     uint32_t rseq, uint16_t orig_dsize, bool *full_retransmit)
 {
     // retransmit must have same payload at same place
-    if ( !SEQ_EQ(i_seq, rseq) )
+    if ( !SEQ_EQ(seq, rseq) )
         return false;
 
-    if ( orig_dsize == c_len )
+    if ( orig_dsize == unscanned() )
     {
-        uint16_t cmp_len = ( c_len <= rsize ) ? c_len : rsize;
+        uint16_t cmp_len = ( length <= rsize ) ? length : rsize;
         if ( !memcmp(data, rdata, cmp_len) )
             return true;
     }
index 1aab42c53b3ef4e687d8821c376423a902126d09..0f86c09a76f405898152818e7a0418691f2ddea7 100644 (file)
 // tcp_segment_node.h author davis mcpherson <davmcphe@cisco.com>
 // Created on: Sep 21, 2015
 
-#ifndef TCP_SEGMENT_H
-#define TCP_SEGMENT_H
+#ifndef TCP_SEGMENT_NODE_H
+#define TCP_SEGMENT_NODE_H
+
+#include <cassert>
+
+#include "protocols/packet.h"
+#include "protocols/tcp.h"
 
-#include "tcp_segment_descriptor.h"
 #include "tcp_defs.h"
 
 class TcpSegmentDescriptor;
@@ -53,104 +57,61 @@ public:
     uint8_t* payload()
     { return data + offset; }
 
+    uint8_t* paf_data()
+    { return data + offset + cursor; }
+
+    uint32_t start_seq() const
+    { return seq + offset; }
+
+    uint32_t next_seq() const
+    { return start_seq() + length; }
+
+    uint32_t scan_seq() const
+    { return start_seq() + cursor; }
+
     bool is_packet_missing(uint32_t to_seq)
     {
         if ( next )
-            return !(SEQ_EQ((i_seq + i_len), next->i_seq));
+            return !(SEQ_EQ(next_seq(), next->start_seq()));
         else
-            return SEQ_LT((c_seq + c_len), to_seq);
+            return SEQ_LT(next_seq(), to_seq);
     }
 
-    void update_reassembly_cursor(uint16_t bytes)
+    void advance_cursor(uint16_t bytes)
+    { cursor += bytes; }
+
+    unsigned unscanned() const
     {
-        c_seq += bytes;
-        c_len -= bytes;
-        offset += bytes;
+        assert(cursor <= length);
+        return length - cursor;
     }
 
-public:
-    TcpSegmentNode* prev;
-    TcpSegmentNode* next;
-
-    struct timeval tv;
-    uint32_t ts;
-    uint32_t i_seq;             // initial seq # of the data segment
-    uint32_t c_seq;             // current seq # of data for reassembly
-    uint16_t i_len;             // initial length of the data segment
-    uint16_t c_len;             // length of data remaining for reassembly
-    uint16_t offset;            // current offset into the data buffer for reassembly
-    uint16_t o_offset;          // offset into the data buffer due to overlaps
-    uint16_t size;              // actual allocated size (overlaps cause i_len to differ)
-    uint8_t data[1];
-};
-
-class TcpSegmentList
-{
-public:
-    uint32_t reset()
+    bool next_no_gap()
     {
-        int i = 0;
-
-        while ( head )
-        {
-            i++;
-            TcpSegmentNode* dump_me = head;
-            head = head->next;
-            dump_me->term();
-        }
-
-        head = tail = cur_rseg = cur_sseg = nullptr;
-        count = 0;
-        return i;
+        return next and SEQ_EQ(next_seq(), next->start_seq());
     }
 
-    void insert(TcpSegmentNode* prev, TcpSegmentNode* ss)
+    bool next_acked_no_gap(uint32_t seq_acked)
     {
-        if ( prev )
-        {
-            ss->next = prev->next;
-            ss->prev = prev;
-            prev->next = ss;
-
-            if ( ss->next )
-                ss->next->prev = ss;
-            else
-                tail = ss;
-        }
-        else
-        {
-            ss->next = head;
-
-            if ( ss->next )
-                ss->next->prev = ss;
-            else
-                tail = ss;
-            head = ss;
-        }
+        if ( !next_no_gap() )
+            return false;
 
-        count++;
+        return SEQ_LT(next->start_seq() + next->cursor, seq_acked);
     }
 
-    void remove(TcpSegmentNode* ss)
-    {
-        if ( ss->prev )
-            ss->prev->next = ss->next;
-        else
-            head = ss->next;
-
-        if ( ss->next )
-            ss->next->prev = ss->prev;
-        else
-            tail = ss->prev;
+public:
+    TcpSegmentNode* prev;
+    TcpSegmentNode* next;
 
-        count--;
-    }
+    struct timeval tv;
+    uint32_t ts;
 
-    TcpSegmentNode* head = nullptr;
-    TcpSegmentNode* tail = nullptr;
-    TcpSegmentNode* cur_rseg = nullptr;
-    TcpSegmentNode* cur_sseg = nullptr;
-    uint32_t count = 0;
+    uint32_t seq;           // initial seq # of the data segment (fixed)
+    uint16_t length;        // working length of the segment data (relative to offset)
+    uint16_t offset;        // working start of segment data
+    uint16_t cursor;        // scan position (relative to offset)
+    uint16_t size;          // allocated payload size
+    uint8_t data[1];
 };
 
 #endif
index aa615790aa182abd2bf33a55de78d2bcb76fa4cf..e202f96b4f3ebf57bcb66cfb4b8920d176c30c7f 100644 (file)
 
 #include "detection/detection_engine.h"
 #include "detection/rules.h"
+#include "framework/data_bus.h"
 #include "log/log.h"
 #include "packet_io/packet_tracer.h"
 #include "profiler/profiler.h"
 #include "protocols/eth.h"
 #include "pub_sub/intrinsic_event_ids.h"
+#include "pub_sub/stream_event_ids.h"
+#include "stream/stream.h"
 
 #include "stream_tcp.h"
 #include "tcp_ha.h"
 #include "tcp_module.h"
 #include "tcp_normalizers.h"
-#include "tcp_reassemblers.h"
 #include "tcp_segment_node.h"
 #include "tcp_state_machine.h"
 #include "tcp_trace.h"
@@ -80,13 +82,13 @@ void TcpSession::sterm()
     TcpSegmentNode::clear();
 }
 
-TcpSession::TcpSession(Flow* f) : TcpStreamSession(f)
+TcpSession::TcpSession(Flow* f)
+    : Session(f), client(true), server(false)
 {
     tsm = TcpStateMachine::get_instance();
-    splitter_init = false;
+    client.init_tcp_state(this);
+    server.init_tcp_state(this);
 
-    client.session = this;
-    server.session = this;
     tcpStats.instantiated++;
 }
 
@@ -97,9 +99,9 @@ TcpSession::~TcpSession()
 
 bool TcpSession::setup(Packet*)
 {
-    client.init_tcp_state();
-    server.init_tcp_state();
-    lws_init = tcp_init = false;
+    client.init_tcp_state(this);
+    server.init_tcp_state(this);
+    tcp_init = false;
     generate_3whs_alert = true;
     cleaning = false;
     splitter_init = false;
@@ -121,7 +123,6 @@ bool TcpSession::setup(Packet*)
     return true;
 }
 
-// FIXIT-M once TcpReassembler interface is abstract class move this to base class
 void TcpSession::restart(Packet* p)
 {
     // sanity check since this is called externally
@@ -150,14 +151,14 @@ void TcpSession::restart(Packet* p)
     if ( talker->midstream_initial_ack_flush )
     {
         talker->midstream_initial_ack_flush = false;
-        talker->reassembler.flush_on_data_policy(p);
+        talker->eval_flush_policy_on_data(p);
     }
 
     if (p->dsize > 0)
-        listener->reassembler.flush_on_data_policy(p);
+        listener->eval_flush_policy_on_data(p);
 
     if (p->ptrs.tcph->is_ack())
-        talker->reassembler.flush_on_ack_policy(p);
+        talker->eval_flush_policy_on_ack(p);
 
     tcpStats.restarts++;
 }
@@ -169,58 +170,23 @@ void TcpSession::clear_session(bool free_flow_data, bool flush_segments, bool re
 {
     assert(!p or p->flow == flow);
     if ( !tcp_init )
-    {
-        if ( lws_init )
-            tcpStats.no_pickups++;
         return;
-    }
 
-    lws_init = false;
     tcp_init = false;
     tcpStats.released++;
 
     if ( !flow->two_way_traffic() and free_flow_data )
         tcpStats.asymmetric_flows++;
 
-    if ( flush_segments )
-    {
-        client.reassembler.flush_queued_segments(flow, true, p);
-        server.reassembler.flush_queued_segments(flow, true, p);
-    }
-
-    if ( p )
-    {
-        client.finalize_held_packet(p);
-        server.finalize_held_packet(p);
-    }
-    else
-    {
-        client.finalize_held_packet(flow);
-        server.finalize_held_packet(flow);
-    }
-
-    client.reassembler.purge_segment_list();
-    server.reassembler.purge_segment_list();
-
+    client.clear_tracker(flow, p, flush_segments, restart);
+    server.clear_tracker(flow, p, flush_segments, restart);
     update_perf_base_state(TcpStreamTracker::TCP_CLOSED);
-
-    set_splitter(true, nullptr);
-    set_splitter(false, nullptr);
+    tel.log_internal_event(SESSION_EVENT_CLEAR);
 
     if ( restart )
-    {
         flow->restart(free_flow_data);
-        client.reassembler.reset_paf();
-        server.reassembler.reset_paf();
-    }
     else
-    {
         flow->clear(free_flow_data);
-        client.reassembler.clear_paf();
-        server.reassembler.clear_paf();
-    }
-
-    tel.log_internal_event(SESSION_EVENT_CLEAR);
 }
 
 void TcpSession::update_perf_base_state(char newState)
@@ -321,56 +287,6 @@ void TcpSession::check_flow_missed_3whs()
     server.normalizer.init(StreamPolicy::MISSED_3WHS, this, &server, &client);
 }
 
-void TcpSession::update_stream_order(const TcpSegmentDescriptor& tsd, bool aligned)
-{
-    TcpStreamTracker* listener = tsd.get_listener();
-    uint32_t seq = tsd.get_seq();
-
-    switch ( listener->order )
-    {
-        case TcpStreamTracker::IN_SEQUENCE:
-            if ( aligned )
-                tsd.set_packet_flags(PKT_STREAM_ORDER_OK);
-            else if ( SEQ_GT(seq, listener->rcv_nxt) )
-            {
-                listener->order = TcpStreamTracker::NONE;
-                           listener->hole_left_edge = listener->rcv_nxt;
-                           listener->hole_right_edge = seq - 1;
-            }
-            break;
-
-        case TcpStreamTracker::NONE:
-            if ( aligned )
-            {
-                tsd.set_packet_flags(PKT_STREAM_ORDER_OK);
-                if ( SEQ_GT(tsd.get_end_seq(), listener->hole_right_edge) )
-                               listener->order = TcpStreamTracker::OUT_OF_SEQUENCE;
-                           else
-                               listener->hole_left_edge = tsd.get_end_seq();
-            }
-            else
-            {
-                if ( SEQ_LEQ(seq, listener->hole_right_edge) )
-                           {
-                               if ( SEQ_GT(seq, listener->hole_left_edge) )
-                                   listener->hole_right_edge = seq - 1;
-                    else if ( SEQ_GT(tsd.get_end_seq(), listener->hole_left_edge) )
-                    {
-                        listener->hole_left_edge = tsd.get_end_seq();
-                        tsd.set_packet_flags(PKT_STREAM_ORDER_OK);
-                    }
-                           }
-                // accounting for overlaps when not aligned
-                if ( SEQ_GT(listener->hole_left_edge, listener->hole_right_edge) )
-                    listener->order = TcpStreamTracker::OUT_OF_SEQUENCE;
-            }
-            break;
-
-        case TcpStreamTracker::OUT_OF_SEQUENCE:
-            tsd.set_packet_flags(PKT_STREAM_ORDER_BAD);
-    }
-}
-
 void TcpSession::set_os_policy()
 {
     StreamPolicy client_os_policy = flow->ssn_policy ?
@@ -388,8 +304,8 @@ void TcpSession::set_os_policy()
         server_os_policy = StreamPolicy::OS_FIRST;
     }
 
-    client.reassembler.init(this, &client, client_os_policy, false);
-    server.reassembler.init(this, &server, server_os_policy, true);
+    client.seglist.init(this, &client, client_os_policy);
+    server.seglist.init(this, &server, server_os_policy);
 }
 
 // FIXIT-M this is no longer called (but should be)
@@ -562,7 +478,7 @@ void TcpSession::handle_data_on_syn(TcpSegmentDescriptor& tsd)
     }
 }
 
-void TcpSession::update_session_on_rst(TcpSegmentDescriptor& tsd, bool flush)
+void TcpSession::update_session_on_rst(const TcpSegmentDescriptor& tsd, bool flush)
 {
     Packet* p = tsd.get_pkt();
 
@@ -676,35 +592,10 @@ void TcpSession::mark_packet_for_drop(TcpSegmentDescriptor& tsd)
     set_pkt_action_flag(ACTION_BAD_PKT);
 }
 
-int32_t TcpSession::kickstart_asymmetric_flow(const TcpSegmentDescriptor& tsd, TcpStreamTracker* listener)
-{
-    listener->reassembler.reset_asymmetric_flow_reassembly();
-    listener->reassembler.flush_on_data_policy(tsd.get_pkt());
-
-    int32_t space_left =
-        tcp_config->max_queued_bytes - listener->reassembler.get_seg_bytes_total();
-
-    if ( listener->get_tcp_state() == TcpStreamTracker::TCP_MID_STREAM_RECV )
-    {
-        listener->set_tcp_state(TcpStreamTracker::TCP_ESTABLISHED);
-        if (PacketTracer::is_active())
-            PacketTracer::log("Stream: Kickstart of midstream asymmetric flow! Seglist queue space: %u\n",
-                space_left );
-    }
-    else
-    {
-        if (PacketTracer::is_active())
-            PacketTracer::log("Stream: Kickstart of asymmetric flow! Seglist queue space: %u\n",
-                space_left );
-    }
-
-    return space_left;
-}
-
 bool TcpSession::check_reassembly_queue_thresholds(TcpSegmentDescriptor& tsd, TcpStreamTracker* listener)
 {
     // if this packet fits within the current queue limit window then it's good
-    if( listener->reassembler.segment_within_seglist_window(tsd) )
+    if( listener->seglist.segment_within_seglist_window(tsd) )
         return false;
 
     bool inline_mode = tsd.is_nap_policy_inline();
@@ -712,7 +603,7 @@ bool TcpSession::check_reassembly_queue_thresholds(TcpSegmentDescriptor& tsd, Tc
     if ( tcp_config->max_queued_bytes )
     {
         int32_t space_left =
-            tcp_config->max_queued_bytes - listener->reassembler.get_seg_bytes_total();
+            tcp_config->max_queued_bytes - listener->seglist.get_seg_bytes_total();
 
         if ( space_left < (int32_t)tsd.get_len() )
         {
@@ -723,7 +614,7 @@ bool TcpSession::check_reassembly_queue_thresholds(TcpSegmentDescriptor& tsd, Tc
             // and flush to free up seglist space
             if ( tsd.is_ips_policy_inline()  && !tsd.get_pkt()->flow->two_way_traffic() )
             {
-                space_left = kickstart_asymmetric_flow(tsd, listener);
+                space_left = listener->kickstart_asymmetric_flow(tsd, tcp_config->max_queued_bytes);
                 if ( space_left >= (int32_t)tsd.get_len() )
                     return false;
             }
@@ -750,7 +641,7 @@ bool TcpSession::check_reassembly_queue_thresholds(TcpSegmentDescriptor& tsd, Tc
 
     if ( tcp_config->max_queued_segs )
     {
-        if ( listener->reassembler.get_seg_count() + 1 > tcp_config->max_queued_segs )
+        if ( listener->seglist.get_seg_count() + 1 > tcp_config->max_queued_segs )
         {
             tcpStats.exceeded_max_segs++;
 
@@ -758,8 +649,8 @@ bool TcpSession::check_reassembly_queue_thresholds(TcpSegmentDescriptor& tsd, Tc
             // and flush to free up seglist space
             if ( tsd.is_ips_policy_inline() && !tsd.get_pkt()->flow->two_way_traffic() )
             {
-                kickstart_asymmetric_flow(tsd, listener);
-                if ( listener->reassembler.get_seg_count() + 1 <= tcp_config->max_queued_segs )
+                listener->kickstart_asymmetric_flow(tsd, tcp_config->max_queued_bytes);
+                if ( listener->seglist.get_seg_count() + 1 <= tcp_config->max_queued_segs )
                     return false;
             }
 
@@ -816,32 +707,33 @@ void TcpSession::handle_data_segment(TcpSegmentDescriptor& tsd, bool flush)
         else
             server.set_tcp_options_len(tcp_options_len);
 
-        bool stream_is_inorder = ( tsd.get_seq() == listener->rcv_nxt );
+        tsd.set_packet_inorder(tsd.get_seq() == listener->rcv_nxt );
 
-        int rc =  listener->normalizer.apply_normalizations(tsd, tsd.get_seq(), stream_is_inorder);
+        int rc =  listener->normalizer.apply_normalizations(tsd, tsd.get_seq(), tsd.is_packet_inorder());
         switch ( rc )
         {
         case TcpNormalizer::NORM_OK:
-            if ( stream_is_inorder )
-                listener->rcv_nxt = tsd.get_end_seq();
-
-            update_stream_order(tsd, stream_is_inorder);
             check_small_segment_threshold(tsd, listener);
 
             // don't queue data if we are ignoring or queue thresholds are exceeded
             if ( filter_packet_for_reassembly(tsd, listener) )
             {
                 set_packet_header_foo(tsd);
-                listener->reassembler.queue_packet_for_reassembly(tsd);
+                listener->seglist.queue_reassembly_segment(tsd);
 
                 // Alert if overlap limit exceeded
                 if ( (tcp_config->overlap_limit)
-                    && (listener->reassembler.get_overlap_count() > tcp_config->overlap_limit) )
+                    && (listener->seglist.get_overlap_count() > tcp_config->overlap_limit) )
                 {
                     tel.set_tcp_event(EVENT_EXCESSIVE_OVERLAP);
-                    listener->reassembler.set_overlap_count(0);
+                    listener->seglist.set_overlap_count(0);
                 }
             }
+            else if ( tsd.is_packet_inorder() )
+                listener->set_rcv_nxt(tsd.get_end_seq());
+
+            listener->update_stream_order(tsd, tsd.is_packet_inorder());
+
             break;
 
         case TcpNormalizer::NORM_TRIMMED:
@@ -853,22 +745,21 @@ void TcpSession::handle_data_segment(TcpSegmentDescriptor& tsd, bool flush)
         default:
             assert(false);
             break;
-
         }
     }
 
     if ( flush )
-        listener->reassembler.flush_on_data_policy(tsd.get_pkt());
+        listener->eval_flush_policy_on_data(tsd.get_pkt());
     else
-        listener->reassembler.initialize_paf();
+        listener->reassembler->initialize_paf();
 }
 
-TcpStreamTracker::TcpState TcpSession::get_talker_state(TcpSegmentDescriptor& tsd)
+TcpStreamTracker::TcpState TcpSession::get_talker_state(const TcpSegmentDescriptor& tsd)
 {
     return tsd.get_talker()->get_tcp_state();
 }
 
-TcpStreamTracker::TcpState TcpSession::get_listener_state(TcpSegmentDescriptor& tsd)
+TcpStreamTracker::TcpState TcpSession::get_listener_state(const TcpSegmentDescriptor& tsd)
 {
     return tsd.get_listener()->get_tcp_state();
 }
@@ -909,8 +800,8 @@ void TcpSession::flush_server(Packet* p)
         return; // We'll check & clear the TF_FORCE_FLUSH next time through
 
     // Need to convert the addresses to network order
-    if ( server.reassembler.flush_stream(p, PKT_FROM_SERVER) )
-        server.reassembler.purge_flushed_ackd();
+    if ( server.reassembler->flush_stream(p, PKT_FROM_SERVER) )
+        server.reassembler->purge_flushed_ackd();
 
     server.clear_tf_flags(TF_FORCE_FLUSH);
 }
@@ -926,8 +817,8 @@ void TcpSession::flush_client(Packet* p)
     if ( p->packet_flags & PKT_REBUILT_STREAM )
         return;         // TF_FORCE_FLUSH checked & cleared next time through
 
-    if ( client.reassembler.flush_stream(p, PKT_FROM_CLIENT) )
-        client.reassembler.purge_flushed_ackd();
+    if ( client.reassembler->flush_stream(p, PKT_FROM_CLIENT) )
+        client.reassembler->purge_flushed_ackd();
 
     client.clear_tf_flags(TF_FORCE_FLUSH);
 }
@@ -939,8 +830,8 @@ void TcpSession::flush_tracker(
          return;
 
      tracker.set_tf_flags(TF_FORCE_FLUSH);
-     if ( tracker.reassembler.flush_stream(p, dir, final_flush) )
-         tracker.reassembler.purge_flushed_ackd();
+     if ( tracker.reassembler->flush_stream(p, dir, final_flush) )
+         tracker.reassembler->purge_flushed_ackd();
 
      tracker.clear_tf_flags(TF_FORCE_FLUSH);
 }
@@ -970,21 +861,19 @@ void TcpSession::flush()
     if ( !tcp_init )
         return;
 
-    //FIXIT-L Cleanup tcp_init and lws_init as they have some side effect in TcpSession::clear_session
-    lws_init = false;
+    //FIXIT-L Cleanup tcp_init has some side effect in TcpSession::clear_session
     tcp_init = false;
 
-    client.reassembler.flush_queued_segments(flow, true);
-    server.reassembler.flush_queued_segments(flow, true);
+    client.reassembler->flush_queued_segments(flow, true);
+    server.reassembler->flush_queued_segments(flow, true);
 
-    lws_init = true;
     tcp_init = true;
 }
 
 void TcpSession::set_extra_data(Packet* p, uint32_t xid)
 {
     TcpStreamTracker& st = p->ptrs.ip_api.get_src()->equals(flow->client_ip) ? server : client;
-    st.reassembler.set_xtradata_mask(st.reassembler.get_xtradata_mask() | BIT(xid));
+    st.tcp_alerts.set_xtradata_mask(st.tcp_alerts.get_xtradata_mask() | BIT(xid));
 }
 
 static inline void set_window_scale(TcpSegmentDescriptor& tsd)
@@ -1032,10 +921,10 @@ bool TcpSession::ignore_this_packet(Packet* p)
     if ( Stream::blocked_flow(p) )
         return true;
 
-    if ( flow->ssn_state.ignore_direction != SSN_DIR_NONE )
+    if ( flow->ssn_state.ignore_direction == SSN_DIR_BOTH )
     {
-        server.set_flush_policy(STREAM_FLPOLICY_IGNORE);
-        client.set_flush_policy(STREAM_FLPOLICY_IGNORE);
+        server.set_splitter((StreamSplitter*)nullptr);
+        client.set_splitter((StreamSplitter*)nullptr);
         return true;
     }
 
@@ -1077,16 +966,12 @@ void TcpSession::init_tcp_packet_analysis(TcpSegmentDescriptor& tsd)
             client.set_splitter(tsd.get_flow());
             server.set_splitter(tsd.get_flow());
 
-            client.init_flush_policy();
-            server.init_flush_policy();
-
             if ( tsd.is_packet_from_client() ) // Important if the 3-way handshake's ACK contains data
                 flow->set_session_flags(SSNFLAG_SEEN_CLIENT);
             else
                 flow->set_session_flags(SSNFLAG_SEEN_SERVER);
 
             check_flow_missed_3whs();
-
             set_no_ack(tcp_config->no_ack);
         }
 
@@ -1181,3 +1066,358 @@ int TcpSession::process(Packet* p)
 
     return process_tcp_packet(tsd, p);
 }
+
+void TcpSession::init_new_tcp_session(TcpSegmentDescriptor& tsd)
+{
+    Packet* p = tsd.get_pkt();
+
+    flow->pkt_type = p->type();
+    flow->ip_proto = (uint8_t)p->get_ip_proto_next();
+
+    /* New session, previous was marked as reset.  Clear the reset flag. */
+    flow->clear_session_flags(SSNFLAG_RESET);
+
+    flow->set_expire(p, flow->default_session_timeout);
+
+    update_perf_base_state(TcpStreamTracker::TCP_SYN_SENT);
+
+    tcp_init = true;
+}
+
+void TcpSession::update_session_on_server_packet(TcpSegmentDescriptor& tsd)
+{
+    flow->set_session_flags(SSNFLAG_SEEN_SERVER);
+    tsd.set_talker(server);
+    tsd.set_listener(client);
+
+    if ( !flow->inner_server_ttl && !tsd.is_meta_ack_packet() )
+        flow->set_ttl(tsd.get_pkt(), false);
+}
+
+void TcpSession::update_session_on_client_packet(TcpSegmentDescriptor& tsd)
+{
+    /* if we got here we have seen the SYN already... */
+    flow->set_session_flags(SSNFLAG_SEEN_CLIENT);
+    tsd.set_talker(client);
+    tsd.set_listener(server);
+
+    if ( !flow->inner_client_ttl && !tsd.is_meta_ack_packet() )
+        flow->set_ttl(tsd.get_pkt(), true);
+}
+
+bool TcpSession::can_set_no_ack()
+{
+    return ( server.get_flush_policy() == STREAM_FLPOLICY_ON_DATA and
+         client.get_flush_policy() == STREAM_FLPOLICY_ON_DATA );
+}
+
+bool TcpSession::set_no_ack(bool b)
+{
+    if ( can_set_no_ack() )
+    {
+        no_ack = b;
+        return true;
+    }
+    else
+        return false;
+}
+
+void TcpSession::disable_reassembly(Flow* f)
+{
+    client.disable_reassembly(f);
+    server.disable_reassembly(f);
+}
+
+bool TcpSession::is_sequenced(uint8_t dir) const
+{
+    if ( dir & SSN_DIR_FROM_CLIENT )
+    {
+        if ( server.get_tf_flags() & ( TF_MISSING_PREV_PKT | TF_PKT_MISSED ) )
+            return false;
+    }
+
+    if ( dir & SSN_DIR_FROM_SERVER )
+    {
+        if ( client.get_tf_flags() & ( TF_MISSING_PREV_PKT | TF_PKT_MISSED ) )
+            return false;
+    }
+
+    return true;
+}
+
+/* This will falsely return SSN_MISSING_BEFORE on the first reassembled
+ * packet if reassembly for this direction was set mid-session */
+uint8_t TcpSession::missing_in_reassembled(uint8_t dir) const
+{
+    if ( dir & SSN_DIR_FROM_CLIENT )
+    {
+        if ( (server.get_tf_flags() & TF_MISSING_PKT)
+            && (server.get_tf_flags() & TF_MISSING_PREV_PKT) )
+            return SSN_MISSING_BOTH;
+        else if ( server.get_tf_flags() & TF_MISSING_PREV_PKT )
+            return SSN_MISSING_BEFORE;
+        else if ( server.get_tf_flags() & TF_MISSING_PKT )
+            return SSN_MISSING_AFTER;
+    }
+    else if ( dir & SSN_DIR_FROM_SERVER )
+    {
+        if ( (client.get_tf_flags() & TF_MISSING_PKT)
+            && (client.get_tf_flags() & TF_MISSING_PREV_PKT) )
+            return SSN_MISSING_BOTH;
+        else if ( client.get_tf_flags() & TF_MISSING_PREV_PKT )
+            return SSN_MISSING_BEFORE;
+        else if ( client.get_tf_flags() & TF_MISSING_PKT )
+            return SSN_MISSING_AFTER;
+    }
+
+    return SSN_MISSING_NONE;
+}
+
+bool TcpSession::are_packets_missing(uint8_t dir) const
+{
+    if ( dir & SSN_DIR_FROM_CLIENT )
+    {
+        if ( server.get_tf_flags() & TF_PKT_MISSED )
+            return true;
+    }
+
+    if ( dir & SSN_DIR_FROM_SERVER )
+    {
+        if ( client.get_tf_flags() & TF_PKT_MISSED )
+            return true;
+    }
+
+    return false;
+}
+
+bool TcpSession::are_client_segments_queued() const
+{
+    return client.seglist.is_segment_pending_flush();
+}
+
+bool TcpSession::add_alert(Packet* p, uint32_t gid, uint32_t sid)
+{
+    TcpStreamTracker& trk = p->ptrs.ip_api.get_src()->equals(flow->client_ip) ?
+        server : client;
+
+    return trk.tcp_alerts.add_alert(gid, sid);
+}
+
+bool TcpSession::check_alerted(Packet* p, uint32_t gid, uint32_t sid)
+{
+    // only check for alert on wire packet, skip this when processing a rebuilt packet
+    if ( !(p->packet_flags & PKT_REBUILT_STREAM) )
+        return false;
+
+    TcpStreamTracker& trk = p->ptrs.ip_api.get_src()->equals(flow->client_ip) ?
+          server : client;
+
+    return trk.tcp_alerts.check_alerted(gid, sid);
+}
+
+int TcpSession::update_alert(Packet* p, uint32_t gid, uint32_t sid,
+    uint32_t event_id, uint32_t event_second)
+{
+      TcpStreamTracker& trk = p->ptrs.ip_api.get_src()->equals(flow->client_ip) ?
+            server : client;
+
+    return trk.tcp_alerts.update_alert(gid, sid, event_id, event_second);
+}
+
+bool TcpSession::set_packet_action_to_hold(Packet* p)
+{
+    if ( p->is_from_client() )
+    {
+        held_packet_dir = SSN_DIR_FROM_CLIENT;
+        return server.set_held_packet(p);
+    }
+    else
+    {
+        held_packet_dir = SSN_DIR_FROM_SERVER;
+        return client.set_held_packet(p);
+    }
+}
+
+void TcpSession::set_packet_header_foo(const TcpSegmentDescriptor& tsd)
+{
+    const Packet* p = tsd.get_pkt();
+
+    if ( tsd.is_packet_from_client() || (p->pkth->egress_index == DAQ_PKTHDR_UNKNOWN
+         && p->pkth->egress_group == DAQ_PKTHDR_UNKNOWN) )
+    {
+        ingress_index = p->pkth->ingress_index;
+        ingress_group = p->pkth->ingress_group;
+        // ssn egress may be unknown, but will be correct
+        egress_index = p->pkth->egress_index;
+        egress_group = p->pkth->egress_group;
+    }
+    else
+    {
+        egress_index = p->pkth->ingress_index;
+        egress_group = p->pkth->ingress_group;
+        ingress_index = p->pkth->egress_index;
+        ingress_group = p->pkth->egress_group;
+    }
+
+    daq_flags = p->pkth->flags;
+    address_space_id = p->pkth->address_space_id;
+}
+
+void TcpSession::get_packet_header_foo(DAQ_PktHdr_t* pkth, const DAQ_PktHdr_t* orig, uint32_t dir)
+{
+    if ( (dir & PKT_FROM_CLIENT) || (egress_index == DAQ_PKTHDR_UNKNOWN &&
+         egress_group == DAQ_PKTHDR_UNKNOWN) )
+    {
+        pkth->ingress_index = ingress_index;
+        pkth->ingress_group = ingress_group;
+        pkth->egress_index = egress_index;
+        pkth->egress_group = egress_group;
+    }
+    else
+    {
+        pkth->ingress_index = egress_index;
+        pkth->ingress_group = egress_group;
+        pkth->egress_index = ingress_index;
+        pkth->egress_group = ingress_group;
+    }
+    pkth->opaque = 0;
+    pkth->flags = daq_flags;
+    pkth->address_space_id = address_space_id;
+    pkth->tenant_id = orig->tenant_id;
+}
+
+void TcpSession::reset()
+{
+    if ( tcp_init )
+        clear_session(true, false, false );
+}
+
+void TcpSession::cleanup(Packet* p)
+{
+    if ( cleaning )
+        return;
+
+    cleaning = true;
+    clear_session(true, true, false, p);
+    client.reset();
+    server.reset();
+    cleaning = false;
+}
+
+void TcpSession::clear()
+{
+    if ( tcp_init )
+        clear_session( true, false, false );
+
+    TcpHAManager::process_deletion(*flow);
+}
+
+void TcpSession::set_splitter(bool to_server, StreamSplitter* ss)
+{
+    TcpStreamTracker& trk = ( to_server ) ? server : client;
+
+    trk.set_splitter(ss);
+}
+
+uint16_t TcpSession::get_mss(bool to_server) const
+{
+    const TcpStreamTracker& trk = (to_server) ? client : server;
+
+    return trk.get_mss();
+}
+
+uint8_t TcpSession::get_tcp_options_len(bool to_server) const
+{
+    const TcpStreamTracker& trk = (to_server) ? client : server;
+
+    return trk.get_tcp_options_len();
+}
+
+StreamSplitter* TcpSession::get_splitter(bool to_server)
+{
+    if ( to_server )
+        return server.get_splitter();
+    else
+        return client.get_splitter();
+}
+
+void TcpSession::start_proxy()
+{
+    if ( PacketTracer::is_active() )
+        PacketTracer::log("Stream TCP normalization policy set to Proxy mode. Normalizations will be skipped\n");
+
+    tcp_config->policy = StreamPolicy::OS_PROXY;
+    client.normalizer.init(tcp_config->policy, this, &client, &server);
+    server.normalizer.init(tcp_config->policy, this, &server, &client);
+    ++tcpStats.proxy_mode_flows;
+}
+
+void TcpSession::set_established(const TcpSegmentDescriptor& tsd)
+{
+    update_perf_base_state(TcpStreamTracker::TCP_ESTABLISHED);
+    flow->session_state |= STREAM_STATE_ESTABLISHED;
+    flow->set_idle_timeout(this->tcp_config->idle_timeout);
+    if (SSNFLAG_ESTABLISHED != (SSNFLAG_ESTABLISHED & flow->get_session_flags()))
+    {
+        flow->set_session_flags(SSNFLAG_ESTABLISHED);
+        // Only send 1 event
+        if (SSNFLAG_TCP_PSEUDO_EST != (SSNFLAG_TCP_PSEUDO_EST & flow->get_session_flags()))
+            DataBus::publish(Stream::get_pub_id(), StreamEventIds::TCP_ESTABLISHED, tsd.get_pkt());
+    }
+}
+
+void TcpSession::set_pseudo_established(Packet* p)
+{
+    p->flow->ssn_state.session_flags |= SSNFLAG_TCP_PSEUDO_EST;
+    DataBus::publish(Stream::get_pub_id(), StreamEventIds::TCP_ESTABLISHED, p);
+}
+
+bool TcpSession::check_for_one_sided_session(Packet* p)
+{
+    Flow& flow = *p->flow;
+    if ( 0 == ( (SSNFLAG_ESTABLISHED | SSNFLAG_TCP_PSEUDO_EST) & flow.ssn_state.session_flags )
+        && p->is_from_client_originally() )
+    {
+        uint64_t initiator_packets;
+        uint64_t responder_packets;
+        if (flow.flags.client_initiated)
+        {
+            initiator_packets = flow.flowstats.client_pkts;
+            responder_packets = flow.flowstats.server_pkts;
+        }
+        else
+        {
+            initiator_packets = flow.flowstats.server_pkts;
+            responder_packets = flow.flowstats.client_pkts;
+        }
+
+        if ( !responder_packets )
+        {
+            // handle case where traffic is only in one direction, but the sequence numbers
+            // are changing indicating an asynchronous session
+            uint32_t watermark = p->ptrs.tcph->seq() + p->ptrs.tcph->ack();
+            if ( 1 == initiator_packets )
+                initiator_watermark = watermark;
+            else if ( initiator_watermark != watermark )
+            {
+                set_pseudo_established(p);
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+void TcpSession::check_for_pseudo_established(Packet* p)
+{
+    Flow& flow = *p->flow;
+    if ( 0 == ( (SSNFLAG_ESTABLISHED | SSNFLAG_TCP_PSEUDO_EST) & flow.ssn_state.session_flags ) )
+    {
+        if ( check_for_one_sided_session(p) )
+            return;
+        if ( 0 < flow.flowstats.client_pkts && 0 < flow.flowstats.server_pkts )
+            set_pseudo_established(p);
+    }
+}
+
+
index 8cd7ce9fefaa96011b3d5f60cfeb41fc295854e5..42a6989842b0296722ab20c00eba4e98d06eb3b7 100644 (file)
 #ifndef TCP_SESSION_H
 #define TCP_SESSION_H
 
+#include "flow/flow.h"
+#include "flow/session.h"
+#include "protocols/packet.h"
+
+#include "tcp_event_logger.h"
 #include "tcp_state_machine.h"
-#include "tcp_stream_session.h"
+#include "tcp_stream_config.h"
 #include "tcp_stream_tracker.h"
 
 namespace snort
@@ -29,10 +34,10 @@ namespace snort
 class Flow;
 struct Packet;
 }
-class TcpEventLogger;
 
+class TcpSegmentDescriptor;
 
-class TcpSession : public TcpStreamSession
+class TcpSession : public Session
 {
 public:
     TcpSession(snort::Flow*);
@@ -46,37 +51,105 @@ public:
     void precheck(snort::Packet* p) override;
     int process(snort::Packet*) override;
 
+    void clear() override;
+    void cleanup(snort::Packet* = nullptr) override;
+
+    void set_splitter(bool, snort::StreamSplitter*) override;
+    snort::StreamSplitter* get_splitter(bool) override;
+
+    void disable_reassembly(snort::Flow*) override;
+    uint8_t missing_in_reassembled(uint8_t dir) const override;
+    bool are_client_segments_queued() const override;
+    bool is_sequenced(uint8_t dir) const override;
+    bool are_packets_missing(uint8_t dir) const override;
+    bool set_packet_action_to_hold(snort::Packet*) override;
+
+    bool add_alert(snort::Packet*, uint32_t gid, uint32_t sid) override;
+    bool check_alerted(snort::Packet*, uint32_t gid, uint32_t sid) override;
+    int update_alert(snort::Packet*, uint32_t gid, uint32_t sid,
+        uint32_t event_id, uint32_t event_second) override;
+    void set_extra_data(snort::Packet*, uint32_t /*flag*/) override;
+
     void flush() override;
     void flush_client(snort::Packet*) override;
     void flush_server(snort::Packet*) override;
     void flush_talker(snort::Packet*, bool final_flush = false) override;
     void flush_listener(snort::Packet*, bool final_flush = false) override;
     // cppcheck-suppress virtualCallInConstructor
-    void clear_session(bool free_flow_data, bool flush_segments, bool restart, snort::Packet* p = nullptr) override;
-    void set_extra_data(snort::Packet*, uint32_t /*flag*/) override;
-    void update_perf_base_state(char new_state) override;
-    TcpStreamTracker::TcpState get_talker_state(TcpSegmentDescriptor& tsd) override;
-    TcpStreamTracker::TcpState get_listener_state(TcpSegmentDescriptor& tsd) override;
-    void update_timestamp_tracking(TcpSegmentDescriptor&) override;
-    void update_session_on_rst(TcpSegmentDescriptor&, bool) override;
-    bool handle_syn_on_reset_session(TcpSegmentDescriptor&) override;
-    void handle_data_on_syn(TcpSegmentDescriptor&) override;
-    void update_ignored_session(TcpSegmentDescriptor&) override;
-    void update_paws_timestamps(TcpSegmentDescriptor&) override;
-    void check_for_repeated_syn(TcpSegmentDescriptor&) override;
-    void check_for_session_hijack(TcpSegmentDescriptor&) override;
-    bool check_for_window_slam(TcpSegmentDescriptor& tsd) override;
-    void mark_packet_for_drop(TcpSegmentDescriptor&) override;
-    void handle_data_segment(TcpSegmentDescriptor&, bool flush = true);
-    bool validate_packet_established_session(TcpSegmentDescriptor&) override;
+
+    void reset();
+    void start_proxy();
+    void clear_session(bool free_flow_data, bool flush_segments, bool restart, snort::Packet* p = nullptr);
+    TcpStreamTracker::TcpState get_talker_state(const TcpSegmentDescriptor& tsd);
+    TcpStreamTracker::TcpState get_listener_state(const TcpSegmentDescriptor& tsd);
+    TcpStreamTracker::TcpState get_peer_state(const TcpStreamTracker* me)
+    { return me == &client ? server.get_tcp_state() : client.get_tcp_state(); }
+
+    void init_new_tcp_session(TcpSegmentDescriptor&);
+    void update_perf_base_state(char new_state);
+    void update_timestamp_tracking(TcpSegmentDescriptor&);
+    void update_paws_timestamps(TcpSegmentDescriptor&);
+    void update_session_on_rst(const TcpSegmentDescriptor&, bool);
+    bool handle_syn_on_reset_session(TcpSegmentDescriptor&);
+    void handle_data_on_syn(TcpSegmentDescriptor&);
+    void update_ignored_session(TcpSegmentDescriptor&);
 
     bool is_midstream_allowed(const TcpSegmentDescriptor& tsd)
     { return tcp_config->midstream_allowed(tsd.get_pkt()); }
 
+    uint16_t get_mss(bool to_server) const;
+    uint8_t get_tcp_options_len(bool to_server) const;
+
+    void get_packet_header_foo(DAQ_PktHdr_t*, const DAQ_PktHdr_t* orig, uint32_t dir);
+    bool can_set_no_ack();
+    bool set_no_ack(bool);
+    bool no_ack_mode_enabled() { return no_ack; }
+
+    void generate_no_3whs_event()
+    {
+        if ( generate_3whs_alert && flow->two_way_traffic())
+        {
+            tel.set_tcp_event(EVENT_NO_3WHS);
+            generate_3whs_alert = false;
+        }
+    }
+
+    void set_pkt_action_flag(uint32_t flag)
+    { pkt_action_mask |= flag; }
+
+    void set_established(const TcpSegmentDescriptor&);
+    void set_pseudo_established(snort::Packet*);
+    void check_for_pseudo_established(snort::Packet*);
+    bool check_for_one_sided_session(snort::Packet*);
+
+    void check_for_repeated_syn(TcpSegmentDescriptor&);
+    void check_for_session_hijack(TcpSegmentDescriptor&);
+    bool check_for_window_slam(TcpSegmentDescriptor& tsd);
+    void mark_packet_for_drop(TcpSegmentDescriptor&);
+    void handle_data_segment(TcpSegmentDescriptor&, bool flush = true);
+    bool validate_packet_established_session(TcpSegmentDescriptor&);
+
+    TcpStreamTracker client;
+    TcpStreamTracker server;
+    TcpStreamConfig* tcp_config = nullptr;
+    TcpEventLogger tel;
+    bool tcp_init = false;
+    uint32_t pkt_action_mask = ACTION_NOTHING;
+    uint32_t initiator_watermark = 0;
+    int32_t ingress_index = DAQ_PKTHDR_UNKNOWN;
+    int32_t egress_index = DAQ_PKTHDR_UNKNOWN;
+    int16_t ingress_group = DAQ_PKTHDR_UNKNOWN;
+    int16_t egress_group = DAQ_PKTHDR_UNKNOWN;
+    uint32_t daq_flags = 0;
+    uint32_t address_space_id = 0;
+    bool generate_3whs_alert = true;
+    bool cleaning = false;
+    uint8_t held_packet_dir = SSN_DIR_NONE;
+    uint8_t ecn = 0;
+
 private:
     int process_tcp_packet(TcpSegmentDescriptor&, const snort::Packet*);
-    void set_os_policy() override;
-    void update_stream_order(const TcpSegmentDescriptor&, bool aligned);
+    void set_os_policy();
     void swap_trackers();
     void init_session_on_syn(TcpSegmentDescriptor&);
     void init_session_on_synack(TcpSegmentDescriptor&);
@@ -89,12 +162,15 @@ private:
     bool check_reassembly_queue_thresholds(TcpSegmentDescriptor&, TcpStreamTracker*);
     bool filter_packet_for_reassembly(TcpSegmentDescriptor&, TcpStreamTracker*);
     void check_small_segment_threshold(const TcpSegmentDescriptor&, TcpStreamTracker*);
-    int32_t kickstart_asymmetric_flow(const TcpSegmentDescriptor&, TcpStreamTracker*);
     void check_flow_missed_3whs();
 
-private:
+    void set_packet_header_foo(const TcpSegmentDescriptor&);
+    void update_session_on_server_packet(TcpSegmentDescriptor&);
+    void update_session_on_client_packet(TcpSegmentDescriptor&);
+
     TcpStateMachine* tsm;
-    bool splitter_init;
+    bool splitter_init = false;
+    bool no_ack = false;
 };
 
 #endif
index 0c0f33597193a191ef2027ab96a084e98ed2a0a0..1b863681c64aedceafd5a4c871de23940b9ab67d 100644 (file)
 
 #include "tcp_session.h"
 
-#ifdef UNIT_TEST
-#include "catch/snort_catch.h"
-#endif
-
 using namespace snort;
 
 TcpStateClosed::TcpStateClosed(TcpStateMachine& tsm) :
index 0a9f4438910d3597a0b39eb4a215b0612b43e205..83543836447f107954292040e05a1b288f1c0b91 100644 (file)
 
 #include "tcp_state_machine.h"
 
-#ifdef UNIT_TEST
-#include "catch/snort_catch.h"
-#endif
-
 using namespace std;
 
 TcpStateHandler::TcpStateHandler(TcpStreamTracker::TcpState state, TcpStateMachine& tsm)
index 37e5cae5406a847473a3e3e2f477a33b698a3c47..b510c862025b0422895f1149728123fdd2dd3cc8 100644 (file)
@@ -53,8 +53,8 @@ bool TcpStateMidStreamRecv::syn_ack_sent(TcpSegmentDescriptor& tsd, TcpStreamTra
 {
     if ( trk.normalizer.is_tcp_ips_enabled() )
     {
-        trk.reassembler.skip_midstream_pickup_seglist_hole(tsd);
-        trk.reassembler.flush_on_data_policy(tsd.get_pkt());
+        trk.seglist.skip_midstream_pickup_seglist_hole(tsd);
+        trk.eval_flush_policy_on_data(tsd.get_pkt());
         trk.midstream_initial_ack_flush = true;
     }
 
@@ -67,8 +67,8 @@ bool TcpStateMidStreamRecv::ack_sent(TcpSegmentDescriptor& tsd, TcpStreamTracker
 {
     if ( trk.normalizer.is_tcp_ips_enabled() )
     {
-        trk.reassembler.skip_midstream_pickup_seglist_hole(tsd);
-        trk.reassembler.flush_on_data_policy(tsd.get_pkt());
+        trk.seglist.skip_midstream_pickup_seglist_hole(tsd);
+        trk.eval_flush_policy_on_data(tsd.get_pkt());
         trk.midstream_initial_ack_flush = true;
     }
 
@@ -87,8 +87,8 @@ bool TcpStateMidStreamRecv::data_seg_sent(TcpSegmentDescriptor& tsd, TcpStreamTr
 {
     if ( trk.normalizer.is_tcp_ips_enabled() )
     {
-        trk.reassembler.skip_midstream_pickup_seglist_hole(tsd);
-        trk.reassembler.flush_on_data_policy(tsd.get_pkt());
+        trk.seglist.skip_midstream_pickup_seglist_hole(tsd);
+        trk.eval_flush_policy_on_data(tsd.get_pkt());
         trk.midstream_initial_ack_flush = true;
     }
 
@@ -110,8 +110,8 @@ bool TcpStateMidStreamRecv::fin_sent(TcpSegmentDescriptor& tsd, TcpStreamTracker
 {
     if ( trk.normalizer.is_tcp_ips_enabled() )
     {
-        trk.reassembler.skip_midstream_pickup_seglist_hole(tsd);
-        trk.reassembler.flush_on_data_policy(tsd.get_pkt());
+        trk.seglist.skip_midstream_pickup_seglist_hole(tsd);
+        trk.eval_flush_policy_on_data(tsd.get_pkt());
         trk.midstream_initial_ack_flush = true;
     }
 
index e7bda4942fecb799be1ee031e09b9afc525aecb8..4df685f2015a269ae446ebaf18c52f9e26bf993c 100644 (file)
@@ -83,7 +83,7 @@ bool TcpStateMidStreamSent::data_seg_sent(TcpSegmentDescriptor& tsd, TcpStreamTr
 bool TcpStateMidStreamSent::data_seg_recv(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
 {
     trk.update_tracker_ack_recv(tsd);
-    trk.reassembler.set_seglist_base_seq(tsd.get_seq());
+    trk.seglist.set_seglist_base_seq(tsd.get_seq());
     trk.session->handle_data_segment(tsd);
     trk.session->set_established(tsd);
     trk.set_tcp_state(TcpStreamTracker::TCP_ESTABLISHED);
index db5e90f424e5a9e24260d2146d6f3e8795220e2d..4334cca5c2bef0eaac1a171ab146764a567fad24 100644 (file)
@@ -81,6 +81,8 @@ bool TcpStateSynSent::syn_ack_recv(TcpSegmentDescriptor& tsd, TcpStreamTracker&
 bool TcpStateSynSent::ack_sent(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
 {
     trk.update_tracker_ack_sent(tsd);
+    if ( SEQ_GT(tsd.get_ack(), trk.get_rcv_nxt()) )
+        trk.set_rcv_nxt(tsd.get_ack());
     trk.session->update_timestamp_tracking(tsd);
     if ( trk.session->flow->two_way_traffic() )
     {
diff --git a/src/stream/tcp/tcp_stream_session.cc b/src/stream/tcp/tcp_stream_session.cc
deleted file mode 100644 (file)
index 513e9f4..0000000
+++ /dev/null
@@ -1,419 +0,0 @@
-//--------------------------------------------------------------------------
-// Copyright (C) 2015-2024 Cisco and/or its affiliates. All rights reserved.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License Version 2 as published
-// by the Free Software Foundation.  You may not use, modify or distribute
-// this program under any other version of the GNU General Public License.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License along
-// with this program; if not, write to the Free Software Foundation, Inc.,
-// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-//--------------------------------------------------------------------------
-
-// tcp_stream_session.cc author davis mcpherson <davmcphe@cisco.com>
-// Created on: Feb 18, 2016
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "tcp_stream_session.h"
-
-#include "framework/data_bus.h"
-#include "packet_io/packet_tracer.h"
-#include "pub_sub/stream_event_ids.h"
-#include "stream/stream.h"
-#include "stream/tcp/tcp_ha.h"
-
-using namespace snort;
-
-TcpStreamSession::TcpStreamSession(Flow* f)
-    : Session(f), client(true), server(false)
-{ }
-
-TcpStreamSession::~TcpStreamSession() = default;
-
-void TcpStreamSession::init_new_tcp_session(TcpSegmentDescriptor& tsd)
-{
-    Packet* p = tsd.get_pkt();
-
-    flow->pkt_type = p->type();
-    flow->ip_proto = (uint8_t)p->get_ip_proto_next();
-
-    /* New session, previous was marked as reset.  Clear the reset flag. */
-    flow->clear_session_flags(SSNFLAG_RESET);
-
-    flow->set_expire(p, flow->default_session_timeout);
-
-    update_perf_base_state(TcpStreamTracker::TCP_SYN_SENT);
-
-    tcp_init = true;
-    lws_init = true;
-}
-
-void TcpStreamSession::update_session_on_server_packet(TcpSegmentDescriptor& tsd)
-{
-    flow->set_session_flags(SSNFLAG_SEEN_SERVER);
-    tsd.set_talker(server);
-    tsd.set_listener(client);
-
-    if ( !flow->inner_server_ttl && !tsd.is_meta_ack_packet() )
-        flow->set_ttl(tsd.get_pkt(), false);
-}
-
-void TcpStreamSession::update_session_on_client_packet(TcpSegmentDescriptor& tsd)
-{
-    /* if we got here we have seen the SYN already... */
-    flow->set_session_flags(SSNFLAG_SEEN_CLIENT);
-    tsd.set_talker(client);
-    tsd.set_listener(server);
-
-    if ( !flow->inner_client_ttl && !tsd.is_meta_ack_packet() )
-        flow->set_ttl(tsd.get_pkt(), true);
-}
-
-bool TcpStreamSession::can_set_no_ack()
-{
-    return ( server.get_flush_policy() == STREAM_FLPOLICY_ON_DATA and
-         client.get_flush_policy() == STREAM_FLPOLICY_ON_DATA );
-}
-
-bool TcpStreamSession::set_no_ack(bool b)
-{
-    if ( can_set_no_ack() )
-    {
-        no_ack = b;
-        return true;
-    }
-    else
-        return false;
-}
-
-void TcpStreamSession::disable_reassembly(Flow* f)
-{
-    client.set_splitter((StreamSplitter*)nullptr);
-    server.set_splitter((StreamSplitter*)nullptr);
-
-    client.reassembler.purge_segment_list();
-    server.reassembler.purge_segment_list();
-
-    client.set_flush_policy(STREAM_FLPOLICY_IGNORE);
-    server.set_flush_policy(STREAM_FLPOLICY_IGNORE);
-
-    client.finalize_held_packet(f);
-    server.finalize_held_packet(f);
-}
-
-uint8_t TcpStreamSession::get_reassembly_direction() const
-{
-    uint8_t dir = SSN_DIR_NONE;
-
-    if ( server.get_flush_policy() != STREAM_FLPOLICY_IGNORE )
-        dir |= SSN_DIR_FROM_CLIENT;
-
-    if ( client.get_flush_policy() != STREAM_FLPOLICY_IGNORE )
-        dir |= SSN_DIR_FROM_SERVER;
-
-    return dir;
-}
-
-bool TcpStreamSession::is_sequenced(uint8_t dir) const
-{
-    if ( dir & SSN_DIR_FROM_CLIENT )
-    {
-        if ( server.get_tf_flags() & ( TF_MISSING_PREV_PKT | TF_PKT_MISSED ) )
-            return false;
-    }
-
-    if ( dir & SSN_DIR_FROM_SERVER )
-    {
-        if ( client.get_tf_flags() & ( TF_MISSING_PREV_PKT | TF_PKT_MISSED ) )
-            return false;
-    }
-
-    return true;
-}
-
-/* This will falsely return SSN_MISSING_BEFORE on the first reassembled
- * packet if reassembly for this direction was set mid-session */
-uint8_t TcpStreamSession::missing_in_reassembled(uint8_t dir) const
-{
-    if ( dir & SSN_DIR_FROM_CLIENT )
-    {
-        if ( (server.get_tf_flags() & TF_MISSING_PKT)
-            && (server.get_tf_flags() & TF_MISSING_PREV_PKT) )
-            return SSN_MISSING_BOTH;
-        else if ( server.get_tf_flags() & TF_MISSING_PREV_PKT )
-            return SSN_MISSING_BEFORE;
-        else if ( server.get_tf_flags() & TF_MISSING_PKT )
-            return SSN_MISSING_AFTER;
-    }
-    else if ( dir & SSN_DIR_FROM_SERVER )
-    {
-        if ( (client.get_tf_flags() & TF_MISSING_PKT)
-            && (client.get_tf_flags() & TF_MISSING_PREV_PKT) )
-            return SSN_MISSING_BOTH;
-        else if ( client.get_tf_flags() & TF_MISSING_PREV_PKT )
-            return SSN_MISSING_BEFORE;
-        else if ( client.get_tf_flags() & TF_MISSING_PKT )
-            return SSN_MISSING_AFTER;
-    }
-
-    return SSN_MISSING_NONE;
-}
-
-bool TcpStreamSession::are_packets_missing(uint8_t dir) const
-{
-    if ( dir & SSN_DIR_FROM_CLIENT )
-    {
-        if ( server.get_tf_flags() & TF_PKT_MISSED )
-            return true;
-    }
-
-    if ( dir & SSN_DIR_FROM_SERVER )
-    {
-        if ( client.get_tf_flags() & TF_PKT_MISSED )
-            return true;
-    }
-
-    return false;
-}
-
-bool TcpStreamSession::are_client_segments_queued() const
-{
-    return client.reassembler.is_segment_pending_flush();
-}
-
-bool TcpStreamSession::add_alert(Packet* p, uint32_t gid, uint32_t sid)
-{
-    TcpReassemblerPolicy& trp = p->ptrs.ip_api.get_src()->equals(flow->client_ip) ?
-        server.reassembler : client.reassembler;
-
-    return trp.add_alert(gid, sid);
-}
-
-bool TcpStreamSession::check_alerted(Packet* p, uint32_t gid, uint32_t sid)
-{
-    // only check for alert on wire packet if this when processing a rebuilt packet
-    if ( !(p->packet_flags & PKT_REBUILT_STREAM) )
-        return false;
-
-    TcpReassemblerPolicy& trp = p->ptrs.ip_api.get_src()->equals(flow->client_ip) ?
-        server.reassembler : client.reassembler;
-
-    return trp.check_alerted(gid, sid);
-}
-
-int TcpStreamSession::update_alert(Packet* p, uint32_t gid, uint32_t sid,
-    uint32_t event_id, uint32_t event_second)
-{
-    TcpReassemblerPolicy& trp = p->ptrs.ip_api.get_src()->equals(flow->client_ip) ?
-        server.reassembler : client.reassembler;
-
-    return trp.update_alert(gid, sid, event_id, event_second);
-}
-
-bool TcpStreamSession::set_packet_action_to_hold(Packet* p)
-{
-    if ( p->is_from_client() )
-    {
-        held_packet_dir = SSN_DIR_FROM_CLIENT;
-        return server.set_held_packet(p);
-    }
-    else
-    {
-        held_packet_dir = SSN_DIR_FROM_SERVER;
-        return client.set_held_packet(p);
-    }
-}
-
-void TcpStreamSession::set_packet_header_foo(const TcpSegmentDescriptor& tsd)
-{
-    const Packet* p = tsd.get_pkt();
-
-    if ( tsd.is_packet_from_client() || (p->pkth->egress_index == DAQ_PKTHDR_UNKNOWN
-         && p->pkth->egress_group == DAQ_PKTHDR_UNKNOWN) )
-    {
-        ingress_index = p->pkth->ingress_index;
-        ingress_group = p->pkth->ingress_group;
-        // ssn egress may be unknown, but will be correct
-        egress_index = p->pkth->egress_index;
-        egress_group = p->pkth->egress_group;
-    }
-    else
-    {
-        egress_index = p->pkth->ingress_index;
-        egress_group = p->pkth->ingress_group;
-        ingress_index = p->pkth->egress_index;
-        ingress_group = p->pkth->egress_group;
-    }
-
-    daq_flags = p->pkth->flags;
-    address_space_id = p->pkth->address_space_id;
-}
-
-void TcpStreamSession::get_packet_header_foo(DAQ_PktHdr_t* pkth, const DAQ_PktHdr_t* orig, uint32_t dir)
-{
-    if ( (dir & PKT_FROM_CLIENT) || (egress_index == DAQ_PKTHDR_UNKNOWN &&
-         egress_group == DAQ_PKTHDR_UNKNOWN) )
-    {
-        pkth->ingress_index = ingress_index;
-        pkth->ingress_group = ingress_group;
-        pkth->egress_index = egress_index;
-        pkth->egress_group = egress_group;
-    }
-    else
-    {
-        pkth->ingress_index = egress_index;
-        pkth->ingress_group = egress_group;
-        pkth->egress_index = ingress_index;
-        pkth->egress_group = ingress_group;
-    }
-    pkth->opaque = 0;
-    pkth->flags = daq_flags;
-    pkth->address_space_id = address_space_id;
-    pkth->tenant_id = orig->tenant_id;
-}
-
-void TcpStreamSession::reset()
-{
-    if ( tcp_init )
-        clear_session(true, false, false );
-}
-
-void TcpStreamSession::cleanup(Packet* p)
-{
-    if ( cleaning )
-        return;
-
-    cleaning = true;
-    clear_session(true, true, false, p);
-    client.normalizer.reset();
-    server.normalizer.reset();
-    client.reassembler.reset();
-    server.reassembler.reset();
-    cleaning = false;
-}
-
-void TcpStreamSession::clear()
-{
-    if ( tcp_init )
-        clear_session( true, false, false );
-
-    TcpHAManager::process_deletion(*flow);
-}
-
-void TcpStreamSession::set_splitter(bool to_server, StreamSplitter* ss)
-{
-    TcpStreamTracker& trk = ( to_server ) ? server : client;
-
-    trk.set_splitter(ss);
-}
-
-uint16_t TcpStreamSession::get_mss(bool to_server) const
-{
-    const TcpStreamTracker& trk = (to_server) ? client : server;
-
-    return trk.get_mss();
-}
-
-uint8_t TcpStreamSession::get_tcp_options_len(bool to_server) const
-{
-    const TcpStreamTracker& trk = (to_server) ? client : server;
-
-    return trk.get_tcp_options_len();
-}
-
-StreamSplitter* TcpStreamSession::get_splitter(bool to_server)
-{
-    if ( to_server )
-        return server.get_splitter();
-    else
-        return client.get_splitter();
-}
-
-void TcpStreamSession::start_proxy()
-{
-    if ( PacketTracer::is_active() )
-        PacketTracer::log("Stream TCP normalization policy set to Proxy mode.  Normalizations will be skipped\n");
-
-    tcp_config->policy = StreamPolicy::OS_PROXY;
-    client.normalizer.init(tcp_config->policy, this, &client, &server);
-    server.normalizer.init(tcp_config->policy, this, &server, &client);
-    ++tcpStats.proxy_mode_flows;
-}
-
-void TcpStreamSession::set_established(const TcpSegmentDescriptor& tsd)
-{
-    update_perf_base_state(TcpStreamTracker::TCP_ESTABLISHED);
-    flow->session_state |= STREAM_STATE_ESTABLISHED;
-    flow->set_idle_timeout(this->tcp_config->idle_timeout);
-    if (SSNFLAG_ESTABLISHED != (SSNFLAG_ESTABLISHED & flow->get_session_flags()))
-    {
-        flow->set_session_flags(SSNFLAG_ESTABLISHED);
-        // Only send 1 event
-        if (SSNFLAG_TCP_PSEUDO_EST != (SSNFLAG_TCP_PSEUDO_EST & flow->get_session_flags()))
-            DataBus::publish(Stream::get_pub_id(), StreamEventIds::TCP_ESTABLISHED, tsd.get_pkt());
-    }
-}
-
-void TcpStreamSession::set_pseudo_established(Packet* p)
-{
-    p->flow->ssn_state.session_flags |= SSNFLAG_TCP_PSEUDO_EST;
-    DataBus::publish(Stream::get_pub_id(), StreamEventIds::TCP_ESTABLISHED, p);
-}
-
-bool TcpStreamSession::check_for_one_sided_session(Packet* p)
-{
-    Flow& flow = *p->flow;
-    if ( 0 == ( (SSNFLAG_ESTABLISHED | SSNFLAG_TCP_PSEUDO_EST) & flow.ssn_state.session_flags )
-        && p->is_from_client_originally() )
-    {
-        uint64_t initiator_packets;
-        uint64_t responder_packets;
-        if (flow.flags.client_initiated)
-        {
-            initiator_packets = flow.flowstats.client_pkts;
-            responder_packets = flow.flowstats.server_pkts;
-        }
-        else
-        {
-            initiator_packets = flow.flowstats.server_pkts;
-            responder_packets = flow.flowstats.client_pkts;
-        }
-
-        if ( !responder_packets )
-        {
-            // handle case where traffic is only in one direction, but the sequence numbers
-            // are changing indicating an asynchronous session
-            uint32_t watermark = p->ptrs.tcph->seq() + p->ptrs.tcph->ack();
-            if ( 1 == initiator_packets )
-                initiator_watermark = watermark;
-            else if ( initiator_watermark != watermark )
-            {
-                set_pseudo_established(p);
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
-void TcpStreamSession::check_for_pseudo_established(Packet* p)
-{
-    Flow& flow = *p->flow;
-    if ( 0 == ( (SSNFLAG_ESTABLISHED | SSNFLAG_TCP_PSEUDO_EST) & flow.ssn_state.session_flags ) )
-    {
-        if ( check_for_one_sided_session(p) )
-            return;
-        if ( 0 < flow.flowstats.client_pkts && 0 < flow.flowstats.server_pkts )
-            set_pseudo_established(p);
-    }
-}
-
diff --git a/src/stream/tcp/tcp_stream_session.h b/src/stream/tcp/tcp_stream_session.h
deleted file mode 100644 (file)
index 3bba9ac..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-//--------------------------------------------------------------------------
-// Copyright (C) 2015-2024 Cisco and/or its affiliates. All rights reserved.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License Version 2 as published
-// by the Free Software Foundation.  You may not use, modify or distribute
-// this program under any other version of the GNU General Public License.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License along
-// with this program; if not, write to the Free Software Foundation, Inc.,
-// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-//--------------------------------------------------------------------------
-
-// tcp_stream_session.h author davis mcpherson <davmcphe@cisco.com>
-// Created on: Feb 18, 2016
-
-#ifndef TCP_STREAM_SESSION_H
-#define TCP_STREAM_SESSION_H
-
-#include "detection/detection_engine.h"
-#include "flow/session.h"
-#include "protocols/ipv6.h"
-
-#include "tcp_stream_config.h"
-#include "tcp_stream_tracker.h"
-
-// FIXIT-L session tracking could be split from reassembly
-// into a separate module a la ip_session.cc and ip_defrag.cc
-// (of course defrag should also be cleaned up)
-class TcpStreamSession : public Session
-{
-public:
-    ~TcpStreamSession() override;
-
-    void clear() override;
-    void cleanup(snort::Packet* = nullptr) override;
-
-    void set_splitter(bool, snort::StreamSplitter*) override;
-    snort::StreamSplitter* get_splitter(bool) override;
-
-    bool is_sequenced(uint8_t dir) const override;
-    bool are_packets_missing(uint8_t dir) const override;
-
-    void disable_reassembly(snort::Flow*) override;
-    uint8_t get_reassembly_direction() const override;
-    uint8_t missing_in_reassembled(uint8_t dir) const override;
-    bool are_client_segments_queued() const override;
-
-    bool add_alert(snort::Packet*, uint32_t gid, uint32_t sid) override;
-    bool check_alerted(snort::Packet*, uint32_t gid, uint32_t sid) override;
-    int update_alert(snort::Packet*, uint32_t gid, uint32_t sid,
-        uint32_t event_id, uint32_t event_second) override;
-
-    bool set_packet_action_to_hold(snort::Packet*) override;
-
-    uint16_t get_mss(bool to_server) const;
-    uint8_t get_tcp_options_len(bool to_server) const;
-
-    void reset();
-    void start_proxy();
-
-    void set_packet_header_foo(const TcpSegmentDescriptor&);
-    void get_packet_header_foo(DAQ_PktHdr_t*, const DAQ_PktHdr_t* orig, uint32_t dir);
-    bool can_set_no_ack();
-    bool set_no_ack(bool);
-    bool no_ack_mode_enabled() { return no_ack; }
-    virtual void update_perf_base_state(char) = 0;
-    virtual void clear_session(
-        bool free_flow_data, bool flush_segments, bool restart, snort::Packet* p = nullptr) = 0;
-    virtual TcpStreamTracker::TcpState get_talker_state(TcpSegmentDescriptor&) = 0;
-    virtual TcpStreamTracker::TcpState get_listener_state(TcpSegmentDescriptor&) = 0;
-    TcpStreamTracker::TcpState get_peer_state(const TcpStreamTracker* me)
-    { return me == &client ? server.get_tcp_state() : client.get_tcp_state(); }
-
-    virtual void init_new_tcp_session(TcpSegmentDescriptor&);
-    virtual void update_timestamp_tracking(TcpSegmentDescriptor&) = 0;
-    virtual void update_session_on_server_packet(TcpSegmentDescriptor&);
-    virtual void update_session_on_client_packet(TcpSegmentDescriptor&);
-    virtual void update_session_on_rst(TcpSegmentDescriptor&, bool) = 0;
-    virtual bool handle_syn_on_reset_session(TcpSegmentDescriptor&) = 0;
-    virtual void handle_data_on_syn(TcpSegmentDescriptor&) = 0;
-    virtual void update_ignored_session(TcpSegmentDescriptor&) = 0;
-    void generate_no_3whs_event()
-    {
-        if ( generate_3whs_alert && flow->two_way_traffic())
-        {
-            tel.set_tcp_event(EVENT_NO_3WHS);
-            generate_3whs_alert = false;
-        }
-    }
-
-    void set_pkt_action_flag(uint32_t flag)
-    { pkt_action_mask |= flag; }
-
-    void set_established(const TcpSegmentDescriptor&);
-    void set_pseudo_established(snort::Packet*);
-    bool check_for_one_sided_session(snort::Packet*);
-    void check_for_pseudo_established(snort::Packet*);
-
-    virtual void update_paws_timestamps(TcpSegmentDescriptor&) = 0;
-    virtual void check_for_repeated_syn(TcpSegmentDescriptor&) = 0;
-    virtual void check_for_session_hijack(TcpSegmentDescriptor&) = 0;
-    virtual bool check_for_window_slam(TcpSegmentDescriptor&) = 0;
-    virtual void mark_packet_for_drop(TcpSegmentDescriptor&) = 0;
-    virtual bool validate_packet_established_session(TcpSegmentDescriptor&) = 0;
-
-    TcpStreamTracker client;
-    TcpStreamTracker server;
-    bool lws_init = false;
-    bool tcp_init = false;
-    uint32_t pkt_action_mask = ACTION_NOTHING;
-    uint32_t initiator_watermark = 0;
-    int32_t ingress_index = DAQ_PKTHDR_UNKNOWN;
-    int32_t egress_index = DAQ_PKTHDR_UNKNOWN;
-    int16_t ingress_group = DAQ_PKTHDR_UNKNOWN;
-    int16_t egress_group = DAQ_PKTHDR_UNKNOWN;
-    uint32_t daq_flags = 0;
-    uint32_t address_space_id = 0;
-    bool generate_3whs_alert = true;
-    TcpStreamConfig* tcp_config = nullptr;
-    TcpEventLogger tel;
-    bool cleaning = false;
-    uint8_t held_packet_dir = SSN_DIR_NONE;
-    uint8_t ecn = 0;
-
-private:
-    bool no_ack = false;
-
-protected:
-    TcpStreamSession(snort::Flow*);
-    virtual void set_os_policy() = 0;
-};
-
-#endif
-
index 4068a4b27b659689482d5e5f5cfdda9f2ae72fe6..279b9eaad1607464a09e1a541724eeef877d0e2e 100644 (file)
 #include "main/analyzer.h"
 #include "main/snort.h"
 #include "packet_io/active.h"
+#include "packet_io/packet_tracer.h"
 #include "profiler/profiler_defs.h"
 #include "protocols/eth.h"
 #include "pub_sub/stream_event_ids.h"
 #include "stream/stream.h"
 
 #include "held_packet_queue.h"
-#include "segment_overlap_editor.h"
+#include "tcp_overlap_resolver.h"
 #include "tcp_normalizers.h"
-#include "tcp_reassemblers.h"
+#include "tcp_reassembler.h"
+#include "tcp_reassembler_ids.h"
+#include "tcp_reassembler_ips.h"
 #include "tcp_session.h"
 
 using namespace snort;
@@ -68,10 +71,78 @@ const char* tcp_event_names[] = {
 TcpStreamTracker::TcpStreamTracker(bool client) :
     tcp_state(client ? TCP_STATE_NONE : TCP_LISTEN), client_tracker(client),
     held_packet(null_iterator)
-{ }
+{
+    reassembler = new  TcpReassemblerIgnore(*this, seglist);
+    reassembler->init(!client_tracker, nullptr);
+}
 
 TcpStreamTracker::~TcpStreamTracker()
-{ if (splitter != nullptr) splitter->go_away(); }
+{
+    delete reassembler;
+
+    if( oaitw_reassembler )
+    {
+        delete oaitw_reassembler;
+        oaitw_reassembler = nullptr;
+    }
+
+    if ( splitter )
+        splitter->go_away();
+}
+
+void TcpStreamTracker::reset()
+{
+    tcp_alerts.clear();
+    normalizer.reset();
+    seglist.reset();
+    reassembler->reset_paf();
+}
+
+void TcpStreamTracker::clear_tracker(snort::Flow* flow, snort::Packet* p, bool flush_segments, bool restart)
+{
+    if ( flush_segments )
+        reassembler->flush_queued_segments(flow, true, p);
+
+    if ( p )
+        finalize_held_packet(p);
+    else
+        finalize_held_packet(flow);
+
+    seglist.purge_segment_list();
+
+    if ( restart )
+        reassembler->reset_paf();
+    else
+        reassembler->clear_paf();
+
+    set_splitter((StreamSplitter*)nullptr);
+}
+
+int TcpStreamTracker::eval_flush_policy_on_ack(snort::Packet* p)
+{
+    if( oaitw_reassembler )
+    {
+        delete oaitw_reassembler;
+        oaitw_reassembler = nullptr;
+    }
+
+    reassembler->eval_flush_policy_on_ack(p);
+
+    return 0;
+}
+
+int TcpStreamTracker::eval_flush_policy_on_data(snort::Packet* p)
+{
+    if( oaitw_reassembler )
+    {
+        delete oaitw_reassembler;
+        oaitw_reassembler = nullptr;
+    }
+
+    reassembler->eval_flush_policy_on_data(p);
+
+    return 0;
+}
 
 TcpStreamTracker::TcpEvent TcpStreamTracker::set_tcp_event(const TcpSegmentDescriptor& tsd)
 {
@@ -212,7 +283,6 @@ void TcpStreamTracker::cache_mac_address(const TcpSegmentDescriptor& tsd, uint8_
     }
 }
 
-
 void TcpStreamTracker::set_fin_seq_status_seen(const TcpSegmentDescriptor& tsd)
 {
     if ( !fin_seq_set and SEQ_GEQ(tsd.get_end_seq(), r_win_base) )
@@ -220,12 +290,13 @@ void TcpStreamTracker::set_fin_seq_status_seen(const TcpSegmentDescriptor& tsd)
         fin_i_seq = tsd.get_seq();
         fin_final_seq = tsd.get_end_seq();
         fin_seq_set = true;
-        fin_seq_status = TcpStreamTracker::FIN_WITH_SEQ_SEEN;
+        fin_seq_status = FIN_WITH_SEQ_SEEN;
     }
 }
 
-void TcpStreamTracker::init_tcp_state()
+void TcpStreamTracker::init_tcp_state(TcpSession* s)
 {
+    session = s;
     tcp_state = ( client_tracker ) ?
         TcpStreamTracker::TCP_STATE_NONE : TcpStreamTracker::TCP_LISTEN;
 
@@ -239,41 +310,146 @@ void TcpStreamTracker::init_tcp_state()
     mac_addr_valid = false;
     fin_i_seq = 0;
     fin_final_seq = 0;
-    fin_seq_status = TcpStreamTracker::FIN_NOT_SEEN;
+    fin_seq_status = FIN_NOT_SEEN;
     fin_seq_set = false;
     rst_pkt_sent = false;
     order = TcpStreamTracker::IN_SEQUENCE;
     held_packet = null_iterator;
+
     flush_policy = STREAM_FLPOLICY_IGNORE;
-    reassembler.reset();
-    splitter_finish_flag = false;
+    if( oaitw_reassembler )
+    {
+        delete oaitw_reassembler;
+        oaitw_reassembler = nullptr;
+    }
+    if ( reassembler )
+        delete reassembler;
+    reassembler = new  TcpReassemblerIgnore(*this, seglist);
+    reassembler->init(!client_tracker, nullptr);
+
+    normalizer.reset();
+    seglist.reset();
+    tcp_alerts.clear();
 }
 
-//-------------------------------------------------------------------------
-// flush policy stuff
-//-------------------------------------------------------------------------
+void TcpStreamTracker::update_stream_order(const TcpSegmentDescriptor& tsd, bool aligned)
+{
+    uint32_t seq = tsd.get_seq();
 
-void TcpStreamTracker::init_flush_policy()
+    switch ( order )
+    {
+        case TcpStreamTracker::IN_SEQUENCE:
+            if ( aligned )
+                tsd.set_packet_flags(PKT_STREAM_ORDER_OK);
+            else if ( SEQ_GT(seq, rcv_nxt) )
+            {
+                order = TcpStreamTracker::NONE;
+                hole_left_edge = rcv_nxt;
+                hole_right_edge = seq - 1;
+            }
+            break;
+
+        case TcpStreamTracker::NONE:
+            if ( aligned )
+            {
+                tsd.set_packet_flags(PKT_STREAM_ORDER_OK);
+                if ( SEQ_GT(tsd.get_end_seq(), hole_right_edge) )
+                    order = TcpStreamTracker::OUT_OF_SEQUENCE;
+                else
+                    hole_left_edge = tsd.get_end_seq();
+            }
+            else
+            {
+                if ( SEQ_LEQ(seq, hole_right_edge) )
+                {
+                    if ( SEQ_GT(seq, hole_left_edge) )
+                        hole_right_edge = seq - 1;
+                    else if ( SEQ_GT(tsd.get_end_seq(), hole_left_edge) )
+                    {
+                        hole_left_edge = tsd.get_end_seq();
+                        tsd.set_packet_flags(PKT_STREAM_ORDER_OK);
+                    }
+                }
+                // accounting for overlaps when not aligned
+                if ( SEQ_GT(hole_left_edge, hole_right_edge) )
+                    order = TcpStreamTracker::OUT_OF_SEQUENCE;
+            }
+            break;
+
+        case TcpStreamTracker::OUT_OF_SEQUENCE:
+            tsd.set_packet_flags(PKT_STREAM_ORDER_BAD);
+    }
+}
+
+void TcpStreamTracker::update_flush_policy(StreamSplitter* splitter)
 {
-    if ( !splitter )
-        flush_policy = STREAM_FLPOLICY_IGNORE;
-    else if ( normalizer.is_tcp_ips_enabled() )
-        flush_policy = STREAM_FLPOLICY_ON_DATA;
+    if( oaitw_reassembler )
+    {
+        delete oaitw_reassembler;
+        oaitw_reassembler = nullptr;
+    }
+
+   if ( reassembler and flush_policy == reassembler->get_flush_policy() )
+    {
+        reassembler->init(!client_tracker, splitter);
+        return;
+    }
+
+    if ( flush_policy == STREAM_FLPOLICY_IGNORE )
+    {
+        // switching to Ignore flush policy...save pointer to current reassembler to delete later
+        if ( reassembler )
+            oaitw_reassembler = reassembler;
+
+        reassembler = new  TcpReassemblerIgnore(*this, seglist);
+        reassembler->init(!client_tracker, splitter);
+    }
+    else if ( flush_policy == STREAM_FLPOLICY_ON_DATA )
+    {
+        if ( reassembler )
+        {
+            // update from IDS -> IPS is not supported
+            assert( reassembler->get_flush_policy() != STREAM_FLPOLICY_ON_ACK );
+            delete reassembler;
+        }
+
+        reassembler = new  TcpReassemblerIps(*this, seglist);
+        reassembler->init(!client_tracker, splitter);
+    }
     else
-        flush_policy = STREAM_FLPOLICY_ON_ACK;
+    {
+        if ( reassembler )
+        {
+            // update from IPS -> IDS is not supported
+            assert( reassembler->get_flush_policy() != STREAM_FLPOLICY_ON_DATA );
+            delete reassembler;
+        }
+
+        reassembler = new  TcpReassemblerIds(*this, seglist);
+        reassembler->init(!client_tracker, splitter);
+    }
 }
 
 void TcpStreamTracker::set_splitter(StreamSplitter* ss)
 {
     if ( splitter )
+    {
+        reassembler->release_splitter();
         splitter->go_away();
+    }
 
     splitter = ss;
-
-    if ( !splitter )
-        flush_policy = STREAM_FLPOLICY_IGNORE;
+    if ( ss )
+    {
+        if ( normalizer.is_tcp_ips_enabled() )
+            flush_policy = STREAM_FLPOLICY_ON_DATA;
+        else
+            flush_policy = STREAM_FLPOLICY_ON_ACK;
+    }
     else
-        reassembler.setup_paf();
+        flush_policy = STREAM_FLPOLICY_IGNORE;
+
+    update_flush_policy(ss);
 }
 
 void TcpStreamTracker::set_splitter(const Flow* flow)
@@ -284,24 +460,53 @@ void TcpStreamTracker::set_splitter(const Flow* flow)
         ins = flow->clouseau;
 
     if ( ins )
-        set_splitter(ins->get_splitter(!client_tracker) );
+        set_splitter(ins->get_splitter(!client_tracker));
     else
-        set_splitter(new AtomSplitter(!client_tracker) );
+        set_splitter(new AtomSplitter(!client_tracker));
 }
 
-bool TcpStreamTracker::splitter_finish(snort::Flow* flow)
+static inline bool both_splitters_aborted(Flow* flow)
 {
-    if (!splitter)
-        return true;
+    uint32_t both_splitters_yoinked = (SSNFLAG_ABORT_CLIENT | SSNFLAG_ABORT_SERVER);
+    return (flow->get_session_flags() & both_splitters_yoinked) == both_splitters_yoinked;
+}
+
+void TcpStreamTracker::fallback()
+{
+#ifndef NDEBUG
+    assert(splitter);
+
+    // FIXIT-L: consolidate these 3
+    //bool to_server = splitter->to_server();
+    //assert(server_side == to_server && server_side == !tracker.client_tracker);
+#endif
+
+    set_splitter(new AtomSplitter(!client_tracker));
+    tcpStats.partial_fallbacks++;
+
+    Flow* flow = session->flow;
+    if ( !client_tracker )
+        flow->set_session_flags(SSNFLAG_ABORT_SERVER);
+    else
+        flow->set_session_flags(SSNFLAG_ABORT_CLIENT);
 
-    if (!splitter_finish_flag)
+    if ( flow->gadget and both_splitters_aborted(flow) )
     {
-        splitter_finish_flag = true;
-        return splitter->finish(flow);
+        flow->clear_gadget();
+
+        if (flow->clouseau)
+            flow->clear_clouseau();
+
+        tcpStats.inspector_fallbacks++;
     }
-    // there shouldn't be any un-flushed data beyond this point,
-    // returning false here, discards it
-    return false;
+}
+
+void TcpStreamTracker::disable_reassembly(Flow* f)
+{
+    set_splitter((StreamSplitter*)nullptr);
+    seglist.reset();
+    reassembler->reset_paf();
+    finalize_held_packet(f);
 }
 
 void TcpStreamTracker::init_on_syn_sent(TcpSegmentDescriptor& tsd)
@@ -328,13 +533,13 @@ void TcpStreamTracker::init_on_syn_sent(TcpSegmentDescriptor& tsd)
     tcpStats.sessions_on_syn++;
 }
 
-void TcpStreamTracker::init_on_syn_recv(TcpSegmentDescriptor& tsd)
+void TcpStreamTracker::init_on_syn_recv(const TcpSegmentDescriptor& tsd)
 {
     irs = tsd.get_seq();
 
     rcv_nxt = tsd.get_seq() + 1;
     r_win_base = tsd.get_seq() + 1;
-    reassembler.set_seglist_base_seq(tsd.get_seq() + 1);
+    seglist.set_seglist_base_seq(tsd.get_seq() + 1);
 
     cache_mac_address(tsd, FROM_CLIENT);
     tcp_state = TcpStreamTracker::TCP_SYN_RECV;
@@ -354,7 +559,7 @@ void TcpStreamTracker::init_on_synack_sent(TcpSegmentDescriptor& tsd)
 
     r_win_base = tsd.get_ack();
     rcv_nxt = tsd.get_ack();
-    reassembler.set_seglist_base_seq(tsd.get_ack() );
+    seglist.set_seglist_base_seq(tsd.get_ack() );
 
     ts_last_packet = tsd.get_packet_timestamp();
     tf_flags |= normalizer.get_tcp_timestamp(tsd, false);
@@ -369,7 +574,7 @@ void TcpStreamTracker::init_on_synack_sent(TcpSegmentDescriptor& tsd)
     tcpStats.sessions_on_syn_ack++;
 }
 
-void TcpStreamTracker::init_on_synack_recv(TcpSegmentDescriptor& tsd)
+void TcpStreamTracker::init_on_synack_recv(const TcpSegmentDescriptor& tsd)
 {
     iss = tsd.get_ack() - 1;
     irs = tsd.get_seq();
@@ -378,7 +583,7 @@ void TcpStreamTracker::init_on_synack_recv(TcpSegmentDescriptor& tsd)
 
     rcv_nxt = tsd.get_seq() + 1;
     r_win_base = tsd.get_seq() + 1;
-    reassembler.set_seglist_base_seq(tsd.get_seq() + 1);
+    seglist.set_seglist_base_seq(tsd.get_seq() + 1);
 
     cache_mac_address(tsd, FROM_SERVER);
     if ( TcpStreamTracker::TCP_SYN_SENT == tcp_state )
@@ -404,7 +609,7 @@ void TcpStreamTracker::init_on_data_seg_sent(TcpSegmentDescriptor& tsd)
 
     r_win_base = tsd.get_ack();
     rcv_nxt = tsd.get_ack();
-    reassembler.set_seglist_base_seq(tsd.get_ack());
+    seglist.set_seglist_base_seq(tsd.get_ack());
 
     ts_last_packet = tsd.get_packet_timestamp();
     tf_flags |= normalizer.get_tcp_timestamp(tsd, false);
@@ -417,7 +622,7 @@ void TcpStreamTracker::init_on_data_seg_sent(TcpSegmentDescriptor& tsd)
     tcp_state = TcpStreamTracker::TCP_MID_STREAM_SENT;
 }
 
-void TcpStreamTracker::init_on_data_seg_recv(TcpSegmentDescriptor& tsd)
+void TcpStreamTracker::init_on_data_seg_recv(const TcpSegmentDescriptor& tsd)
 {
     iss = tsd.get_ack() - 1;
     irs = tsd.get_seq() - 1;
@@ -427,7 +632,7 @@ void TcpStreamTracker::init_on_data_seg_recv(TcpSegmentDescriptor& tsd)
 
     rcv_nxt = tsd.get_seq();
     r_win_base = tsd.get_seq();
-    reassembler.set_seglist_base_seq(tsd.get_seq());
+    seglist.set_seglist_base_seq(tsd.get_seq());
 
     cache_mac_address(tsd, tsd.get_direction() );
     tcpStats.sessions_on_data++;
@@ -455,26 +660,26 @@ void TcpStreamTracker::finish_server_init(TcpSegmentDescriptor& tsd)
     tf_flags |= ( tsd.init_mss(&mss) | tsd.init_wscale(&wscale) );
 }
 
-void TcpStreamTracker::finish_client_init(TcpSegmentDescriptor& tsd)
+void TcpStreamTracker::finish_client_init(const TcpSegmentDescriptor& tsd)
 {
     Flow* flow = tsd.get_flow();
     rcv_nxt = tsd.get_end_seq();
 
-    if ( reassembler.data_was_queued() )
+    if ( seglist.data_was_queued() )
         return;  // we already have state, don't mess it up
 
     if ( !( flow->session_state & STREAM_STATE_MIDSTREAM ) )
     {
         if ( tsd.get_tcph()->is_syn() )
-            reassembler.set_seglist_base_seq(tsd.get_seq() + 1);
+            seglist.set_seglist_base_seq(tsd.get_seq() + 1);
         else
-            reassembler.set_seglist_base_seq(tsd.get_seq());
+            seglist.set_seglist_base_seq(tsd.get_seq());
 
         r_win_base = tsd.get_end_seq();
     }
     else
     {
-        reassembler.set_seglist_base_seq(tsd.get_seq());
+        seglist.set_seglist_base_seq(tsd.get_seq());
         r_win_base = tsd.get_seq();
     }
 }
@@ -498,15 +703,15 @@ void TcpStreamTracker::update_tracker_ack_recv(TcpSegmentDescriptor& tsd)
 }
 
 // In no-ack policy, data is implicitly acked immediately.
-void TcpStreamTracker::update_tracker_no_ack_recv(TcpSegmentDescriptor& tsd)
+void TcpStreamTracker::update_tracker_no_ack_recv(const TcpSegmentDescriptor& tsd)
 {
     snd_una = snd_nxt = tsd.get_end_seq();
 }
 
-void TcpStreamTracker::update_tracker_no_ack_sent(TcpSegmentDescriptor& tsd)
+void TcpStreamTracker::update_tracker_no_ack_sent(const TcpSegmentDescriptor& tsd)
 {
     r_win_base = tsd.get_end_seq();
-    reassembler.flush_on_ack_policy(tsd.get_pkt());
+    eval_flush_policy_on_ack(tsd.get_pkt());
 }
 
 void TcpStreamTracker::update_tracker_ack_sent(TcpSegmentDescriptor& tsd)
@@ -522,13 +727,17 @@ void TcpStreamTracker::update_tracker_ack_sent(TcpSegmentDescriptor& tsd)
         snd_wnd = tsd.get_wnd();
     }
 
-    if ( ( fin_seq_status == TcpStreamTracker::FIN_WITH_SEQ_SEEN )
+    if ( flush_policy == STREAM_FLPOLICY_IGNORE
+        and SEQ_GT(tsd.get_ack(), rcv_nxt) )
+        rcv_nxt = tsd.get_ack();
+
+    if ( ( fin_seq_status == FIN_WITH_SEQ_SEEN )
         && SEQ_GEQ(tsd.get_ack(), fin_final_seq + 1) && !(tsd.is_meta_ack_packet()) )
     {
-        fin_seq_status = TcpStreamTracker::FIN_WITH_SEQ_ACKED;
+        fin_seq_status = FIN_WITH_SEQ_ACKED;
     }
 
-    reassembler.flush_on_ack_policy(tsd.get_pkt());
+    eval_flush_policy_on_ack(tsd.get_pkt());
 }
 
 bool TcpStreamTracker::update_on_3whs_ack(TcpSegmentDescriptor& tsd)
@@ -654,6 +863,36 @@ bool TcpStreamTracker::set_held_packet(Packet* p)
     return true;
 }
 
+int32_t TcpStreamTracker::kickstart_asymmetric_flow(const TcpSegmentDescriptor& tsd, uint32_t max_queued_bytes)
+{
+    seglist.skip_holes();
+
+    if ( reassembler->is_splitter_paf() )
+        fallback();
+    else
+        reassembler->reset_paf();
+
+    eval_flush_policy_on_data(tsd.get_pkt());
+
+    int32_t space_left = max_queued_bytes - seglist.get_seg_bytes_total();
+
+    if ( get_tcp_state() == TcpStreamTracker::TCP_MID_STREAM_RECV )
+    {
+        set_tcp_state(TcpStreamTracker::TCP_ESTABLISHED);
+        if (PacketTracer::is_active())
+            PacketTracer::log("Stream: Kickstart of midstream asymmetric flow! Seglist queue space: %u\n",
+                space_left );
+    }
+    else
+    {
+        if (PacketTracer::is_active())
+            PacketTracer::log("Stream: Kickstart of asymmetric flow! Seglist queue space: %u\n",
+                space_left );
+    }
+
+    return space_left;
+}
+
 void TcpStreamTracker::perform_fin_recv_flush(TcpSegmentDescriptor& tsd)
 {
     if ( tsd.is_data_segment() )
@@ -661,7 +900,7 @@ void TcpStreamTracker::perform_fin_recv_flush(TcpSegmentDescriptor& tsd)
 
     if ( flush_policy == STREAM_FLPOLICY_ON_DATA and SEQ_EQ(tsd.get_end_seq(), rcv_nxt)
          and !tsd.get_flow()->searching_for_service() )
-        reassembler.finish_and_final_flush(tsd.get_flow(), true, tsd.get_pkt());
+        reassembler->finish_and_final_flush(tsd.get_flow(), true, tsd.get_pkt());
 }
 
 uint32_t TcpStreamTracker::perform_partial_flush()
@@ -670,7 +909,7 @@ uint32_t TcpStreamTracker::perform_partial_flush()
     if ( held_packet != null_iterator )
     {
         Packet* p;
-        flushed = reassembler.perform_partial_flush(session->flow, p);
+        flushed = reassembler->perform_partial_flush(session->flow, p);
 
         // If the held_packet hasn't been released by perform_partial_flush(),
         // call finalize directly.
@@ -727,7 +966,7 @@ void TcpStreamTracker::finalize_held_packet(Packet* cp)
                 tcpStats.held_packets_passed++;
             }
 
-            TcpStreamSession* tcp_session = (TcpStreamSession*)cp->flow->session;
+            TcpSession* tcp_session = (TcpSession*)cp->flow->session;
             tcp_session->held_packet_dir = SSN_DIR_NONE;
         }
 
@@ -755,7 +994,7 @@ void TcpStreamTracker::finalize_held_packet(Flow* flow)
         }
         else
         {
-            TcpStreamSession* tcp_session = (TcpStreamSession*)flow->session;
+            TcpSession* tcp_session = (TcpSession*)flow->session;
             tcp_session->held_packet_dir = SSN_DIR_NONE;
             Analyzer::get_local_analyzer()->finalize_daq_message(msg, DAQ_VERDICT_PASS);
             tcpStats.held_packets_passed++;
index 80b6b41d7897af3651c448933f799ce3b2ff08f4..b8850bfc532955c50bf9c0fe11e19480b45c80c9 100644 (file)
 #include <cstdint>
 #include <list>
 
-#include "stream/paf.h"
-
-#include "segment_overlap_editor.h"
+#include "tcp_alerts.h"
 #include "tcp_defs.h"
 #include "tcp_module.h"
 #include "tcp_normalizers.h"
-#include "tcp_reassemblers.h"
+#include "tcp_reassembler.h"
+#include "tcp_reassembly_segments.h"
+#include "tcp_segment_node.h"
 #include "tcp_segment_descriptor.h"
 
 extern const char* tcp_state_names[];
@@ -43,9 +43,10 @@ struct Packet;
 }
 
 class HeldPacket;
-class TcpReassembler;
 class TcpSession;
 
+enum FinSeqNumStatus : uint8_t { FIN_NOT_SEEN, FIN_WITH_SEQ_SEEN, FIN_WITH_SEQ_ACKED };
+
 class TcpStreamTracker
 {
 public:
@@ -88,10 +89,17 @@ public:
 
     enum PacketOrder : uint8_t { OUT_OF_SEQUENCE, IN_SEQUENCE, NONE };
 
-    enum FinSeqNumStatus : uint8_t { FIN_NOT_SEEN, FIN_WITH_SEQ_SEEN, FIN_WITH_SEQ_ACKED };
-
     TcpStreamTracker(bool client);
-    virtual ~TcpStreamTracker();
+    ~TcpStreamTracker();
+
+    void reset();
+    void clear_tracker(snort::Flow*, snort::Packet*, bool flush_segments, bool restart);
+
+    int eval_flush_policy_on_ack(snort::Packet*);
+    int eval_flush_policy_on_data(snort::Packet*);
+    void update_stream_order(const TcpSegmentDescriptor&, bool aligned);
+
+    void fallback();
 
     bool is_client_tracker() const
     { return client_tracker; }
@@ -255,54 +263,50 @@ public:
     bool is_rst_pkt_sent() const
     { return rst_pkt_sent; }
 
-    void set_flush_policy(FlushPolicy policy)
-    { flush_policy = policy; }
-
+    void set_flush_policy(FlushPolicy policy);
     FlushPolicy get_flush_policy() const
     { return flush_policy; }
 
-    virtual void init_tcp_state();
-    virtual void init_flush_policy();
-    virtual void set_splitter(snort::StreamSplitter* ss);
-    virtual void set_splitter(const snort::Flow* flow);
+    void set_order(uint8_t order)
+    { this->order = order; }
+
+    void init_tcp_state(TcpSession*);
+    void set_splitter(snort::StreamSplitter* ss);
+    void set_splitter(const snort::Flow* flow);
 
     snort::StreamSplitter* get_splitter()
     { return splitter; }
 
-    bool is_splitter_paf() const
-    { return splitter && splitter->is_paf(); }
-
-    bool splitter_finish(snort::Flow* flow);
-
-    bool is_reassembly_enabled() const
-    { return  ( splitter and (flush_policy != STREAM_FLPOLICY_IGNORE) ); }
-
-    virtual void init_on_syn_sent(TcpSegmentDescriptor&);
-    virtual void init_on_syn_recv(TcpSegmentDescriptor&);
-    virtual void init_on_synack_sent(TcpSegmentDescriptor&);
-    virtual void init_on_synack_recv(TcpSegmentDescriptor&);
-    virtual void init_on_data_seg_sent(TcpSegmentDescriptor&);
-    virtual void init_on_data_seg_recv(TcpSegmentDescriptor&);
-    virtual void finish_server_init(TcpSegmentDescriptor&);
-    virtual void finish_client_init(TcpSegmentDescriptor&);
-
-    virtual void update_tracker_ack_recv(TcpSegmentDescriptor&);
-    virtual void update_tracker_ack_sent(TcpSegmentDescriptor&);
-    virtual void update_tracker_no_ack_recv(TcpSegmentDescriptor&);
-    virtual void update_tracker_no_ack_sent(TcpSegmentDescriptor&);
-    virtual bool update_on_3whs_ack(TcpSegmentDescriptor&);
-    virtual bool update_on_rst_recv(TcpSegmentDescriptor&);
-    virtual void update_on_rst_sent();
-    virtual bool update_on_fin_recv(TcpSegmentDescriptor&);
-    virtual bool update_on_fin_sent(TcpSegmentDescriptor&);
-    virtual bool is_segment_seq_valid(TcpSegmentDescriptor&);
+    void disable_reassembly(snort::Flow* f);
+
+    void init_on_syn_sent(TcpSegmentDescriptor&);
+    void init_on_syn_recv(const TcpSegmentDescriptor&);
+    void init_on_synack_sent(TcpSegmentDescriptor&);
+    void init_on_synack_recv(const TcpSegmentDescriptor&);
+    void init_on_data_seg_sent(TcpSegmentDescriptor&);
+    void init_on_data_seg_recv(const TcpSegmentDescriptor&);
+    void finish_server_init(TcpSegmentDescriptor&);
+    void finish_client_init(const TcpSegmentDescriptor&);
+
+    void update_tracker_ack_recv(TcpSegmentDescriptor&);
+    void update_tracker_ack_sent(TcpSegmentDescriptor&);
+    void update_tracker_no_ack_recv(const TcpSegmentDescriptor&);
+    void update_tracker_no_ack_sent(const TcpSegmentDescriptor&);
+    bool update_on_3whs_ack(TcpSegmentDescriptor&);
+    bool update_on_rst_recv(TcpSegmentDescriptor&);
+    void update_on_rst_sent();
+    bool update_on_fin_recv(TcpSegmentDescriptor&);
+    bool update_on_fin_sent(TcpSegmentDescriptor&);
+    bool is_segment_seq_valid(TcpSegmentDescriptor&);
     bool set_held_packet(snort::Packet*);
     bool is_retransmit_of_held_packet(snort::Packet*);
     void finalize_held_packet(snort::Packet*);
     void finalize_held_packet(snort::Flow*);
     void perform_fin_recv_flush(TcpSegmentDescriptor&);
+    int32_t kickstart_asymmetric_flow(const TcpSegmentDescriptor& tsd, uint32_t max_queued_bytes);
     uint32_t perform_partial_flush();
-    bool is_holding_packet() const { return held_packet != null_iterator; }
+    bool is_holding_packet() const
+    { return held_packet != null_iterator; }
 
     // max_remove < 0 means time out all eligible packets.
     // Return whether there are more packets that need to be released.
@@ -313,23 +317,21 @@ public:
     static void thread_term();
 
 public:
-    uint32_t snd_una = 0; // SND.UNA - send unacknowledged
-    uint32_t snd_nxt = 0; // SND.NXT - send next
-    uint32_t snd_wnd = 0; // SND.WND - send window
-    uint32_t snd_wl1 = 0; // SND.WL1 - segment sequence number used for last window update
-    uint32_t snd_wl2 = 0; // SND.WL2 - segment acknowledgment number used for last window update
-    uint32_t iss = 0;     // ISS     - initial send sequence number
+    uint32_t snd_una = 0;          // SND.UNA - send unacknowledged
+    uint32_t snd_nxt = 0;          // SND.NXT - send next
+    uint32_t snd_wnd = 0;          // SND.WND - send window
+    uint32_t snd_wl1 = 0;          // SND.WL1 - segment sequence number used for last window update
+    uint32_t snd_wl2 = 0;          // SND.WL2 - segment acknowledgment number used for last window update
+    uint32_t iss = 0;              // ISS     - initial send sequence number
 
-    uint32_t rcv_nxt = 0; // RCV.NXT - receive next
-    uint32_t rcv_wnd = 0; // RCV.WND - receive window
-    uint32_t irs = 0;     // IRS     - initial receive sequence number
+    uint32_t rcv_nxt = 0;          // RCV.NXT - receive next
+    uint32_t rcv_wnd = 0;          // RCV.WND - receive window
+    uint32_t irs = 0;              // IRS     - initial receive sequence number
 
-    uint16_t snd_up = 0;  // SND.UP  - send urgent pointer
-    uint16_t rcv_up = 0;  // RCV.UP  - receive urgent pointer
+    uint16_t snd_up = 0;           // SND.UP  - send urgent pointer
+    uint16_t rcv_up = 0;           // RCV.UP  - receive urgent pointer
 
     uint32_t held_pkt_seq = 0;
-    uint32_t hole_left_edge = 0;   // First left hole
-    uint32_t hole_right_edge = 0;
 
     TcpState tcp_state;
     TcpEvent tcp_event = TCP_MAX_EVENTS;
@@ -342,18 +344,22 @@ public:
 // FIXIT-L make these non-public
 public:
     TcpNormalizerPolicy normalizer;
-    TcpReassemblerPolicy reassembler;
+    TcpReassemblySegments seglist;
+    TcpReassembler* reassembler = nullptr;
+    TcpReassembler* oaitw_reassembler = nullptr;
     TcpSession* session = nullptr;
+    TcpAlerts tcp_alerts;
 
     uint32_t r_win_base = 0; // remote side window base sequence number (the last ack we got)
     uint32_t small_seg_count = 0;
-    uint8_t order = IN_SEQUENCE;
-    FinSeqNumStatus fin_seq_status = TcpStreamTracker::FIN_NOT_SEEN;
+    FinSeqNumStatus fin_seq_status = FIN_NOT_SEEN;
+
+private:
+    void update_flush_policy(snort::StreamSplitter*);
 
-protected:
+    snort::StreamSplitter* splitter = nullptr;
     static const std::list<HeldPacket>::iterator null_iterator;
     std::list<HeldPacket>::iterator held_packet;
-    snort::StreamSplitter* splitter = nullptr;
     uint32_t ts_last_packet = 0;
     uint32_t ts_last = 0;       // last timestamp (for PAWS)
     uint32_t fin_final_seq = 0;
@@ -367,7 +373,10 @@ protected:
     FlushPolicy flush_policy = STREAM_FLPOLICY_IGNORE;
     bool mac_addr_valid = false;
     bool fin_seq_set = false;  // FIXIT-M should be obviated by tcp state
-    bool splitter_finish_flag = false;
+
+    uint8_t order = IN_SEQUENCE;
+    uint32_t hole_left_edge = 0;   // First left hole
+    uint32_t hole_right_edge = 0;
 };
 
 // <--- note -- the 'state' parameter must be a reference
index 63544ff0aff5f0949aa7a3b0742d4c123cf673c9..e53023cbc874be1246a57d28c359ea9f6d641b2b 100644 (file)
@@ -87,10 +87,10 @@ inline void TraceSession(const snort::Flow* flow, const snort::Packet* p)
         flow->ssn_state.session_flags, flow->client_port, flow->server_port);
 }
 
-inline void TraceSegments(const TcpReassemblerPolicy& trp, const snort::Packet* p)
+inline void TraceSegments(const TcpReassemblySegments& seglist, const snort::Packet* p)
 {
-    const TcpSegmentNode* tsn = trp.trs.sos.seglist.head;
-    uint32_t sx = trp.trs.tracker->r_win_base;
+    const TcpSegmentNode* tsn = seglist.head;
+    uint32_t sx = seglist.tracker->r_win_base;
     unsigned segs = 0;
     unsigned bytes = 0;
     std::stringstream ss;
@@ -100,30 +100,29 @@ inline void TraceSegments(const TcpReassemblerPolicy& trp, const snort::Packet*
 
     while ( tsn )
     {
-        if ( SEQ_LT(sx, tsn->i_seq) )
-            ss << " +" << tsn->i_seq - sx;
-        else if ( SEQ_GT(sx, tsn->i_seq) )
-            ss << " -" << sx - tsn->i_seq;
+        uint32_t seq = tsn->start_seq();
 
-        ss << " " << tsn->i_len;
+        if ( SEQ_LT(sx, seq) )
+            ss << " +" << seq - sx;
 
-        if ( tsn->c_len and tsn->c_len != tsn->i_len )
-        {
-            ss << "(" << tsn->offset << "|" << tsn->c_len;
-            ss << "|" << tsn->i_len-tsn->offset-tsn->c_len << ")";
-        }
+        else if ( SEQ_GT(sx, seq) )
+            ss << " -" << sx - seq;
 
+        ss << " " << tsn->length;
+
+        if ( tsn->cursor and tsn->unscanned() > 0 )
+            ss << "(" << tsn->cursor << "|" << tsn->unscanned() << ")";
         segs++;
-        bytes += tsn->i_len;
-        sx = tsn->i_seq + tsn->i_len;
+        bytes += tsn->length;
+        sx = seq + tsn->length;
         tsn = tsn->next;
     }
 
     if ( !ss.str().empty() )
         trace_logf(DEFAULT_TRACE_LOG_LEVEL, stream_tcp_trace, TRACE_SEGMENTS, p, "       %s\n", ss.str().c_str());
 
-    assert(trp.trs.sos.seg_count == segs);
-    assert(trp.trs.sos.seg_bytes_logical == bytes);
+    assert(seglist.seg_count == segs);
+    assert(seglist.seg_bytes_logical == bytes);
 }
 
 inline void TraceState(const TcpStreamTracker& a, const TcpStreamTracker& b, const char* s,
@@ -137,17 +136,17 @@ inline void TraceState(const TcpStreamTracker& a, const TcpStreamTracker& b, con
         s, stream_tcp_state_to_str(a), ua, ns, a.get_snd_wnd( ),
         RMT(a, rcv_nxt, b), RMT(a, r_win_base, b), a.get_iss(), a.get_irs());
 
-    unsigned paf = a.is_splitter_paf() ? 2 : 0;
+    unsigned paf = a.reassembler->is_splitter_paf() ? 2 : 0;
     unsigned fpt = a.get_flush_policy() ? 192 : 0;
 
     trace_logf(DEFAULT_TRACE_LOG_LEVEL, stream_tcp_trace, TRACE_STATE, p,
         "           FP=%s:%-4u SC=%-4u FL=%-4u SL=%-5u BS=%-4u\n",
         flushxt[a.get_flush_policy() + paf], fpt,
-        a.reassembler.get_seg_count(), a.reassembler.get_flush_count(),
-        a.reassembler.get_seg_bytes_logical(),
-        a.reassembler.get_seglist_base_seq() - b.get_iss());
+        a.seglist.get_seg_count(), a.seglist.get_flush_count(),
+        a.seglist.get_seg_bytes_logical(),
+        a.seglist.get_seglist_base_seq() - b.get_iss());
 
-    TraceSegments(a.reassembler, p);
+    TraceSegments(a.seglist, p);
 }
 
 void S5TraceTCP(const TcpSegmentDescriptor& tsd, const snort::Packet* p)
@@ -179,7 +178,7 @@ void S5TraceTCP(const TcpSegmentDescriptor& tsd, const snort::Packet* p)
 
     TraceEvent(tsd, txd, rxd, p);
 
-    if ( ssn->lws_init )
+    if ( ssn->tcp_init )
         TraceSession(tsd.get_flow(), p);
 
     TraceState(cli, srv, cdir, p);
index c6e8cc4d81a204927e56d609e679cf327fd0da82..bb8364597106eaba305e1771e68ade83ae146f50 100644 (file)
@@ -66,7 +66,6 @@ StreamSplitter* TcpSession::get_splitter(bool){ return nullptr; }
 void TcpSession::set_extra_data(Packet*, unsigned int){ }
 bool TcpSession::is_sequenced(unsigned char) const { return true; }
 bool TcpSession::are_packets_missing(unsigned char) const { return false; }
-uint8_t TcpSession::get_reassembly_direction() const { return 0; }
 uint8_t  TcpSession::missing_in_reassembled(unsigned char) const { return 0; }
 
 class TcpSessionMock : public TcpSession