/src/suricata7/src/stream-tcp-list.c
Line | Count | Source |
1 | | /* Copyright (C) 2007-2023 Open Information Security Foundation |
2 | | * |
3 | | * You can copy, redistribute or modify this Program under the terms of |
4 | | * the GNU General Public License version 2 as published by the Free |
5 | | * Software Foundation. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * version 2 along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
15 | | * 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | /** \file |
19 | | * |
20 | | * Segment list functions for insertions, overlap handling, removal and |
21 | | * more. |
22 | | */ |
23 | | |
24 | | #include "suricata-common.h" |
25 | | #include "rust.h" |
26 | | #include "stream-tcp-private.h" |
27 | | #include "stream-tcp.h" |
28 | | #include "stream-tcp-reassemble.h" |
29 | | #include "stream-tcp-inline.h" |
30 | | #include "stream-tcp-list.h" |
31 | | #include "util-streaming-buffer.h" |
32 | | #include "util-print.h" |
33 | | #include "util-validate.h" |
34 | | #include "app-layer-frames.h" |
35 | | |
36 | | static void StreamTcpRemoveSegmentFromStream(TcpStream *stream, TcpSegment *seg); |
37 | | |
38 | | static int check_overlap_different_data = 0; |
39 | | |
40 | | void StreamTcpReassembleConfigEnableOverlapCheck(void) |
41 | 915 | { |
42 | 915 | check_overlap_different_data = 1; |
43 | 915 | } |
44 | | |
45 | | /* |
46 | | * Inserts and overlap handling |
47 | | */ |
48 | | |
49 | 150M | RB_GENERATE(TCPSEG, TcpSegment, rb, TcpSegmentCompare); Line | Count | Source | 49 | | RB_GENERATE(TCPSEG, TcpSegment, rb, TcpSegmentCompare); |
Line | Count | Source | 49 | | RB_GENERATE(TCPSEG, TcpSegment, rb, TcpSegmentCompare); |
Line | Count | Source | 49 | | RB_GENERATE(TCPSEG, TcpSegment, rb, TcpSegmentCompare); |
Line | Count | Source | 49 | | RB_GENERATE(TCPSEG, TcpSegment, rb, TcpSegmentCompare); |
Unexecuted instantiation: TCPSEG_RB_FIND Unexecuted instantiation: TCPSEG_RB_NFIND Line | Count | Source | 49 | | RB_GENERATE(TCPSEG, TcpSegment, rb, TcpSegmentCompare); |
|
50 | 150M | |
51 | 150M | int TcpSegmentCompare(struct TcpSegment *a, struct TcpSegment *b) |
52 | 150M | { |
53 | 84.7M | if (SEQ_GT(a->seq, b->seq)) |
54 | 83.8M | return 1; |
55 | 812k | else if (SEQ_LT(a->seq, b->seq)) |
56 | 296k | return -1; |
57 | 515k | else { |
58 | 515k | if (a->payload_len == b->payload_len) |
59 | 485k | return 0; |
60 | 30.5k | else if (a->payload_len > b->payload_len) |
61 | 16.4k | return 1; |
62 | 14.1k | else |
63 | 14.1k | return -1; |
64 | 515k | } |
65 | 84.7M | } |
66 | | |
67 | | /** \internal |
68 | | * \brief insert segment data into the streaming buffer |
69 | | * \param seg segment to store stream offset in |
70 | | * \param data segment data after overlap handling (if any) |
71 | | * \param data_len data length |
72 | | * |
73 | | * \return SC_OK on success |
74 | | * \return SC_ENOMEM on error (memory allocation error) |
75 | | */ |
76 | | static inline int InsertSegmentDataCustom(TcpStream *stream, TcpSegment *seg, uint8_t *data, uint16_t data_len) |
77 | 7.40M | { |
78 | 7.40M | uint64_t stream_offset; |
79 | 7.40M | uint32_t data_offset; |
80 | | |
81 | 7.40M | if (likely(SEQ_GEQ(seg->seq, stream->base_seq))) { |
82 | 7.39M | stream_offset = STREAM_BASE_OFFSET(stream) + (seg->seq - stream->base_seq); |
83 | 7.39M | data_offset = 0; |
84 | 7.39M | } else { |
85 | | /* segment is partly before base_seq */ |
86 | 9.56k | data_offset = stream->base_seq - seg->seq; |
87 | 9.56k | stream_offset = STREAM_BASE_OFFSET(stream); |
88 | 9.56k | } |
89 | | |
90 | 7.40M | SCLogDebug("stream %p buffer %p, stream_offset %"PRIu64", " |
91 | 7.40M | "data_offset %"PRIu16", SEQ %u BASE %u, data_len %u", |
92 | 7.40M | stream, &stream->sb, stream_offset, |
93 | 7.40M | data_offset, seg->seq, stream->base_seq, data_len); |
94 | 7.40M | DEBUG_VALIDATE_BUG_ON(data_offset > data_len); |
95 | 7.40M | if (data_len <= data_offset) { |
96 | 0 | SCReturnInt(SC_OK); |
97 | 0 | } |
98 | | |
99 | 7.40M | int ret = StreamingBufferInsertAt(&stream->sb, &stream_config.sbcnf, &seg->sbseg, |
100 | 7.40M | data + data_offset, data_len - data_offset, stream_offset); |
101 | 7.40M | if (ret != SC_OK) { |
102 | 0 | SCReturnInt(ret); |
103 | 0 | } |
104 | | #ifdef DEBUG |
105 | | { |
106 | | const uint8_t *mydata; |
107 | | uint32_t mydata_len; |
108 | | uint64_t mydata_offset; |
109 | | StreamingBufferGetData(&stream->sb, &mydata, &mydata_len, &mydata_offset); |
110 | | |
111 | | SCLogDebug("stream %p seg %p data in buffer %p of len %u and offset %"PRIu64, |
112 | | stream, seg, &stream->sb, mydata_len, mydata_offset); |
113 | | //PrintRawDataFp(stdout, mydata, mydata_len); |
114 | | } |
115 | | #endif |
116 | 7.40M | SCReturnInt(SC_OK); |
117 | 7.40M | } |
118 | | |
119 | | /** \internal |
120 | | * \brief check if this segments overlaps with an in-tree seg. |
121 | | * \retval true |
122 | | * \retval false |
123 | | */ |
124 | | static inline bool CheckOverlap(struct TCPSEG *tree, TcpSegment *seg) |
125 | 6.49M | { |
126 | 6.49M | const uint32_t re = SEG_SEQ_RIGHT_EDGE(seg); |
127 | 6.49M | SCLogDebug("start. SEQ %u payload_len %u. Right edge: %u. Seg %p", |
128 | 6.49M | seg->seq, seg->payload_len, re, seg); |
129 | | |
130 | | /* check forward */ |
131 | 6.49M | TcpSegment *next = TCPSEG_RB_NEXT(seg); |
132 | 6.49M | if (next) { |
133 | | // next has same seq, so data must overlap |
134 | 84.2k | if (SEQ_EQ(next->seq, seg->seq)) |
135 | 3.47k | return true; |
136 | | // our right edge is beyond next seq, overlap |
137 | 80.7k | if (SEQ_GT(re, next->seq)) |
138 | 1.93k | return true; |
139 | 80.7k | } |
140 | | /* check backwards */ |
141 | 6.49M | TcpSegment *prev = TCPSEG_RB_PREV(seg); |
142 | 6.49M | if (prev) { |
143 | | // prev has same seq, so data must overlap |
144 | 6.48M | if (SEQ_EQ(prev->seq, seg->seq)) |
145 | 6.84k | return true; |
146 | | // prev's right edge is beyond our seq, overlap |
147 | 6.47M | const uint32_t prev_re = SEG_SEQ_RIGHT_EDGE(prev); |
148 | 6.47M | if (SEQ_GT(prev_re, seg->seq)) |
149 | 1.95k | return true; |
150 | 6.47M | } |
151 | | |
152 | 6.48M | SCLogDebug("no overlap"); |
153 | 6.48M | return false; |
154 | 6.49M | } |
155 | | |
156 | | /** \internal |
157 | | * \brief insert the segment into the proper place in the tree |
158 | | * don't worry about the data or overlaps |
159 | | * |
160 | | * \retval 2 not inserted, data overlap |
161 | | * \retval 1 inserted with overlap detected |
162 | | * \retval 0 inserted, no overlap |
163 | | * \retval -EINVAL seg out of seq range |
164 | | */ |
165 | | static int DoInsertSegment (TcpStream *stream, TcpSegment *seg, TcpSegment **dup_seg, Packet *p) |
166 | 3.80M | { |
167 | | /* in lossy traffic, we can get here with the wrong sequence numbers */ |
168 | 3.80M | if (SEQ_LEQ(SEG_SEQ_RIGHT_EDGE(seg), stream->base_seq)) { |
169 | 0 | return -EINVAL; |
170 | 0 | } |
171 | | |
172 | | /* fast track */ |
173 | 3.80M | if (RB_EMPTY(&stream->seg_tree)) { |
174 | 211k | SCLogDebug("empty tree, inserting seg %p seq %" PRIu32 ", " |
175 | 211k | "len %" PRIu32 "", seg, seg->seq, TCP_SEG_LEN(seg)); |
176 | 211k | TCPSEG_RB_INSERT(&stream->seg_tree, seg); |
177 | 211k | stream->segs_right_edge = SEG_SEQ_RIGHT_EDGE(seg); |
178 | 211k | return 0; |
179 | 211k | } |
180 | | |
181 | | /* insert and then check if there was any overlap with other segments */ |
182 | 3.58M | TcpSegment *res = TCPSEG_RB_INSERT(&stream->seg_tree, seg); |
183 | 3.58M | if (res) { |
184 | 225k | SCLogDebug("seg has a duplicate in the tree seq %u/%u", |
185 | 225k | res->seq, res->payload_len); |
186 | | /* exact duplicate SEQ + payload_len */ |
187 | 225k | *dup_seg = res; |
188 | 225k | return 2; // duplicate has overlap by definition. |
189 | 3.36M | } else { |
190 | 3.36M | if (SEQ_GT(SEG_SEQ_RIGHT_EDGE(seg), stream->segs_right_edge)) |
191 | 3.31M | stream->segs_right_edge = SEG_SEQ_RIGHT_EDGE(seg); |
192 | | |
193 | | /* insert succeeded, now check if we overlap with someone */ |
194 | 3.36M | if (CheckOverlap(&stream->seg_tree, seg) == true) { |
195 | 10.4k | SCLogDebug("seg %u has overlap in the tree", seg->seq); |
196 | 10.4k | return 1; |
197 | 10.4k | } |
198 | 3.36M | } |
199 | 3.35M | SCLogDebug("seg %u: no overlap", seg->seq); |
200 | 3.35M | return 0; |
201 | 3.58M | } |
202 | | |
203 | | /** \internal |
204 | | * \brief handle overlap per list segment |
205 | | * |
206 | | * For a list segment handle the overlap according to the policy. |
207 | | * |
208 | | * The 'buf' parameter points to the memory that will be inserted into |
209 | | * the stream after the overlap checks are complete. As it will |
210 | | * unconditionally overwrite whats in the stream now, the overlap |
211 | | * policies are applied to this buffer. It starts with the 'new' data, |
212 | | * so when the policy states 'old' data has to be used, 'buf' is |
213 | | * updated to contain the 'old' data here. |
214 | | * |
215 | | * \param buf stack allocated buffer sized p->payload_len that will be |
216 | | * inserted into the stream buffer |
217 | | * |
218 | | * \retval 1 if data was different |
219 | | * \retval 0 data was the same or we didn't check for differences |
220 | | */ |
221 | | static int DoHandleDataOverlap(TcpStream *stream, const TcpSegment *list, |
222 | | const TcpSegment *seg, uint8_t *buf, Packet *p) |
223 | 572k | { |
224 | 572k | SCLogDebug("handle overlap for segment %p seq %u len %u re %u, " |
225 | 572k | "list segment %p seq %u len %u re %u", seg, seg->seq, |
226 | 572k | p->payload_len, SEG_SEQ_RIGHT_EDGE(seg), |
227 | 572k | list, list->seq, TCP_SEG_LEN(list), SEG_SEQ_RIGHT_EDGE(list)); |
228 | | |
229 | 572k | int data_is_different = 0; |
230 | 572k | int use_new_data = 0; |
231 | | |
232 | 572k | if (StreamTcpInlineMode()) { |
233 | 0 | SCLogDebug("inline mode"); |
234 | 0 | if (StreamTcpInlineSegmentCompare(stream, p, list) != 0) { |
235 | 0 | SCLogDebug("already accepted data not the same as packet data, rewrite packet"); |
236 | 0 | StreamTcpInlineSegmentReplacePacket(stream, p, list); |
237 | 0 | data_is_different = 1; |
238 | | |
239 | | /* in inline mode we check for different data unconditionally, |
240 | | * but setting events still depends on config */ |
241 | 0 | if (check_overlap_different_data) { |
242 | 0 | StreamTcpSetEvent(p, STREAM_REASSEMBLY_OVERLAP_DIFFERENT_DATA); |
243 | 0 | } |
244 | 0 | } |
245 | | |
246 | | /* IDS mode */ |
247 | 572k | } else { |
248 | 572k | if (check_overlap_different_data) { |
249 | 421k | if (StreamTcpInlineSegmentCompare(stream, p, list) != 0) { |
250 | 109k | SCLogDebug("data is different from what is in the list"); |
251 | 109k | data_is_different = 1; |
252 | 109k | } |
253 | 421k | } else { |
254 | | /* if we're not checking, assume it's different */ |
255 | 151k | data_is_different = 1; |
256 | 151k | } |
257 | | |
258 | | /* apply overlap policies */ |
259 | | |
260 | 572k | if (stream->os_policy == OS_POLICY_LAST) { |
261 | | /* buf will start with LAST data (from the segment), |
262 | | * so if policy is LAST we're now done here. */ |
263 | 0 | return (check_overlap_different_data && data_is_different); |
264 | 0 | } |
265 | | |
266 | | /* start at the same seq */ |
267 | 572k | if (SEQ_EQ(seg->seq, list->seq)) { |
268 | 546k | SCLogDebug("seg starts at list segment"); |
269 | | |
270 | 546k | if (SEQ_LT(SEG_SEQ_RIGHT_EDGE(seg), SEG_SEQ_RIGHT_EDGE(list))) { |
271 | 29.1k | SCLogDebug("seg ends before list end, end overlapped by list"); |
272 | 517k | } else { |
273 | 517k | if (SEQ_GT(SEG_SEQ_RIGHT_EDGE(seg), SEG_SEQ_RIGHT_EDGE(list))) { |
274 | 32.1k | SCLogDebug("seg ends beyond list end, list overlapped and more"); |
275 | 32.1k | switch (stream->os_policy) { |
276 | 0 | case OS_POLICY_LINUX: |
277 | 0 | if (data_is_different) { |
278 | 0 | use_new_data = 1; |
279 | 0 | } |
280 | 0 | break; |
281 | 32.1k | } |
282 | 485k | } else { |
283 | 485k | SCLogDebug("full overlap"); |
284 | 485k | } |
285 | | |
286 | 517k | switch (stream->os_policy) { |
287 | 0 | case OS_POLICY_OLD_LINUX: |
288 | 0 | case OS_POLICY_SOLARIS: |
289 | 0 | case OS_POLICY_HPUX11: |
290 | 0 | if (data_is_different) { |
291 | 0 | use_new_data = 1; |
292 | 0 | } |
293 | 0 | break; |
294 | 517k | } |
295 | 517k | } |
296 | | |
297 | | /* new seg starts before list segment */ |
298 | 546k | } else if (SEQ_LT(seg->seq, list->seq)) { |
299 | 10.4k | SCLogDebug("seg starts before list segment"); |
300 | | |
301 | 10.4k | if (SEQ_LT(SEG_SEQ_RIGHT_EDGE(seg), SEG_SEQ_RIGHT_EDGE(list))) { |
302 | 7.58k | SCLogDebug("seg ends before list end, end overlapped by list"); |
303 | 7.58k | } else { |
304 | 2.83k | if (SEQ_GT(SEG_SEQ_RIGHT_EDGE(seg), SEG_SEQ_RIGHT_EDGE(list))) { |
305 | 1.88k | SCLogDebug("seg starts before and fully overlaps list and beyond"); |
306 | 1.88k | } else { |
307 | 951 | SCLogDebug("seg starts before and fully overlaps list"); |
308 | 951 | } |
309 | | |
310 | 2.83k | switch (stream->os_policy) { |
311 | 0 | case OS_POLICY_SOLARIS: |
312 | 0 | case OS_POLICY_HPUX11: |
313 | 0 | if (data_is_different) { |
314 | 0 | use_new_data = 1; |
315 | 0 | } |
316 | 0 | break; |
317 | 2.83k | } |
318 | 2.83k | } |
319 | | |
320 | 10.4k | switch (stream->os_policy) { |
321 | 10.4k | case OS_POLICY_BSD: |
322 | 10.4k | case OS_POLICY_HPUX10: |
323 | 10.4k | case OS_POLICY_IRIX: |
324 | 10.4k | case OS_POLICY_WINDOWS: |
325 | 10.4k | case OS_POLICY_WINDOWS2K3: |
326 | 10.4k | case OS_POLICY_OLD_LINUX: |
327 | 10.4k | case OS_POLICY_LINUX: |
328 | 10.4k | case OS_POLICY_MACOS: |
329 | 10.4k | if (data_is_different) { |
330 | 7.05k | use_new_data = 1; |
331 | 7.05k | } |
332 | 10.4k | break; |
333 | 10.4k | } |
334 | | |
335 | | /* new seg starts after list segment */ |
336 | 15.8k | } else { //if (SEQ_GT(seg->seq, list->seq)) { |
337 | 15.8k | SCLogDebug("seg starts after list segment"); |
338 | | |
339 | 15.8k | if (SEQ_EQ(SEG_SEQ_RIGHT_EDGE(seg), SEG_SEQ_RIGHT_EDGE(list))) { |
340 | 1.19k | SCLogDebug("seg after and is fully overlapped by list"); |
341 | 14.6k | } else if (SEQ_GT(SEG_SEQ_RIGHT_EDGE(seg), SEG_SEQ_RIGHT_EDGE(list))) { |
342 | 11.9k | SCLogDebug("seg starts after list and ends after list"); |
343 | | |
344 | 11.9k | switch (stream->os_policy) { |
345 | 0 | case OS_POLICY_SOLARIS: |
346 | 0 | case OS_POLICY_HPUX11: |
347 | 0 | if (data_is_different) { |
348 | 0 | use_new_data = 1; |
349 | 0 | } |
350 | 0 | break; |
351 | 11.9k | } |
352 | 11.9k | } else { |
353 | 2.71k | SCLogDebug("seg starts after list and ends before list end"); |
354 | | |
355 | 2.71k | } |
356 | 15.8k | } |
357 | 572k | } |
358 | | |
359 | 572k | SCLogDebug("data_is_different %s, use_new_data %s", |
360 | 572k | data_is_different ? "yes" : "no", |
361 | 572k | use_new_data ? "yes" : "no"); |
362 | | |
363 | | /* if the data is different and we don't want to use the new (seg) |
364 | | * data, we have to update buf with the list data */ |
365 | 572k | if (data_is_different && !use_new_data) { |
366 | | /* we need to copy list into seg */ |
367 | 254k | uint32_t list_offset = 0; |
368 | 254k | uint32_t seg_offset = 0; |
369 | 254k | uint32_t list_len; |
370 | 254k | uint16_t seg_len = p->payload_len; |
371 | 254k | uint32_t list_seq = list->seq; |
372 | | |
373 | 254k | const uint8_t *list_data; |
374 | 254k | StreamingBufferSegmentGetData(&stream->sb, &list->sbseg, &list_data, &list_len); |
375 | 254k | DEBUG_VALIDATE_BUG_ON(list_len > USHRT_MAX); |
376 | 254k | if (list_data == NULL || list_len == 0 || list_len > USHRT_MAX) |
377 | 271 | return 0; |
378 | | |
379 | | /* if list seg is partially before base_seq, list_len (from stream) and |
380 | | * TCP_SEG_LEN(list) will not be the same */ |
381 | 254k | if (SEQ_GEQ(list->seq, stream->base_seq)) { |
382 | 244k | ; |
383 | 244k | } else { |
384 | 9.63k | list_seq = stream->base_seq; |
385 | 9.63k | list_len = SEG_SEQ_RIGHT_EDGE(list) - stream->base_seq; |
386 | 9.63k | } |
387 | | |
388 | 254k | if (SEQ_LT(seg->seq, list_seq)) { |
389 | 6.28k | seg_offset = list_seq - seg->seq; |
390 | 6.28k | seg_len -= seg_offset; |
391 | 247k | } else if (SEQ_GT(seg->seq, list_seq)) { |
392 | 9.94k | list_offset = seg->seq - list_seq; |
393 | 9.94k | list_len -= list_offset; |
394 | 9.94k | } |
395 | | |
396 | 254k | if (SEQ_LT(seg->seq + seg_offset + seg_len, list_seq + list_offset + list_len)) { |
397 | 26.5k | list_len -= (list_seq + list_offset + list_len) - (seg->seq + seg_offset + seg_len); |
398 | 26.5k | } |
399 | 254k | SCLogDebug("here goes nothing: list %u %u, seg %u %u", list_offset, list_len, seg_offset, seg_len); |
400 | | |
401 | | //PrintRawDataFp(stdout, list_data + list_offset, list_len); |
402 | | //PrintRawDataFp(stdout, buf + seg_offset, seg_len); |
403 | | |
404 | 254k | memcpy(buf + seg_offset, list_data + list_offset, list_len); |
405 | | //PrintRawDataFp(stdout, buf, p->payload_len); |
406 | 254k | } |
407 | 572k | return (check_overlap_different_data && data_is_different); |
408 | 572k | } |
409 | | |
410 | | /** \internal |
411 | | * \brief walk segment tree backwards to see if there are overlaps |
412 | | * |
413 | | * Walk back from the current segment which is already in the tree. |
414 | | * We walk until we can't possibly overlap anymore. |
415 | | */ |
416 | | static int DoHandleDataCheckBackwards(TcpStream *stream, |
417 | | TcpSegment *seg, uint8_t *buf, Packet *p) |
418 | 254k | { |
419 | 254k | int retval = 0; |
420 | | |
421 | 254k | SCLogDebug("check tree backwards: insert data for segment %p seq %u len %u re %u", |
422 | 254k | seg, seg->seq, TCP_SEG_LEN(seg), SEG_SEQ_RIGHT_EDGE(seg)); |
423 | | |
424 | | /* check backwards */ |
425 | 254k | TcpSegment *tree_seg = NULL, *s = seg; |
426 | 528k | RB_FOREACH_REVERSE_FROM(tree_seg, TCPSEG, s) { |
427 | 528k | if (tree_seg == seg) |
428 | 254k | continue; |
429 | | |
430 | 274k | int overlap = 0; |
431 | 274k | if (SEQ_LEQ(SEG_SEQ_RIGHT_EDGE(tree_seg), stream->base_seq)) { |
432 | | // segment entirely before base_seq |
433 | 23 | ; |
434 | 274k | } else if (SEQ_LEQ(tree_seg->seq + tree_seg->payload_len, seg->seq)) { |
435 | 226k | SCLogDebug("list segment too far to the left, no more overlap will be found"); |
436 | 226k | break; |
437 | 226k | } else if (SEQ_GT(SEG_SEQ_RIGHT_EDGE(tree_seg), seg->seq)) { |
438 | 48.0k | overlap = 1; |
439 | 48.0k | } |
440 | | |
441 | 48.0k | SCLogDebug("(back) tree seg %u len %u re %u overlap? %s", |
442 | 48.0k | tree_seg->seq, TCP_SEG_LEN(tree_seg), |
443 | 48.0k | SEG_SEQ_RIGHT_EDGE(tree_seg), overlap ? "yes" : "no"); |
444 | | |
445 | 48.0k | if (overlap) { |
446 | 48.0k | retval |= DoHandleDataOverlap(stream, tree_seg, seg, buf, p); |
447 | 48.0k | } |
448 | 48.0k | } |
449 | 254k | return retval; |
450 | 254k | } |
451 | | |
452 | | /** \internal |
453 | | * \brief walk segment tree in forward direction to see if there are overlaps |
454 | | * |
455 | | * Walk forward from the current segment which is already in the tree. |
456 | | * We walk until the next segs start with a SEQ beyond our right edge. |
457 | | * |
458 | | * \retval 1 data was different |
459 | | * \retval 0 data was the same |
460 | | */ |
461 | | static int DoHandleDataCheckForward(TcpStream *stream, |
462 | | TcpSegment *seg, uint8_t *buf, Packet *p) |
463 | 228k | { |
464 | 228k | int retval = 0; |
465 | | |
466 | 228k | uint32_t seg_re = SEG_SEQ_RIGHT_EDGE(seg); |
467 | | |
468 | 228k | SCLogDebug("check list forward: insert data for segment %p seq %u len %u re %u", |
469 | 228k | seg, seg->seq, TCP_SEG_LEN(seg), seg_re); |
470 | | |
471 | 228k | TcpSegment *tree_seg = NULL, *s = seg; |
472 | 480k | RB_FOREACH_FROM(tree_seg, TCPSEG, s) { |
473 | 480k | if (tree_seg == seg) |
474 | 228k | continue; |
475 | | |
476 | 251k | int overlap = 0; |
477 | 251k | if (SEQ_GT(seg_re, tree_seg->seq)) |
478 | 39.5k | overlap = 1; |
479 | 212k | else if (SEQ_LEQ(seg_re, tree_seg->seq)) { |
480 | 212k | SCLogDebug("tree segment %u too far ahead, " |
481 | 212k | "no more overlaps can happen", tree_seg->seq); |
482 | 212k | break; |
483 | 212k | } |
484 | | |
485 | 39.5k | SCLogDebug("(fwd) in-tree seg %u len %u re %u overlap? %s", |
486 | 39.5k | tree_seg->seq, TCP_SEG_LEN(tree_seg), |
487 | 39.5k | SEG_SEQ_RIGHT_EDGE(tree_seg), overlap ? "yes" : "no"); |
488 | | |
489 | 39.5k | if (overlap) { |
490 | 39.5k | retval |= DoHandleDataOverlap(stream, tree_seg, seg, buf, p); |
491 | 39.5k | } |
492 | 39.5k | } |
493 | 228k | return retval; |
494 | 228k | } |
495 | | |
496 | | /** |
497 | | * \param tree_seg in-tree duplicate of `seg` |
498 | | * \retval res 0 ok, -1 insertion error due to memcap |
499 | | */ |
500 | | static int DoHandleData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, |
501 | | TcpStream *stream, TcpSegment *seg, TcpSegment *tree_seg, Packet *p) |
502 | 499k | { |
503 | 499k | int result = 0; |
504 | 499k | TcpSegment *handle = seg; |
505 | | |
506 | 499k | SCLogDebug("insert data for segment %p seq %u len %u re %u", |
507 | 499k | seg, seg->seq, TCP_SEG_LEN(seg), SEG_SEQ_RIGHT_EDGE(seg)); |
508 | | |
509 | | /* create temporary buffer to contain the data we will insert. Overlap |
510 | | * handling may update it. By using this we don't have to track whether |
511 | | * parts of the data are already inserted or not. */ |
512 | 499k | uint8_t buf[p->payload_len]; |
513 | 499k | memcpy(buf, p->payload, p->payload_len); |
514 | | |
515 | | /* if tree_seg is set, we have an exact duplicate that we need to check */ |
516 | 499k | if (tree_seg) { |
517 | 485k | DoHandleDataOverlap(stream, tree_seg, seg, buf, p); |
518 | 485k | handle = tree_seg; |
519 | 485k | } |
520 | | |
521 | 499k | const bool is_head = !(TCPSEG_RB_PREV(handle)); |
522 | 499k | const bool is_tail = !(TCPSEG_RB_NEXT(handle)); |
523 | | |
524 | | /* new list head */ |
525 | 499k | if (is_head && !is_tail) { |
526 | 58.4k | result = DoHandleDataCheckForward(stream, handle, buf, p); |
527 | | |
528 | | /* new list tail */ |
529 | 441k | } else if (!is_head && is_tail) { |
530 | 83.9k | result = DoHandleDataCheckBackwards(stream, handle, buf, p); |
531 | | |
532 | | /* middle of the list */ |
533 | 357k | } else if (!is_head && !is_tail) { |
534 | 170k | result = DoHandleDataCheckBackwards(stream, handle, buf, p); |
535 | 170k | result |= DoHandleDataCheckForward(stream, handle, buf, p); |
536 | 170k | } |
537 | | |
538 | | /* we had an overlap with different data */ |
539 | 499k | if (result) { |
540 | 27.4k | StreamTcpSetEvent(p, STREAM_REASSEMBLY_OVERLAP_DIFFERENT_DATA); |
541 | 27.4k | StatsIncr(tv, ra_ctx->counter_tcp_reass_overlap_diff_data); |
542 | 27.4k | } |
543 | | |
544 | | /* insert the temp buffer now that we've (possibly) updated |
545 | | * it to account for the overlap policies */ |
546 | 499k | int res = InsertSegmentDataCustom(stream, handle, buf, p->payload_len); |
547 | 499k | if (res != SC_OK) { |
548 | 0 | if (res == SC_ENOMEM) { |
549 | 0 | StatsIncr(tv, ra_ctx->counter_tcp_segment_memcap); |
550 | 0 | StreamTcpSetEvent(p, STREAM_REASSEMBLY_INSERT_MEMCAP); |
551 | 0 | } else if (res == SC_ELIMIT) { |
552 | 0 | StreamTcpSetEvent(p, STREAM_REASSEMBLY_INSERT_LIMIT); |
553 | 0 | } else if (res == SC_EINVAL) { |
554 | 0 | StreamTcpSetEvent(p, STREAM_REASSEMBLY_INSERT_INVALID); |
555 | 0 | } else { |
556 | 0 | DEBUG_VALIDATE_BUG_ON(1); |
557 | 0 | } |
558 | 0 | return -1; |
559 | 0 | } |
560 | | |
561 | 499k | return 0; |
562 | 499k | } |
563 | | |
564 | | /** \internal |
565 | | * \brief Add the header data to the segment |
566 | | * \param rp packet to take the headers from. Might differ from `pp` in tunnels. |
567 | | * \param pp packet to take the payload size from. |
568 | | */ |
569 | | static void StreamTcpSegmentAddPacketDataDo(TcpSegment *seg, const Packet *rp, const Packet *pp) |
570 | 0 | { |
571 | 0 | if (GET_PKT_DATA(rp) != NULL && GET_PKT_LEN(rp) > pp->payload_len) { |
572 | 0 | seg->pcap_hdr_storage->ts.tv_sec = SCTIME_SECS(rp->ts); |
573 | 0 | seg->pcap_hdr_storage->ts.tv_usec = SCTIME_USECS(rp->ts); |
574 | 0 | seg->pcap_hdr_storage->pktlen = GET_PKT_LEN(rp) - pp->payload_len; |
575 | | /* |
576 | | * pkt_hdr members are initially allocated 64 bytes of memory. Thus, |
577 | | * need to check that this is sufficient and allocate more memory if |
578 | | * not. |
579 | | */ |
580 | 0 | if (seg->pcap_hdr_storage->pktlen > seg->pcap_hdr_storage->alloclen) { |
581 | 0 | uint8_t *tmp_pkt_hdr = StreamTcpReassembleRealloc(seg->pcap_hdr_storage->pkt_hdr, |
582 | 0 | seg->pcap_hdr_storage->alloclen, seg->pcap_hdr_storage->pktlen); |
583 | 0 | if (tmp_pkt_hdr == NULL) { |
584 | 0 | SCLogDebug("Failed to realloc"); |
585 | 0 | seg->pcap_hdr_storage->ts.tv_sec = 0; |
586 | 0 | seg->pcap_hdr_storage->ts.tv_usec = 0; |
587 | 0 | seg->pcap_hdr_storage->pktlen = 0; |
588 | 0 | return; |
589 | 0 | } else { |
590 | 0 | seg->pcap_hdr_storage->pkt_hdr = tmp_pkt_hdr; |
591 | 0 | seg->pcap_hdr_storage->alloclen = GET_PKT_LEN(rp) - pp->payload_len; |
592 | 0 | } |
593 | 0 | } |
594 | 0 | memcpy(seg->pcap_hdr_storage->pkt_hdr, GET_PKT_DATA(rp), |
595 | 0 | (size_t)GET_PKT_LEN(rp) - pp->payload_len); |
596 | 0 | } else { |
597 | 0 | seg->pcap_hdr_storage->ts.tv_sec = 0; |
598 | 0 | seg->pcap_hdr_storage->ts.tv_usec = 0; |
599 | 0 | seg->pcap_hdr_storage->pktlen = 0; |
600 | 0 | } |
601 | 0 | } |
602 | | |
603 | | /** |
604 | | * \brief Adds the following information to the TcpSegment from the current |
605 | | * packet being processed: time values, packet length, and the |
606 | | * header data of the packet. This information is added to the TcpSegment so |
607 | | * that it can be used in pcap capturing (log-pcap-stream) to dump the tcp |
608 | | * session at the beginning of the pcap capture. |
609 | | * \param seg TcpSegment where information is being stored. |
610 | | * \param p Packet being processed. |
611 | | * \param tv Thread-specific variables. |
612 | | * \param ra_ctx TcpReassembly thread-specific variables |
613 | | */ |
614 | | static void StreamTcpSegmentAddPacketData( |
615 | | TcpSegment *seg, Packet *p, ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx) |
616 | 0 | { |
617 | 0 | if (seg->pcap_hdr_storage == NULL || seg->pcap_hdr_storage->pkt_hdr == NULL) { |
618 | 0 | return; |
619 | 0 | } |
620 | | |
621 | 0 | if (IS_TUNNEL_PKT(p) && !IS_TUNNEL_ROOT_PKT(p)) { |
622 | 0 | Packet *rp = p->root; |
623 | 0 | StreamTcpSegmentAddPacketDataDo(seg, rp, p); |
624 | 0 | } else { |
625 | 0 | StreamTcpSegmentAddPacketDataDo(seg, p, p); |
626 | 0 | } |
627 | 0 | } |
628 | | |
629 | | /** |
630 | | * \return 0 ok |
631 | | * \return -1 segment not inserted due to memcap issue |
632 | | * |
633 | | * \param seg segment, this function takes total ownership |
634 | | * |
635 | | * In case of error, this function returns the segment to the pool |
636 | | */ |
637 | | int StreamTcpReassembleInsertSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, |
638 | | TcpStream *stream, TcpSegment *seg, Packet *p, uint8_t *pkt_data, uint16_t pkt_datalen) |
639 | 7.40M | { |
640 | 7.40M | SCEnter(); |
641 | | |
642 | 7.40M | TcpSegment *dup_seg = NULL; |
643 | | |
644 | | /* insert segment into list. Note: doesn't handle the data */ |
645 | 7.40M | int r = DoInsertSegment (stream, seg, &dup_seg, p); |
646 | | |
647 | 7.40M | if (IsTcpSessionDumpingEnabled()) { |
648 | 0 | StreamTcpSegmentAddPacketData(seg, p, tv, ra_ctx); |
649 | 0 | } |
650 | | |
651 | 7.40M | if (likely(r == 0)) { |
652 | | /* no overlap, straight data insert */ |
653 | 6.90M | int res = InsertSegmentDataCustom(stream, seg, pkt_data, pkt_datalen); |
654 | 6.90M | if (res != SC_OK) { |
655 | 0 | StatsIncr(tv, ra_ctx->counter_tcp_reass_data_normal_fail); |
656 | 0 | StreamTcpRemoveSegmentFromStream(stream, seg); |
657 | 0 | StreamTcpSegmentReturntoPool(seg); |
658 | 0 | if (res == SC_ENOMEM) { |
659 | 0 | StatsIncr(tv, ra_ctx->counter_tcp_segment_memcap); |
660 | 0 | SCReturnInt(-SC_ENOMEM); |
661 | 0 | } |
662 | 0 | SCReturnInt(-1); |
663 | 0 | } |
664 | | |
665 | 6.90M | } else if (r == 1 || r == 2) { |
666 | 499k | SCLogDebug("overlap (%s%s)", r == 1 ? "normal" : "", r == 2 ? "duplicate" : ""); |
667 | | |
668 | 499k | if (r == 2) { |
669 | 485k | SCLogDebug("dup_seg %p", dup_seg); |
670 | 485k | } |
671 | | |
672 | | /* XXX should we exclude 'retransmissions' here? */ |
673 | 499k | StatsIncr(tv, ra_ctx->counter_tcp_reass_overlap); |
674 | | |
675 | | /* now let's consider the data in the overlap case */ |
676 | 499k | int res = DoHandleData(tv, ra_ctx, stream, seg, dup_seg, p); |
677 | 499k | if (res < 0) { |
678 | 0 | StatsIncr(tv, ra_ctx->counter_tcp_reass_data_overlap_fail); |
679 | |
|
680 | 0 | if (r == 1) // r == 2 mean seg wasn't added to stream |
681 | 0 | StreamTcpRemoveSegmentFromStream(stream, seg); |
682 | |
|
683 | 0 | StreamTcpSegmentReturntoPool(seg); |
684 | 0 | SCReturnInt(-1); |
685 | 0 | } |
686 | 499k | if (r == 2) { |
687 | 485k | SCLogDebug("duplicate segment %u/%u, discard it", |
688 | 485k | seg->seq, seg->payload_len); |
689 | | |
690 | 485k | StreamTcpSegmentReturntoPool(seg); |
691 | | #ifdef DEBUG |
692 | | if (SCLogDebugEnabled()) { |
693 | | TcpSegment *s = NULL, *safe = NULL; |
694 | | RB_FOREACH_SAFE(s, TCPSEG, &stream->seg_tree, safe) |
695 | | { |
696 | | SCLogDebug("tree: seg %p, SEQ %"PRIu32", LEN %"PRIu16", SUM %"PRIu32"%s%s%s", |
697 | | s, s->seq, TCP_SEG_LEN(s), |
698 | | (uint32_t)(s->seq + TCP_SEG_LEN(s)), |
699 | | s->seq == seg->seq ? " DUPLICATE" : "", |
700 | | TCPSEG_RB_PREV(s) == NULL ? " HEAD" : "", |
701 | | TCPSEG_RB_NEXT(s) == NULL ? " TAIL" : ""); |
702 | | } |
703 | | } |
704 | | #endif |
705 | 485k | } |
706 | 499k | } else { |
707 | | // EINVAL |
708 | 0 | StreamTcpSegmentReturntoPool(seg); |
709 | 0 | } |
710 | | |
711 | 7.40M | SCReturnInt(0); |
712 | 7.40M | } |
713 | | |
714 | | |
715 | | /* |
716 | | * Pruning & removal |
717 | | */ |
718 | | |
719 | | |
720 | | static inline bool SegmentInUse(const TcpStream *stream, const TcpSegment *seg) |
721 | 18.2M | { |
722 | | /* if proto detect isn't done, we're not returning */ |
723 | 18.2M | if (!(stream->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) { |
724 | 18.2M | if (!(StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream))) { |
725 | 1.63M | SCReturnInt(true); |
726 | 1.63M | } |
727 | 18.2M | } |
728 | | |
729 | 18.2M | SCReturnInt(false); |
730 | 18.2M | } |
731 | | |
732 | | |
733 | | /** \internal |
734 | | * \brief check if we can remove a segment from our segment list |
735 | | * |
736 | | * \retval true |
737 | | * \retval false |
738 | | */ |
739 | | static inline bool StreamTcpReturnSegmentCheck(const TcpStream *stream, const TcpSegment *seg) |
740 | 12.5M | { |
741 | 12.5M | if (SegmentInUse(stream, seg)) { |
742 | 1.61M | SCReturnInt(false); |
743 | 1.61M | } |
744 | | |
745 | 10.8M | if (!(StreamingBufferSegmentIsBeforeWindow(&stream->sb, &seg->sbseg))) { |
746 | 8.12M | SCReturnInt(false); |
747 | 8.12M | } |
748 | | |
749 | 10.8M | SCReturnInt(true); |
750 | 10.8M | } |
751 | | |
752 | | static inline uint64_t GetLeftEdgeForApp(Flow *f, TcpSession *ssn, TcpStream *stream) |
753 | 11.1M | { |
754 | 11.1M | const FramesContainer *frames_container = AppLayerFramesGetContainer(f); |
755 | 11.1M | if (frames_container == NULL) |
756 | 4.65M | return STREAM_APP_PROGRESS(stream); |
757 | | |
758 | 6.52M | const Frames *frames = |
759 | 6.52M | stream == &ssn->client ? &frames_container->toserver : &frames_container->toclient; |
760 | | // const uint64_t x = FramesLeftEdge(stream, frames); |
761 | | // BUG_ON(x != (frames->left_edge_rel + STREAM_BASE_OFFSET(stream))); |
762 | | // return x; |
763 | 6.52M | const uint64_t o = (uint64_t)frames->left_edge_rel + STREAM_BASE_OFFSET(stream); |
764 | 6.52M | SCLogDebug( |
765 | 6.52M | "%s: frames left edge: %" PRIu64, &ssn->client == stream ? "toserver" : "toclient", o); |
766 | 6.52M | return o; |
767 | 11.1M | } |
768 | | |
769 | | static inline uint64_t GetLeftEdge(Flow *f, TcpSession *ssn, TcpStream *stream) |
770 | 6.24M | { |
771 | 6.24M | uint64_t left_edge = 0; |
772 | 6.24M | const bool use_app = !(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED); |
773 | 6.24M | const bool use_raw = !(stream->flags & STREAMTCP_STREAM_FLAG_DISABLE_RAW); |
774 | 6.24M | const bool use_log = stream_config.streaming_log_api; |
775 | 6.24M | SCLogDebug("use_app %d use_raw %d use_log %d tcp win %u", use_app, use_raw, use_log, |
776 | 6.24M | stream->window); |
777 | | |
778 | 6.24M | if (use_raw) { |
779 | 1.13M | uint64_t raw_progress = STREAM_RAW_PROGRESS(stream); |
780 | | |
781 | 1.13M | if (StreamTcpInlineMode() == TRUE) { |
782 | 0 | uint32_t chunk_size = (stream == &ssn->client) ? |
783 | 0 | stream_config.reassembly_toserver_chunk_size : |
784 | 0 | stream_config.reassembly_toclient_chunk_size; |
785 | 0 | if (raw_progress < (uint64_t)chunk_size) { |
786 | 0 | raw_progress = 0; |
787 | 0 | } else { |
788 | 0 | raw_progress -= (uint64_t)chunk_size; |
789 | 0 | } |
790 | 0 | } |
791 | | |
792 | | /* apply min inspect depth: if it is set we need to keep data |
793 | | * before the raw progress. */ |
794 | 1.13M | if (use_app && stream->min_inspect_depth && ssn->state < TCP_CLOSED) { |
795 | 296k | if (raw_progress < stream->min_inspect_depth) |
796 | 220k | raw_progress = 0; |
797 | 76.0k | else |
798 | 76.0k | raw_progress -= stream->min_inspect_depth; |
799 | | |
800 | 296k | SCLogDebug("stream->min_inspect_depth %u, raw_progress %"PRIu64, |
801 | 296k | stream->min_inspect_depth, raw_progress); |
802 | 296k | } |
803 | | |
804 | 1.13M | if (use_app) { |
805 | 1.00M | const uint64_t app_le = GetLeftEdgeForApp(f, ssn, stream); |
806 | 1.00M | left_edge = MIN(app_le, raw_progress); |
807 | 1.00M | SCLogDebug("left_edge %" PRIu64 ", using both app:%" PRIu64 ", raw:%" PRIu64, left_edge, |
808 | 1.00M | app_le, raw_progress); |
809 | 1.00M | } else { |
810 | 135k | left_edge = raw_progress; |
811 | 135k | SCLogDebug("left_edge %"PRIu64", using only raw:%"PRIu64, |
812 | 135k | left_edge, raw_progress); |
813 | 135k | } |
814 | 5.11M | } else if (use_app) { |
815 | 5.11M | const uint64_t app_le = GetLeftEdgeForApp(f, ssn, stream); |
816 | 5.11M | left_edge = app_le; |
817 | 5.11M | SCLogDebug("left_edge %" PRIu64 ", using only app:%" PRIu64, left_edge, app_le); |
818 | 5.11M | } else { |
819 | 0 | left_edge = StreamingBufferGetConsecutiveDataRightEdge(&stream->sb); |
820 | 0 | SCLogDebug("no app & raw: left_edge %"PRIu64" (full stream)", left_edge); |
821 | 0 | } |
822 | | |
823 | 6.24M | if (use_log) { |
824 | 0 | if (use_app || use_raw) { |
825 | 0 | left_edge = MIN(left_edge, STREAM_LOG_PROGRESS(stream)); |
826 | 0 | } else { |
827 | 0 | left_edge = STREAM_LOG_PROGRESS(stream); |
828 | 0 | } |
829 | 0 | } |
830 | | |
831 | 6.24M | uint64_t last_ack_abs = STREAM_BASE_OFFSET(stream); |
832 | 6.24M | if (STREAM_LASTACK_GT_BASESEQ(stream)) { |
833 | 4.46M | last_ack_abs += (stream->last_ack - stream->base_seq); |
834 | 4.46M | } |
835 | | /* in IDS mode we shouldn't see the base_seq pass last_ack */ |
836 | 6.24M | DEBUG_VALIDATE_BUG_ON(last_ack_abs < left_edge && StreamTcpInlineMode() == FALSE && !f->ffr && |
837 | 6.24M | ssn->state < TCP_CLOSED); |
838 | 6.24M | left_edge = MIN(left_edge, last_ack_abs); |
839 | | |
840 | | /* if we're told to look for overlaps with different data we should |
841 | | * consider data that is ack'd as well. Injected packets may have |
842 | | * been ack'd or injected packet may be too late. */ |
843 | 6.24M | if (StreamTcpInlineMode() == FALSE && check_overlap_different_data) { |
844 | 2.86M | const uint32_t window = stream->window ? stream->window : 4096; |
845 | 2.86M | if (window < left_edge) |
846 | 587k | left_edge -= window; |
847 | 2.27M | else |
848 | 2.27M | left_edge = 0; |
849 | | |
850 | 2.86M | SCLogDebug("stream:%p left_edge %"PRIu64, stream, left_edge); |
851 | 2.86M | } |
852 | | |
853 | 6.24M | if (left_edge > 0) { |
854 | | /* we know left edge based on the progress values now, |
855 | | * lets adjust it to make sure in-use segments still have |
856 | | * data */ |
857 | 2.53M | TcpSegment *seg = NULL; |
858 | 6.56M | RB_FOREACH(seg, TCPSEG, &stream->seg_tree) { |
859 | 6.56M | if (TCP_SEG_OFFSET(seg) > left_edge) { |
860 | 2.07M | SCLogDebug("seg beyond left_edge, we're done"); |
861 | 2.07M | break; |
862 | 2.07M | } |
863 | | |
864 | 4.49M | if (SegmentInUse(stream, seg)) { |
865 | 11.0k | left_edge = TCP_SEG_OFFSET(seg); |
866 | 11.0k | SCLogDebug("in-use seg before left_edge, adjust to %"PRIu64" and bail", left_edge); |
867 | 11.0k | break; |
868 | 11.0k | } |
869 | 4.49M | } |
870 | 2.53M | } |
871 | | |
872 | 6.24M | return left_edge; |
873 | 6.24M | } |
874 | | |
875 | | static void StreamTcpRemoveSegmentFromStream(TcpStream *stream, TcpSegment *seg) |
876 | 2.77M | { |
877 | 2.77M | RB_REMOVE(TCPSEG, &stream->seg_tree, seg); |
878 | 2.77M | } |
879 | | |
880 | | /** \brief Remove idle TcpSegments from TcpSession |
881 | | * |
882 | | * Checks app progress and raw progress and progresses them |
883 | | * if needed, slides the streaming buffer, then gets rid of |
884 | | * excess segments. |
885 | | * |
886 | | * \param f flow |
887 | | * \param flags direction flags |
888 | | */ |
889 | | void StreamTcpPruneSession(Flow *f, uint8_t flags) |
890 | 6.77M | { |
891 | 6.77M | SCEnter(); |
892 | | |
893 | 6.77M | if (f == NULL || f->protoctx == NULL) { |
894 | 0 | SCReturn; |
895 | 0 | } |
896 | | |
897 | 6.77M | TcpSession *ssn = f->protoctx; |
898 | 6.77M | TcpStream *stream = NULL; |
899 | | |
900 | 6.77M | if (flags & STREAM_TOSERVER) { |
901 | 3.73M | stream = &ssn->client; |
902 | 3.73M | } else if (flags & STREAM_TOCLIENT) { |
903 | 3.04M | stream = &ssn->server; |
904 | 3.04M | } else { |
905 | 0 | SCReturn; |
906 | 0 | } |
907 | | |
908 | 6.77M | if (stream->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) { |
909 | 502k | return; |
910 | 502k | } |
911 | | |
912 | 6.27M | if (stream->flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED) { |
913 | 0 | stream->flags |= STREAMTCP_STREAM_FLAG_NOREASSEMBLY; |
914 | 0 | SCLogDebug("ssn %p / stream %p: reassembly depth reached, " |
915 | 0 | "STREAMTCP_STREAM_FLAG_NOREASSEMBLY set", ssn, stream); |
916 | 0 | StreamTcpReturnStreamSegments(stream); |
917 | 0 | StreamingBufferClear(&stream->sb, &stream_config.sbcnf); |
918 | 0 | return; |
919 | |
|
920 | 6.27M | } else if ((ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) && |
921 | 161k | (stream->flags & STREAMTCP_STREAM_FLAG_DISABLE_RAW)) { |
922 | 26.4k | SCLogDebug("ssn %p / stream %p: both app and raw are done, " |
923 | 26.4k | "STREAMTCP_STREAM_FLAG_NOREASSEMBLY set", ssn, stream); |
924 | 26.4k | stream->flags |= STREAMTCP_STREAM_FLAG_NOREASSEMBLY; |
925 | 26.4k | StreamTcpReturnStreamSegments(stream); |
926 | 26.4k | StreamingBufferClear(&stream->sb, &stream_config.sbcnf); |
927 | 26.4k | return; |
928 | 26.4k | } |
929 | | |
930 | 6.24M | const uint64_t left_edge = GetLeftEdge(f, ssn, stream); |
931 | 6.24M | SCLogDebug("buffer left_edge %" PRIu64, left_edge); |
932 | 6.24M | if (left_edge && left_edge > STREAM_BASE_OFFSET(stream)) { |
933 | 689k | uint32_t slide = left_edge - STREAM_BASE_OFFSET(stream); |
934 | 689k | SCLogDebug("buffer sliding %u to offset %"PRIu64, slide, left_edge); |
935 | | |
936 | 689k | if (!(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED)) { |
937 | 685k | AppLayerFramesSlide(f, slide, flags & (STREAM_TOSERVER | STREAM_TOCLIENT)); |
938 | 685k | } |
939 | 689k | StreamingBufferSlideToOffset(&stream->sb, &stream_config.sbcnf, left_edge); |
940 | 689k | stream->base_seq += slide; |
941 | | |
942 | 689k | if (slide <= stream->app_progress_rel) { |
943 | 686k | stream->app_progress_rel -= slide; |
944 | 686k | } else { |
945 | 3.77k | stream->app_progress_rel = 0; |
946 | 3.77k | } |
947 | 689k | if (slide <= stream->raw_progress_rel) { |
948 | 27.0k | stream->raw_progress_rel -= slide; |
949 | 662k | } else { |
950 | 662k | stream->raw_progress_rel = 0; |
951 | 662k | } |
952 | 689k | if (slide <= stream->log_progress_rel) { |
953 | 0 | stream->log_progress_rel -= slide; |
954 | 689k | } else { |
955 | 689k | stream->log_progress_rel = 0; |
956 | 689k | } |
957 | | |
958 | 689k | SCLogDebug("stream base_seq %u at stream offset %"PRIu64, |
959 | 689k | stream->base_seq, STREAM_BASE_OFFSET(stream)); |
960 | 689k | } |
961 | | |
962 | | /* loop through the segments and remove all not in use */ |
963 | 6.24M | TcpSegment *seg = NULL, *safe = NULL; |
964 | 6.24M | RB_FOREACH_SAFE(seg, TCPSEG, &stream->seg_tree, safe) |
965 | 7.37M | { |
966 | 7.37M | SCLogDebug("seg %p, SEQ %"PRIu32", LEN %"PRIu16", SUM %"PRIu32, |
967 | 7.37M | seg, seg->seq, TCP_SEG_LEN(seg), |
968 | 7.37M | (uint32_t)(seg->seq + TCP_SEG_LEN(seg))); |
969 | | |
970 | 7.37M | if (StreamTcpReturnSegmentCheck(stream, seg) == 0) { |
971 | 5.25M | SCLogDebug("not removing segment"); |
972 | 5.25M | break; |
973 | 5.25M | } |
974 | | |
975 | 2.12M | StreamTcpRemoveSegmentFromStream(stream, seg); |
976 | 2.12M | StreamTcpSegmentReturntoPool(seg); |
977 | 2.12M | SCLogDebug("removed segment"); |
978 | 2.12M | continue; |
979 | 7.37M | } |
980 | | |
981 | 6.24M | SCReturn; |
982 | 6.27M | } |
983 | | |
984 | | |
985 | | /* |
986 | | * unittests |
987 | | */ |
988 | | |
989 | | #ifdef UNITTESTS |
990 | | #include "tests/stream-tcp-list.c" |
991 | | #endif |