1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License Version 2 as published
6 // by the Free Software Foundation.  You may not use, modify or distribute
7 // this program under any other version of the GNU General Public License.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License along
15 // with this program; if not, write to the Free Software Foundation, Inc.,
16 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 //--------------------------------------------------------------------------
18 
19 // tcp_segment.cc author davis mcpherson <davmcphe@cisco.com>
20 // Created on: Sep 21, 2015
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include "tcp_segment_node.h"
27 
28 #include "main/thread.h"
29 #include "memory/memory_cap.h"
30 #include "utils/util.h"
31 
32 #include "segment_overlap_editor.h"
33 #include "tcp_module.h"
34 
35 #define USE_RESERVE
36 #ifdef USE_RESERVE
37 static THREAD_LOCAL TcpSegmentNode* reserved = nullptr;
38 static THREAD_LOCAL unsigned reserve_sz = 0;
39 
40 static constexpr unsigned num_res = 4096;
41 static constexpr unsigned res_min = 1024;
42 static constexpr unsigned res_max = 1460;
43 #endif
44 
setup()45 void TcpSegmentNode::setup()
46 {
47 #ifdef USE_RESERVE
48     reserved = nullptr;
49     reserve_sz = 0;
50 #endif
51 }
52 
clear()53 void TcpSegmentNode::clear()
54 {
55 #ifdef USE_RESERVE
56     while ( reserved )
57     {
58         TcpSegmentNode* tsn = reserved;
59         reserved = reserved->next;
60         tcpStats.mem_in_use -= tsn->size;
61         snort_free(tsn);
62     }
63     reserve_sz = 0;
64 #endif
65 }
66 
67 //-------------------------------------------------------------------------
68 // TcpSegment stuff
69 //-------------------------------------------------------------------------
70 
create(const struct timeval & tv,const uint8_t * payload,uint16_t len)71 TcpSegmentNode* TcpSegmentNode::create(
72     const struct timeval& tv, const uint8_t* payload, uint16_t len)
73 {
74     TcpSegmentNode* tsn;
75 
76 #ifdef USE_RESERVE
77     if ( reserved and len > res_min and len <= res_max )
78     {
79         tsn = reserved;
80         reserved = tsn->next;
81         --reserve_sz;
82     }
83     else
84 #endif
85     {
86         size_t size = sizeof(*tsn) + len;
87         tsn = (TcpSegmentNode*)snort_alloc(size);
88         tsn->size = len;
89         tcpStats.mem_in_use += len;
90     }
91     tsn->tv = tv;
92     tsn->i_len = tsn->c_len = len;
93     memcpy(tsn->data, payload, len);
94 
95     tsn->prev = tsn->next = nullptr;
96     tsn->i_seq = tsn->c_seq = 0;
97     tsn->offset = 0;
98     tsn->ts = 0;
99 
100     return tsn;
101 }
102 
init(const TcpSegmentDescriptor & tsd)103 TcpSegmentNode* TcpSegmentNode::init(const TcpSegmentDescriptor& tsd)
104 {
105     return create(tsd.get_pkt()->pkth->ts, tsd.get_pkt()->data, tsd.get_len());
106 }
107 
init(TcpSegmentNode & tns)108 TcpSegmentNode* TcpSegmentNode::init(TcpSegmentNode& tns)
109 {
110     return create(tns.tv, tns.payload(), tns.c_len);
111 }
112 
term()113 void TcpSegmentNode::term()
114 {
115 #ifdef USE_RESERVE
116     if ( size == res_max and reserve_sz < num_res )
117     {
118         next = reserved;
119         reserved = this;
120         reserve_sz++;
121     }
122     else
123 #endif
124     {
125         tcpStats.mem_in_use -= size;
126         snort_free(this);
127     }
128     tcpStats.segs_released++;
129 }
130 
is_retransmit(const uint8_t * rdata,uint16_t rsize,uint32_t rseq,uint16_t orig_dsize,bool * full_retransmit)131 bool TcpSegmentNode::is_retransmit(const uint8_t* rdata, uint16_t rsize,
132     uint32_t rseq, uint16_t orig_dsize, bool *full_retransmit)
133 {
134     // retransmit must have same payload at same place
135     if ( !SEQ_EQ(i_seq, rseq) )
136         return false;
137 
138     if ( orig_dsize == c_len )
139     {
140         uint16_t cmp_len = ( c_len <= rsize ) ? c_len : rsize;
141         if ( !memcmp(data, rdata, cmp_len) )
142             return true;
143     }
144     //Checking for a possible split of segment in which case
145     //we compare complete data of the segment to find a retransmission
146     else if ( (orig_dsize == rsize) and !memcmp(data, rdata, rsize) )
147     {
148         if ( full_retransmit )
149             *full_retransmit = true;
150         return true;
151     }
152 
153     return false;
154 }
155