]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #3216: stream_tcp: Skip seglist gap in post-ack mode if data is acked...
authorMasud Hasan (mashasan) <mashasan@cisco.com>
Thu, 16 Dec 2021 20:00:07 +0000 (20:00 +0000)
committerMasud Hasan (mashasan) <mashasan@cisco.com>
Thu, 16 Dec 2021 20:00:07 +0000 (20:00 +0000)
Merge in SNORT/snort3 from ~MASHASAN/snort3:post_ack_gap2 to master

Squashed commit of the following:

commit 9cf27746cc85718d6273cdf061814fdbf51f8479
Author: Masud Hasan <mashasan@cisco.com>
Date:   Mon Nov 29 18:19:08 2021 -0500

    stream_tcp: Skip seglist gap in post-ack mode if data is acked beyond the gap

src/stream/tcp/tcp_reassembler.cc
src/stream/tcp/tcp_reassembler.h
src/stream/tcp/tcp_segment_node.h

index e0d704b5a4c057d7f84868b17c9de7a9fa5cdab1..c8ffe48ee208e70b7c9f9f350554a0c7df855d63 100644 (file)
@@ -114,6 +114,26 @@ void TcpReassembler::update_next(TcpReassemblerState& trs, const TcpSegmentNode&
         trs.sos.seglist.cur_rseg->c_seq = trs.sos.seglist.cur_rseg->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)
+{
+    TcpSegmentNode* tsn;
+
+    while ( remaining_bytes and (tsn = trs.sos.seglist.cur_rseg) )
+    {
+        auto bytes_skipped = ( tsn->c_len <= remaining_bytes ) ? tsn->c_len : remaining_bytes;
+
+        remaining_bytes -= bytes_skipped;
+        tsn->update_ressembly_lengths(bytes_skipped);
+
+        if ( !tsn->c_len )
+        {
+            trs.flush_count++;
+            update_next(trs, *tsn);
+        }
+    }
+}
+
 int TcpReassembler::delete_reassembly_segment(TcpReassemblerState& trs, TcpSegmentNode* tsn)
 {
     int ret;
@@ -413,9 +433,7 @@ int TcpReassembler::flush_data_segments(TcpReassemblerState& trs, uint32_t flush
         }
 
         total_flushed += bytes_copied;
-        tsn->c_seq += bytes_copied;
-        tsn->c_len -= bytes_copied;
-        tsn->offset += bytes_copied;
+        tsn->update_ressembly_lengths(bytes_copied);
         flags = 0;
 
         if ( !tsn->c_len )
@@ -425,10 +443,8 @@ int TcpReassembler::flush_data_segments(TcpReassemblerState& trs, uint32_t flush
         }
 
         /* Check for a gap/missing packet */
-        // FIXIT-L PAF should account for missing data and resume
-        // scanning at the start of next PDU instead of aborting.
         // FIXIT-L FIN may be in to_seq causing bogus gap counts.
-        if ( tsn->is_packet_missing(to_seq) )
+        if ( tsn->is_packet_missing(to_seq) or trs.paf_state.paf == StreamSplitter::SKIP )
         {
             // FIXIT-H // assert(false); find when this scenario happens
             // FIXIT-L this is suboptimal - better to exclude fin from to_seq
@@ -444,6 +460,9 @@ int TcpReassembler::flush_data_segments(TcpReassemblerState& trs, uint32_t flush
             break;
     }
 
+    if ( trs.paf_state.paf == StreamSplitter::SKIP )
+        update_skipped_bytes(remaining_bytes, trs);
+
     return total_flushed;
 }
 
@@ -889,7 +908,6 @@ static inline bool both_splitters_aborted(Flow* flow)
 static inline void fallback(TcpStreamTracker& trk, bool server_side, uint16_t max)
 {
     trk.set_splitter(new AtomSplitter(!server_side, max));
-    trk.reassembler.setup_paf();
     tcpStats.partial_fallbacks++;
 }
 
@@ -911,6 +929,24 @@ void TcpReassembler::fallback(TcpStreamTracker& tracker, bool server_side)
     }
 }
 
+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) )
+        return false;
+
+    // safety - prevent seq + total < seq
+    if ( total > 0x7FFFFFFF )
+        total = 0x7FFFFFFF;
+
+    if ( !ps.tot )
+        flags |= PKT_PDU_HEAD;
+
+    ps.paf = StreamSplitter::SKIP;
+    return true;
+}
+
 // 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
@@ -944,9 +980,6 @@ int32_t TcpReassembler::scan_data_post_ack(TcpReassemblerState& trs, uint32_t* f
         uint32_t end_seq = tsn->c_seq + tsn->c_len;
         if ( SEQ_EQ(end_seq, paf_position(&trs.paf_state)) )
         {
-            if ( splitter->is_paf() and !next_no_gap(*tsn) )
-                return -1;
-
             total = end_seq - trs.sos.seglist_base_seq;
             tsn = tsn->next;
         }
@@ -959,20 +992,26 @@ int32_t TcpReassembler::scan_data_post_ack(TcpReassemblerState& trs, uint32_t* f
         // 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;
 
-        total += flush_len;
-
         if ( next_acked_no_gap_c(*tsn, trs) )
             *flags |= PKT_MORE_TO_FLUSH;
         else
             *flags &= ~PKT_MORE_TO_FLUSH;
-        int32_t flush_pt = paf_check(splitter, &trs.paf_state, p, tsn->payload(),
-            flush_len, total, tsn->c_seq, flags);
+
+        if ( has_seglist_hole(trs, *tsn, trs.paf_state, total, *flags) )
+            flush_pt = total;
+        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();
@@ -1054,6 +1093,35 @@ int TcpReassembler::flush_on_data_policy(TcpReassemblerState& trs, Packet* p)
     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;
@@ -1066,11 +1134,14 @@ int TcpReassembler::flush_on_ack_policy(TcpReassemblerState& trs, Packet* p)
 
     case STREAM_FLPOLICY_ON_ACK:
     {
+        int32_t flush_amt;
+        uint32_t flags;
+
         do
         {
-            uint32_t flags = get_reverse_packet_dir(trs, p);
-            int32_t flush_amt = scan_data_post_ack(trs, &flags, p);
-            if ( flush_amt <= 0 )
+            flags = get_reverse_packet_dir(trs, p);
+            flush_amt = scan_data_post_ack(trs, &flags, p);
+            if ( flush_amt <= 0 or trs.paf_state.paf == StreamSplitter::SKIP )
                 break;
 
             if ( trs.paf_state.paf == StreamSplitter::ABORT )
@@ -1092,6 +1163,11 @@ int TcpReassembler::flush_on_ack_policy(TcpReassemblerState& trs, Packet* p)
             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);
+        }
     }
     break;
 
index f6400e8c398fe5e3064834c2445759333ebd87c7..a9fc8f36a03ecf2b1d70ccdc3b7b6876aa1d74f8 100644 (file)
@@ -89,6 +89,11 @@ protected:
     bool next_no_gap_c(const TcpSegmentNode&);
     bool next_acked_no_gap_c(const TcpSegmentNode&, const TcpReassemblerState&);
     void update_next(TcpReassemblerState&, const TcpSegmentNode&);
+    void update_skipped_bytes(uint32_t, TcpReassemblerState&);
+    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);
 };
 
index ed891cb2dd66e6cf6ca17694577f1cc8c8ff8df9..5260a1f05d33c97e60c30e4c6e779bec2c0a3059 100644 (file)
@@ -61,6 +61,13 @@ public:
             return (c_seq + c_len) < to_seq;
     }
 
+    void update_ressembly_lengths(uint16_t bytes)
+    {
+        c_seq += bytes;
+        c_len -= bytes;
+        offset += bytes;
+    }
+
 public:
     TcpSegmentNode* prev;
     TcpSegmentNode* next;