/src/suricata/src/stream-tcp-list.c
Line | Count | Source (jump to first uncovered line) |
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 | 637 | { |
42 | 637 | check_overlap_different_data = 1; |
43 | 637 | } |
44 | | |
45 | | /* |
46 | | * Inserts and overlap handling |
47 | | */ |
48 | | |
49 | | RB_GENERATE(TCPSEG, TcpSegment, rb, TcpSegmentCompare); |
50 | | |
51 | | int TcpSegmentCompare(struct TcpSegment *a, struct TcpSegment *b) |
52 | 93.8M | { |
53 | 93.8M | if (SEQ_GT(a->seq, b->seq)) |
54 | 92.9M | return 1; |
55 | 862k | else if (SEQ_LT(a->seq, b->seq)) |
56 | 290k | return -1; |
57 | 572k | else { |
58 | 572k | if (a->payload_len == b->payload_len) |
59 | 531k | return 0; |
60 | 40.5k | else if (a->payload_len > b->payload_len) |
61 | 20.0k | return 1; |
62 | 20.5k | else |
63 | 20.5k | return -1; |
64 | 572k | } |
65 | 93.8M | } |
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 | 8.24M | { |
78 | 8.24M | uint64_t stream_offset; |
79 | 8.24M | uint32_t data_offset; |
80 | | |
81 | 8.24M | if (likely(SEQ_GEQ(seg->seq, stream->base_seq))) { |
82 | 8.22M | stream_offset = STREAM_BASE_OFFSET(stream) + (seg->seq - stream->base_seq); |
83 | 8.22M | data_offset = 0; |
84 | 8.22M | } else { |
85 | | /* segment is partly before base_seq */ |
86 | 14.5k | data_offset = stream->base_seq - seg->seq; |
87 | 14.5k | stream_offset = STREAM_BASE_OFFSET(stream); |
88 | 14.5k | } |
89 | | |
90 | 8.24M | SCLogDebug("stream %p buffer %p, stream_offset %"PRIu64", " |
91 | 8.24M | "data_offset %"PRIu16", SEQ %u BASE %u, data_len %u", |
92 | 8.24M | stream, &stream->sb, stream_offset, |
93 | 8.24M | data_offset, seg->seq, stream->base_seq, data_len); |
94 | 8.24M | DEBUG_VALIDATE_BUG_ON(data_offset > data_len); |
95 | 8.24M | if (data_len <= data_offset) { |
96 | 0 | SCReturnInt(SC_OK); |
97 | 0 | } |
98 | | |
99 | 8.24M | int ret = StreamingBufferInsertAt(&stream->sb, &stream_config.sbcnf, &seg->sbseg, |
100 | 8.24M | data + data_offset, data_len - data_offset, stream_offset); |
101 | 8.24M | 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 | 8.24M | SCReturnInt(SC_OK); |
117 | 8.24M | } |
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 | 7.27M | { |
126 | 7.27M | const uint32_t re = SEG_SEQ_RIGHT_EDGE(seg); |
127 | 7.27M | SCLogDebug("start. SEQ %u payload_len %u. Right edge: %u. Seg %p", |
128 | 7.27M | seg->seq, seg->payload_len, re, seg); |
129 | | |
130 | | /* check forward */ |
131 | 7.27M | TcpSegment *next = TCPSEG_RB_NEXT(seg); |
132 | 7.27M | if (next) { |
133 | | // next has same seq, so data must overlap |
134 | 73.7k | if (SEQ_EQ(next->seq, seg->seq)) |
135 | 5.11k | return true; |
136 | | // our right edge is beyond next seq, overlap |
137 | 68.6k | if (SEQ_GT(re, next->seq)) |
138 | 2.66k | return true; |
139 | 68.6k | } |
140 | | /* check backwards */ |
141 | 7.26M | TcpSegment *prev = TCPSEG_RB_PREV(seg); |
142 | 7.26M | if (prev) { |
143 | | // prev has same seq, so data must overlap |
144 | 7.25M | if (SEQ_EQ(prev->seq, seg->seq)) |
145 | 7.56k | return true; |
146 | | // prev's right edge is beyond our seq, overlap |
147 | 7.24M | const uint32_t prev_re = SEG_SEQ_RIGHT_EDGE(prev); |
148 | 7.24M | if (SEQ_GT(prev_re, seg->seq)) |
149 | 2.20k | return true; |
150 | 7.24M | } |
151 | | |
152 | 7.25M | SCLogDebug("no overlap"); |
153 | 7.25M | return false; |
154 | 7.26M | } |
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 | 4.66M | { |
167 | | /* in lossy traffic, we can get here with the wrong sequence numbers */ |
168 | 4.66M | if (SEQ_LEQ(SEG_SEQ_RIGHT_EDGE(seg), stream->base_seq)) { |
169 | 0 | return -EINVAL; |
170 | 0 | } |
171 | | |
172 | | /* fast track */ |
173 | 4.66M | if (RB_EMPTY(&stream->seg_tree)) { |
174 | 248k | SCLogDebug("empty tree, inserting seg %p seq %" PRIu32 ", " |
175 | 248k | "len %" PRIu32 "", seg, seg->seq, TCP_SEG_LEN(seg)); |
176 | 248k | TCPSEG_RB_INSERT(&stream->seg_tree, seg); |
177 | 248k | stream->segs_right_edge = SEG_SEQ_RIGHT_EDGE(seg); |
178 | 248k | return 0; |
179 | 248k | } |
180 | | |
181 | | /* insert and then check if there was any overlap with other segments */ |
182 | 4.41M | TcpSegment *res = TCPSEG_RB_INSERT(&stream->seg_tree, seg); |
183 | 4.41M | if (res) { |
184 | 307k | SCLogDebug("seg has a duplicate in the tree seq %u/%u", |
185 | 307k | res->seq, res->payload_len); |
186 | | /* exact duplicate SEQ + payload_len */ |
187 | 307k | *dup_seg = res; |
188 | 307k | return 2; // duplicate has overlap by definition. |
189 | 4.10M | } else { |
190 | 4.10M | if (SEQ_GT(SEG_SEQ_RIGHT_EDGE(seg), stream->segs_right_edge)) |
191 | 4.05M | stream->segs_right_edge = SEG_SEQ_RIGHT_EDGE(seg); |
192 | | |
193 | | /* insert succeeded, now check if we overlap with someone */ |
194 | 4.10M | if (CheckOverlap(&stream->seg_tree, seg)) { |
195 | 3.82k | SCLogDebug("seg %u has overlap in the tree", seg->seq); |
196 | 3.82k | return 1; |
197 | 3.82k | } |
198 | 4.10M | } |
199 | 4.10M | SCLogDebug("seg %u: no overlap", seg->seq); |
200 | 4.10M | return 0; |
201 | 4.41M | } |
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 | 651k | { |
224 | 651k | SCLogDebug("handle overlap for segment %p seq %u len %u re %u, " |
225 | 651k | "list segment %p seq %u len %u re %u", seg, seg->seq, |
226 | 651k | p->payload_len, SEG_SEQ_RIGHT_EDGE(seg), |
227 | 651k | list, list->seq, TCP_SEG_LEN(list), SEG_SEQ_RIGHT_EDGE(list)); |
228 | | |
229 | 651k | int data_is_different = 0; |
230 | 651k | int use_new_data = 0; |
231 | | |
232 | 651k | 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 | 651k | } else { |
248 | 651k | if (check_overlap_different_data) { |
249 | 440k | if (StreamTcpInlineSegmentCompare(stream, p, list) != 0) { |
250 | 114k | SCLogDebug("data is different from what is in the list"); |
251 | 114k | data_is_different = 1; |
252 | 114k | } |
253 | 440k | } else { |
254 | | /* if we're not checking, assume it's different */ |
255 | 210k | data_is_different = 1; |
256 | 210k | } |
257 | | |
258 | | /* apply overlap policies */ |
259 | | |
260 | 651k | 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 | 651k | if (SEQ_EQ(seg->seq, list->seq)) { |
268 | 613k | SCLogDebug("seg starts at list segment"); |
269 | | |
270 | 613k | if (SEQ_LT(SEG_SEQ_RIGHT_EDGE(seg), SEG_SEQ_RIGHT_EDGE(list))) { |
271 | 41.3k | SCLogDebug("seg ends before list end, end overlapped by list"); |
272 | 571k | } else { |
273 | 571k | if (SEQ_GT(SEG_SEQ_RIGHT_EDGE(seg), SEG_SEQ_RIGHT_EDGE(list))) { |
274 | 40.3k | SCLogDebug("seg ends beyond list end, list overlapped and more"); |
275 | 40.3k | 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 | 40.3k | } |
282 | 531k | } else { |
283 | 531k | SCLogDebug("full overlap"); |
284 | 531k | } |
285 | | |
286 | 571k | 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 | 571k | } |
295 | 571k | } |
296 | | |
297 | | /* new seg starts before list segment */ |
298 | 613k | } else if (SEQ_LT(seg->seq, list->seq)) { |
299 | 14.6k | SCLogDebug("seg starts before list segment"); |
300 | | |
301 | 14.6k | if (SEQ_LT(SEG_SEQ_RIGHT_EDGE(seg), SEG_SEQ_RIGHT_EDGE(list))) { |
302 | 10.6k | SCLogDebug("seg ends before list end, end overlapped by list"); |
303 | 10.6k | } else { |
304 | 3.98k | if (SEQ_GT(SEG_SEQ_RIGHT_EDGE(seg), SEG_SEQ_RIGHT_EDGE(list))) { |
305 | 2.90k | SCLogDebug("seg starts before and fully overlaps list and beyond"); |
306 | 2.90k | } else { |
307 | 1.08k | SCLogDebug("seg starts before and fully overlaps list"); |
308 | 1.08k | } |
309 | | |
310 | 3.98k | 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 | 3.98k | } |
318 | 3.98k | } |
319 | | |
320 | 14.6k | switch (stream->os_policy) { |
321 | 14.6k | case OS_POLICY_BSD: |
322 | 14.6k | case OS_POLICY_HPUX10: |
323 | 14.6k | case OS_POLICY_IRIX: |
324 | 14.6k | case OS_POLICY_WINDOWS: |
325 | 14.6k | case OS_POLICY_WINDOWS2K3: |
326 | 14.6k | case OS_POLICY_OLD_LINUX: |
327 | 14.6k | case OS_POLICY_LINUX: |
328 | 14.6k | case OS_POLICY_MACOS: |
329 | 14.6k | if (data_is_different) { |
330 | 10.8k | use_new_data = 1; |
331 | 10.8k | } |
332 | 14.6k | break; |
333 | 14.6k | } |
334 | | |
335 | | /* new seg starts after list segment */ |
336 | 23.0k | } else { //if (SEQ_GT(seg->seq, list->seq)) { |
337 | 23.0k | SCLogDebug("seg starts after list segment"); |
338 | | |
339 | 23.0k | if (SEQ_EQ(SEG_SEQ_RIGHT_EDGE(seg), SEG_SEQ_RIGHT_EDGE(list))) { |
340 | 1.84k | SCLogDebug("seg after and is fully overlapped by list"); |
341 | 21.2k | } else if (SEQ_GT(SEG_SEQ_RIGHT_EDGE(seg), SEG_SEQ_RIGHT_EDGE(list))) { |
342 | 16.7k | SCLogDebug("seg starts after list and ends after list"); |
343 | | |
344 | 16.7k | 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 | 16.7k | } |
352 | 16.7k | } else { |
353 | 4.49k | SCLogDebug("seg starts after list and ends before list end"); |
354 | | |
355 | 4.49k | } |
356 | 23.0k | } |
357 | 651k | } |
358 | | |
359 | 651k | SCLogDebug("data_is_different %s, use_new_data %s", |
360 | 651k | data_is_different ? "yes" : "no", |
361 | 651k | 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 | 651k | if (data_is_different && !use_new_data) { |
366 | | /* we need to copy list into seg */ |
367 | 314k | uint32_t list_offset = 0; |
368 | 314k | uint32_t seg_offset = 0; |
369 | 314k | uint32_t list_len; |
370 | 314k | uint16_t seg_len = p->payload_len; |
371 | 314k | uint32_t list_seq = list->seq; |
372 | | |
373 | 314k | const uint8_t *list_data; |
374 | 314k | StreamingBufferSegmentGetData(&stream->sb, &list->sbseg, &list_data, &list_len); |
375 | 314k | DEBUG_VALIDATE_BUG_ON(list_len > USHRT_MAX); |
376 | 314k | if (list_data == NULL || list_len == 0 || list_len > USHRT_MAX) |
377 | 566 | 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 | 313k | if (SEQ_GEQ(list->seq, stream->base_seq)) { |
382 | 295k | ; |
383 | 295k | } else { |
384 | 18.4k | list_seq = stream->base_seq; |
385 | 18.4k | list_len = SEG_SEQ_RIGHT_EDGE(list) - stream->base_seq; |
386 | 18.4k | } |
387 | | |
388 | 313k | if (SEQ_LT(seg->seq, list_seq)) { |
389 | 12.3k | seg_offset = list_seq - seg->seq; |
390 | 12.3k | seg_len -= seg_offset; |
391 | 301k | } else if (SEQ_GT(seg->seq, list_seq)) { |
392 | 13.1k | list_offset = seg->seq - list_seq; |
393 | 13.1k | list_len -= list_offset; |
394 | 13.1k | } |
395 | | |
396 | 313k | if (SEQ_LT(seg->seq + seg_offset + seg_len, list_seq + list_offset + list_len)) { |
397 | 40.8k | list_len -= (list_seq + list_offset + list_len) - (seg->seq + seg_offset + seg_len); |
398 | 40.8k | } |
399 | 313k | 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 | 313k | memcpy(buf + seg_offset, list_data + list_offset, list_len); |
405 | | //PrintRawDataFp(stdout, buf, p->payload_len); |
406 | 313k | } |
407 | 650k | return (check_overlap_different_data && data_is_different); |
408 | 651k | } |
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 | 281k | { |
419 | 281k | int retval = 0; |
420 | | |
421 | 281k | SCLogDebug("check tree backwards: insert data for segment %p seq %u len %u re %u", |
422 | 281k | seg, seg->seq, TCP_SEG_LEN(seg), SEG_SEQ_RIGHT_EDGE(seg)); |
423 | | |
424 | | /* check backwards */ |
425 | 281k | TcpSegment *tree_seg = NULL, *s = seg; |
426 | 592k | RB_FOREACH_REVERSE_FROM(tree_seg, TCPSEG, s) { |
427 | 592k | if (tree_seg == seg) |
428 | 281k | continue; |
429 | | |
430 | 310k | int overlap = 0; |
431 | 310k | if (SEQ_LEQ(SEG_SEQ_RIGHT_EDGE(tree_seg), stream->base_seq)) { |
432 | | // segment entirely before base_seq |
433 | 0 | ; |
434 | 310k | } else if (SEQ_LEQ(tree_seg->seq + tree_seg->payload_len, seg->seq)) { |
435 | 247k | SCLogDebug("list segment too far to the left, no more overlap will be found"); |
436 | 247k | break; |
437 | 247k | } else if (SEQ_GT(SEG_SEQ_RIGHT_EDGE(tree_seg), seg->seq)) { |
438 | 63.3k | overlap = 1; |
439 | 63.3k | } |
440 | | |
441 | 63.3k | SCLogDebug("(back) tree seg %u len %u re %u overlap? %s", |
442 | 63.3k | tree_seg->seq, TCP_SEG_LEN(tree_seg), |
443 | 63.3k | SEG_SEQ_RIGHT_EDGE(tree_seg), overlap ? "yes" : "no"); |
444 | | |
445 | 63.3k | if (overlap) { |
446 | 63.3k | retval |= DoHandleDataOverlap(stream, tree_seg, seg, buf, p); |
447 | 63.3k | } |
448 | 63.3k | } |
449 | 281k | return retval; |
450 | 281k | } |
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 | 252k | { |
464 | 252k | int retval = 0; |
465 | | |
466 | 252k | uint32_t seg_re = SEG_SEQ_RIGHT_EDGE(seg); |
467 | | |
468 | 252k | SCLogDebug("check list forward: insert data for segment %p seq %u len %u re %u", |
469 | 252k | seg, seg->seq, TCP_SEG_LEN(seg), seg_re); |
470 | | |
471 | 252k | TcpSegment *tree_seg = NULL, *s = seg; |
472 | 537k | RB_FOREACH_FROM(tree_seg, TCPSEG, s) { |
473 | 537k | if (tree_seg == seg) |
474 | 252k | continue; |
475 | | |
476 | 285k | int overlap = 0; |
477 | 285k | if (SEQ_GT(seg_re, tree_seg->seq)) |
478 | 56.0k | overlap = 1; |
479 | 229k | else if (SEQ_LEQ(seg_re, tree_seg->seq)) { |
480 | 229k | SCLogDebug("tree segment %u too far ahead, " |
481 | 229k | "no more overlaps can happen", tree_seg->seq); |
482 | 229k | break; |
483 | 229k | } |
484 | | |
485 | 56.0k | SCLogDebug("(fwd) in-tree seg %u len %u re %u overlap? %s", |
486 | 56.0k | tree_seg->seq, TCP_SEG_LEN(tree_seg), |
487 | 56.0k | SEG_SEQ_RIGHT_EDGE(tree_seg), overlap ? "yes" : "no"); |
488 | | |
489 | 56.0k | if (overlap) { |
490 | 56.0k | retval |= DoHandleDataOverlap(stream, tree_seg, seg, buf, p); |
491 | 56.0k | } |
492 | 56.0k | } |
493 | 252k | return retval; |
494 | 252k | } |
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 | 549k | { |
503 | 549k | int result = 0; |
504 | 549k | TcpSegment *handle = seg; |
505 | | |
506 | 549k | SCLogDebug("insert data for segment %p seq %u len %u re %u", |
507 | 549k | 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 | 549k | uint8_t buf[p->payload_len]; |
513 | 549k | 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 | 549k | if (tree_seg) { |
517 | 531k | DoHandleDataOverlap(stream, tree_seg, seg, buf, p); |
518 | 531k | handle = tree_seg; |
519 | 531k | } |
520 | | |
521 | 549k | const bool is_head = !(TCPSEG_RB_PREV(handle)); |
522 | 549k | const bool is_tail = !(TCPSEG_RB_NEXT(handle)); |
523 | | |
524 | | /* new list head */ |
525 | 549k | if (is_head && !is_tail) { |
526 | 65.3k | result = DoHandleDataCheckForward(stream, handle, buf, p); |
527 | | |
528 | | /* new list tail */ |
529 | 483k | } else if (!is_head && is_tail) { |
530 | 94.9k | result = DoHandleDataCheckBackwards(stream, handle, buf, p); |
531 | | |
532 | | /* middle of the list */ |
533 | 388k | } else if (!is_head && !is_tail) { |
534 | 187k | result = DoHandleDataCheckBackwards(stream, handle, buf, p); |
535 | 187k | result |= DoHandleDataCheckForward(stream, handle, buf, p); |
536 | 187k | } |
537 | | |
538 | | /* we had an overlap with different data */ |
539 | 549k | if (result) { |
540 | 28.7k | StreamTcpSetEvent(p, STREAM_REASSEMBLY_OVERLAP_DIFFERENT_DATA); |
541 | 28.7k | StatsIncr(tv, ra_ctx->counter_tcp_reass_overlap_diff_data); |
542 | 28.7k | } |
543 | | |
544 | | /* insert the temp buffer now that we've (possibly) updated |
545 | | * it to account for the overlap policies */ |
546 | 549k | int res = InsertSegmentDataCustom(stream, handle, buf, p->payload_len); |
547 | 549k | 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 | 549k | return 0; |
562 | 549k | } |
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 = rp->ts; |
573 | 0 | seg->pcap_hdr_storage->pktlen = GET_PKT_LEN(rp) - pp->payload_len; |
574 | | /* |
575 | | * pkt_hdr members are initially allocated 64 bytes of memory. Thus, |
576 | | * need to check that this is sufficient and allocate more memory if |
577 | | * not. |
578 | | */ |
579 | 0 | if (seg->pcap_hdr_storage->pktlen > seg->pcap_hdr_storage->alloclen) { |
580 | 0 | uint8_t *tmp_pkt_hdr = StreamTcpReassembleRealloc(seg->pcap_hdr_storage->pkt_hdr, |
581 | 0 | seg->pcap_hdr_storage->alloclen, seg->pcap_hdr_storage->pktlen); |
582 | 0 | if (tmp_pkt_hdr == NULL) { |
583 | 0 | SCLogDebug("Failed to realloc"); |
584 | 0 | seg->pcap_hdr_storage->ts = SCTIME_INITIALIZER; |
585 | 0 | seg->pcap_hdr_storage->pktlen = 0; |
586 | 0 | return; |
587 | 0 | } else { |
588 | 0 | seg->pcap_hdr_storage->pkt_hdr = tmp_pkt_hdr; |
589 | 0 | seg->pcap_hdr_storage->alloclen = GET_PKT_LEN(rp) - pp->payload_len; |
590 | 0 | } |
591 | 0 | } |
592 | 0 | memcpy(seg->pcap_hdr_storage->pkt_hdr, GET_PKT_DATA(rp), |
593 | 0 | (size_t)GET_PKT_LEN(rp) - pp->payload_len); |
594 | 0 | } else { |
595 | 0 | seg->pcap_hdr_storage->ts = SCTIME_INITIALIZER; |
596 | 0 | seg->pcap_hdr_storage->pktlen = 0; |
597 | 0 | } |
598 | 0 | } |
599 | | |
600 | | /** |
601 | | * \brief Adds the following information to the TcpSegment from the current |
602 | | * packet being processed: time values, packet length, and the |
603 | | * header data of the packet. This information is added to the TcpSegment so |
604 | | * that it can be used in pcap capturing (log-pcap-stream) to dump the tcp |
605 | | * session at the beginning of the pcap capture. |
606 | | * \param seg TcpSegment where information is being stored. |
607 | | * \param p Packet being processed. |
608 | | * \param tv Thread-specific variables. |
609 | | * \param ra_ctx TcpReassembly thread-specific variables |
610 | | */ |
611 | | static void StreamTcpSegmentAddPacketData( |
612 | | TcpSegment *seg, Packet *p, ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx) |
613 | 0 | { |
614 | 0 | if (seg->pcap_hdr_storage == NULL || seg->pcap_hdr_storage->pkt_hdr == NULL) { |
615 | 0 | return; |
616 | 0 | } |
617 | | |
618 | 0 | if (PacketIsTunnelChild(p)) { |
619 | 0 | Packet *rp = p->root; |
620 | 0 | StreamTcpSegmentAddPacketDataDo(seg, rp, p); |
621 | 0 | } else { |
622 | 0 | StreamTcpSegmentAddPacketDataDo(seg, p, p); |
623 | 0 | } |
624 | 0 | } |
625 | | |
626 | | /** |
627 | | * \return 0 ok |
628 | | * \return -1 segment not inserted due to memcap issue |
629 | | * |
630 | | * \param seg segment, this function takes total ownership |
631 | | * |
632 | | * In case of error, this function returns the segment to the pool |
633 | | */ |
634 | | int StreamTcpReassembleInsertSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, |
635 | | TcpStream *stream, TcpSegment *seg, Packet *p, uint8_t *pkt_data, uint16_t pkt_datalen) |
636 | 8.24M | { |
637 | 8.24M | SCEnter(); |
638 | | |
639 | 8.24M | TcpSegment *dup_seg = NULL; |
640 | | |
641 | | /* insert segment into list. Note: doesn't handle the data */ |
642 | 8.24M | int r = DoInsertSegment (stream, seg, &dup_seg, p); |
643 | | |
644 | 8.24M | if (IsTcpSessionDumpingEnabled()) { |
645 | 0 | StreamTcpSegmentAddPacketData(seg, p, tv, ra_ctx); |
646 | 0 | } |
647 | | |
648 | 8.24M | if (likely(r == 0)) { |
649 | | /* no overlap, straight data insert */ |
650 | 7.69M | int res = InsertSegmentDataCustom(stream, seg, pkt_data, pkt_datalen); |
651 | 7.69M | if (res != SC_OK) { |
652 | 0 | StatsIncr(tv, ra_ctx->counter_tcp_reass_data_normal_fail); |
653 | 0 | StreamTcpRemoveSegmentFromStream(stream, seg); |
654 | 0 | StreamTcpSegmentReturntoPool(seg); |
655 | 0 | if (res == SC_ENOMEM) { |
656 | 0 | StatsIncr(tv, ra_ctx->counter_tcp_segment_memcap); |
657 | 0 | SCReturnInt(-SC_ENOMEM); |
658 | 0 | } |
659 | 0 | SCReturnInt(-1); |
660 | 0 | } |
661 | | |
662 | 7.69M | } else if (r == 1 || r == 2) { |
663 | 549k | SCLogDebug("overlap (%s%s)", r == 1 ? "normal" : "", r == 2 ? "duplicate" : ""); |
664 | | |
665 | 549k | if (r == 2) { |
666 | 531k | SCLogDebug("dup_seg %p", dup_seg); |
667 | 531k | } |
668 | | |
669 | | /* XXX should we exclude 'retransmissions' here? */ |
670 | 549k | StatsIncr(tv, ra_ctx->counter_tcp_reass_overlap); |
671 | | |
672 | | /* now let's consider the data in the overlap case */ |
673 | 549k | int res = DoHandleData(tv, ra_ctx, stream, seg, dup_seg, p); |
674 | 549k | if (res < 0) { |
675 | 0 | StatsIncr(tv, ra_ctx->counter_tcp_reass_data_overlap_fail); |
676 | |
|
677 | 0 | if (r == 1) // r == 2 mean seg wasn't added to stream |
678 | 0 | StreamTcpRemoveSegmentFromStream(stream, seg); |
679 | |
|
680 | 0 | StreamTcpSegmentReturntoPool(seg); |
681 | 0 | SCReturnInt(-1); |
682 | 0 | } |
683 | 549k | if (r == 2) { |
684 | 531k | SCLogDebug("duplicate segment %u/%u, discard it", |
685 | 531k | seg->seq, seg->payload_len); |
686 | | |
687 | 531k | StreamTcpSegmentReturntoPool(seg); |
688 | | #ifdef DEBUG |
689 | | if (SCLogDebugEnabled()) { |
690 | | TcpSegment *s = NULL, *safe = NULL; |
691 | | RB_FOREACH_SAFE(s, TCPSEG, &stream->seg_tree, safe) |
692 | | { |
693 | | SCLogDebug("tree: seg %p, SEQ %"PRIu32", LEN %"PRIu16", SUM %"PRIu32"%s%s%s", |
694 | | s, s->seq, TCP_SEG_LEN(s), |
695 | | (uint32_t)(s->seq + TCP_SEG_LEN(s)), |
696 | | s->seq == seg->seq ? " DUPLICATE" : "", |
697 | | TCPSEG_RB_PREV(s) == NULL ? " HEAD" : "", |
698 | | TCPSEG_RB_NEXT(s) == NULL ? " TAIL" : ""); |
699 | | } |
700 | | } |
701 | | #endif |
702 | 531k | } |
703 | 549k | } else { |
704 | | // EINVAL |
705 | 0 | StreamTcpSegmentReturntoPool(seg); |
706 | 0 | } |
707 | | |
708 | 8.24M | SCReturnInt(0); |
709 | 8.24M | } |
710 | | |
711 | | |
712 | | /* |
713 | | * Pruning & removal |
714 | | */ |
715 | | |
716 | | |
717 | | static inline bool SegmentInUse(const TcpStream *stream, const TcpSegment *seg) |
718 | 21.0M | { |
719 | | /* if proto detect isn't done, we're not returning */ |
720 | 21.0M | if (!(stream->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) { |
721 | 21.0M | if (!(StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream))) { |
722 | 1.73M | SCReturnInt(true); |
723 | 1.73M | } |
724 | 21.0M | } |
725 | | |
726 | 21.0M | SCReturnInt(false); |
727 | 21.0M | } |
728 | | |
729 | | |
730 | | /** \internal |
731 | | * \brief check if we can remove a segment from our segment list |
732 | | * |
733 | | * \retval true |
734 | | * \retval false |
735 | | */ |
736 | | static inline bool StreamTcpReturnSegmentCheck(const TcpStream *stream, const TcpSegment *seg) |
737 | 13.9M | { |
738 | 13.9M | if (SegmentInUse(stream, seg)) { |
739 | 1.71M | SCReturnInt(false); |
740 | 1.71M | } |
741 | | |
742 | 12.2M | if (!(StreamingBufferSegmentIsBeforeWindow(&stream->sb, &seg->sbseg))) { |
743 | 8.84M | SCReturnInt(false); |
744 | 8.84M | } |
745 | | |
746 | 12.2M | SCReturnInt(true); |
747 | 12.2M | } |
748 | | |
749 | | static inline uint64_t GetLeftEdgeForApp(Flow *f, TcpSession *ssn, TcpStream *stream) |
750 | 11.8M | { |
751 | 11.8M | const FramesContainer *frames_container = AppLayerFramesGetContainer(f); |
752 | 11.8M | if (frames_container == NULL) |
753 | 5.31M | return STREAM_APP_PROGRESS(stream); |
754 | | |
755 | 6.57M | const Frames *frames = |
756 | 6.57M | stream == &ssn->client ? &frames_container->toserver : &frames_container->toclient; |
757 | | // const uint64_t x = FramesLeftEdge(stream, frames); |
758 | | // BUG_ON(x != (frames->left_edge_rel + STREAM_BASE_OFFSET(stream))); |
759 | | // return x; |
760 | 6.57M | const uint64_t o = (uint64_t)frames->left_edge_rel + STREAM_BASE_OFFSET(stream); |
761 | 6.57M | SCLogDebug( |
762 | 6.57M | "%s: frames left edge: %" PRIu64, &ssn->client == stream ? "toserver" : "toclient", o); |
763 | 6.57M | return o; |
764 | 11.8M | } |
765 | | |
766 | | static inline uint64_t GetLeftEdge(Flow *f, TcpSession *ssn, TcpStream *stream) |
767 | 6.34M | { |
768 | 6.34M | uint64_t left_edge = 0; |
769 | 6.34M | const bool use_app = !(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED); |
770 | 6.34M | const bool use_raw = !(stream->flags & STREAMTCP_STREAM_FLAG_DISABLE_RAW); |
771 | 6.34M | const bool use_log = stream_config.streaming_log_api; |
772 | 6.34M | SCLogDebug("use_app %d use_raw %d use_log %d tcp win %u", use_app, use_raw, use_log, |
773 | 6.34M | stream->window); |
774 | | |
775 | 6.34M | if (use_raw) { |
776 | 1.09M | uint64_t raw_progress = STREAM_RAW_PROGRESS(stream); |
777 | | |
778 | 1.09M | if (StreamTcpInlineMode()) { |
779 | 0 | uint32_t chunk_size = (stream == &ssn->client) ? |
780 | 0 | stream_config.reassembly_toserver_chunk_size : |
781 | 0 | stream_config.reassembly_toclient_chunk_size; |
782 | 0 | if (raw_progress < (uint64_t)chunk_size) { |
783 | 0 | raw_progress = 0; |
784 | 0 | } else { |
785 | 0 | raw_progress -= (uint64_t)chunk_size; |
786 | 0 | } |
787 | 0 | } |
788 | | |
789 | | /* apply min inspect depth: if it is set we need to keep data |
790 | | * before the raw progress. */ |
791 | 1.09M | if (use_app && stream->min_inspect_depth && ssn->state < TCP_CLOSED) { |
792 | 384k | if (raw_progress < stream->min_inspect_depth) |
793 | 245k | raw_progress = 0; |
794 | 138k | else |
795 | 138k | raw_progress -= stream->min_inspect_depth; |
796 | | |
797 | 384k | SCLogDebug("stream->min_inspect_depth %u, raw_progress %"PRIu64, |
798 | 384k | stream->min_inspect_depth, raw_progress); |
799 | 384k | } |
800 | | |
801 | 1.09M | if (use_app) { |
802 | 977k | const uint64_t app_le = GetLeftEdgeForApp(f, ssn, stream); |
803 | 977k | left_edge = MIN(app_le, raw_progress); |
804 | 977k | SCLogDebug("left_edge %" PRIu64 ", using both app:%" PRIu64 ", raw:%" PRIu64, left_edge, |
805 | 977k | app_le, raw_progress); |
806 | 977k | } else { |
807 | 112k | left_edge = raw_progress; |
808 | 112k | SCLogDebug("left_edge %"PRIu64", using only raw:%"PRIu64, |
809 | 112k | left_edge, raw_progress); |
810 | 112k | } |
811 | 5.25M | } else if (use_app) { |
812 | 5.25M | const uint64_t app_le = GetLeftEdgeForApp(f, ssn, stream); |
813 | 5.25M | left_edge = app_le; |
814 | 5.25M | SCLogDebug("left_edge %" PRIu64 ", using only app:%" PRIu64, left_edge, app_le); |
815 | 5.25M | } else { |
816 | 0 | left_edge = StreamingBufferGetConsecutiveDataRightEdge(&stream->sb); |
817 | 0 | SCLogDebug("no app & raw: left_edge %"PRIu64" (full stream)", left_edge); |
818 | 0 | } |
819 | | |
820 | 6.34M | if (use_log) { |
821 | 0 | if (use_app || use_raw) { |
822 | 0 | left_edge = MIN(left_edge, STREAM_LOG_PROGRESS(stream)); |
823 | 0 | } else { |
824 | 0 | left_edge = STREAM_LOG_PROGRESS(stream); |
825 | 0 | } |
826 | 0 | } |
827 | | |
828 | 6.34M | uint64_t last_ack_abs = STREAM_BASE_OFFSET(stream); |
829 | 6.34M | if (STREAM_LASTACK_GT_BASESEQ(stream)) { |
830 | 5.22M | last_ack_abs += (stream->last_ack - stream->base_seq); |
831 | 5.22M | } |
832 | | /* in IDS mode we shouldn't see the base_seq pass last_ack */ |
833 | 6.34M | DEBUG_VALIDATE_BUG_ON(last_ack_abs < left_edge && !StreamTcpInlineMode() && !f->ffr && |
834 | 6.34M | ssn->state < TCP_CLOSED); |
835 | 6.34M | left_edge = MIN(left_edge, last_ack_abs); |
836 | | |
837 | | /* if we're told to look for overlaps with different data we should |
838 | | * consider data that is ack'd as well. Injected packets may have |
839 | | * been ack'd or injected packet may be too late. */ |
840 | 6.34M | if (!StreamTcpInlineMode() && check_overlap_different_data) { |
841 | 5.97M | const uint32_t window = stream->window ? stream->window : 4096; |
842 | 5.97M | if (window < left_edge) |
843 | 1.01M | left_edge -= window; |
844 | 4.96M | else |
845 | 4.96M | left_edge = 0; |
846 | | |
847 | 5.97M | SCLogDebug("stream:%p left_edge %"PRIu64, stream, left_edge); |
848 | 5.97M | } |
849 | | |
850 | 6.34M | if (left_edge > 0) { |
851 | | /* we know left edge based on the progress values now, |
852 | | * lets adjust it to make sure in-use segments still have |
853 | | * data */ |
854 | 1.14M | TcpSegment *seg = NULL; |
855 | 3.25M | RB_FOREACH(seg, TCPSEG, &stream->seg_tree) { |
856 | 3.25M | if (TCP_SEG_OFFSET(seg) > left_edge) { |
857 | 1.06M | SCLogDebug("seg beyond left_edge, we're done"); |
858 | 1.06M | break; |
859 | 1.06M | } |
860 | | |
861 | 2.18M | if (SegmentInUse(stream, seg)) { |
862 | 15.9k | left_edge = TCP_SEG_OFFSET(seg); |
863 | 15.9k | SCLogDebug("in-use seg before left_edge, adjust to %"PRIu64" and bail", left_edge); |
864 | 15.9k | break; |
865 | 15.9k | } |
866 | 2.18M | } |
867 | 1.14M | } |
868 | | |
869 | 6.34M | return left_edge; |
870 | 6.34M | } |
871 | | |
872 | | static void StreamTcpRemoveSegmentFromStream(TcpStream *stream, TcpSegment *seg) |
873 | 3.35M | { |
874 | 3.35M | RB_REMOVE(TCPSEG, &stream->seg_tree, seg); |
875 | 3.35M | } |
876 | | |
877 | | /** \brief Remove idle TcpSegments from TcpSession |
878 | | * |
879 | | * Checks app progress and raw progress and progresses them |
880 | | * if needed, slides the streaming buffer, then gets rid of |
881 | | * excess segments. |
882 | | * |
883 | | * \param f flow |
884 | | * \param flags direction flags |
885 | | */ |
886 | | void StreamTcpPruneSession(Flow *f, uint8_t flags) |
887 | 6.72M | { |
888 | 6.72M | SCEnter(); |
889 | | |
890 | 6.72M | if (f == NULL || f->protoctx == NULL) { |
891 | 0 | SCReturn; |
892 | 0 | } |
893 | | |
894 | 6.72M | TcpSession *ssn = f->protoctx; |
895 | 6.72M | TcpStream *stream = NULL; |
896 | | |
897 | 6.72M | if (flags & STREAM_TOSERVER) { |
898 | 3.55M | stream = &ssn->client; |
899 | 3.55M | } else if (flags & STREAM_TOCLIENT) { |
900 | 3.16M | stream = &ssn->server; |
901 | 3.16M | } else { |
902 | 0 | SCReturn; |
903 | 0 | } |
904 | | |
905 | 6.72M | if (stream->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) { |
906 | 348k | return; |
907 | 348k | } |
908 | | |
909 | 6.37M | if (stream->flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED) { |
910 | 0 | stream->flags |= STREAMTCP_STREAM_FLAG_NOREASSEMBLY; |
911 | 0 | SCLogDebug("ssn %p / stream %p: reassembly depth reached, " |
912 | 0 | "STREAMTCP_STREAM_FLAG_NOREASSEMBLY set", ssn, stream); |
913 | 0 | StreamTcpReturnStreamSegments(stream); |
914 | 0 | StreamingBufferClear(&stream->sb, &stream_config.sbcnf); |
915 | 0 | return; |
916 | |
|
917 | 6.37M | } else if ((ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) && |
918 | 6.37M | (stream->flags & STREAMTCP_STREAM_FLAG_DISABLE_RAW)) { |
919 | 35.6k | SCLogDebug("ssn %p / stream %p: both app and raw are done, " |
920 | 35.6k | "STREAMTCP_STREAM_FLAG_NOREASSEMBLY set", ssn, stream); |
921 | 35.6k | stream->flags |= STREAMTCP_STREAM_FLAG_NOREASSEMBLY; |
922 | 35.6k | StreamTcpReturnStreamSegments(stream); |
923 | 35.6k | StreamingBufferClear(&stream->sb, &stream_config.sbcnf); |
924 | 35.6k | return; |
925 | 35.6k | } |
926 | | |
927 | 6.34M | const uint64_t left_edge = GetLeftEdge(f, ssn, stream); |
928 | 6.34M | SCLogDebug("buffer left_edge %" PRIu64, left_edge); |
929 | 6.34M | if (left_edge && left_edge > STREAM_BASE_OFFSET(stream)) { |
930 | 380k | DEBUG_VALIDATE_BUG_ON(left_edge - STREAM_BASE_OFFSET(stream) > UINT32_MAX); |
931 | 380k | uint32_t slide = (uint32_t)(left_edge - STREAM_BASE_OFFSET(stream)); |
932 | 380k | SCLogDebug("buffer sliding %u to offset %"PRIu64, slide, left_edge); |
933 | | |
934 | 380k | if (!(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED)) { |
935 | 379k | AppLayerFramesSlide(f, slide, flags & (STREAM_TOSERVER | STREAM_TOCLIENT)); |
936 | 379k | } |
937 | 380k | StreamingBufferSlideToOffset(&stream->sb, &stream_config.sbcnf, left_edge); |
938 | 380k | stream->base_seq += slide; |
939 | | |
940 | 380k | if (slide <= stream->app_progress_rel) { |
941 | 379k | stream->app_progress_rel -= slide; |
942 | 379k | } else { |
943 | 555 | stream->app_progress_rel = 0; |
944 | 555 | } |
945 | 380k | if (slide <= stream->raw_progress_rel) { |
946 | 17.5k | stream->raw_progress_rel -= slide; |
947 | 362k | } else { |
948 | 362k | stream->raw_progress_rel = 0; |
949 | 362k | } |
950 | 380k | if (slide <= stream->log_progress_rel) { |
951 | 0 | stream->log_progress_rel -= slide; |
952 | 380k | } else { |
953 | 380k | stream->log_progress_rel = 0; |
954 | 380k | } |
955 | | |
956 | 380k | SCLogDebug("stream base_seq %u at stream offset %"PRIu64, |
957 | 380k | stream->base_seq, STREAM_BASE_OFFSET(stream)); |
958 | 380k | } |
959 | | |
960 | | /* loop through the segments and remove all not in use */ |
961 | 6.34M | TcpSegment *seg = NULL, *safe = NULL; |
962 | 6.34M | RB_FOREACH_SAFE(seg, TCPSEG, &stream->seg_tree, safe) |
963 | 6.72M | { |
964 | 6.72M | SCLogDebug("seg %p, SEQ %"PRIu32", LEN %"PRIu16", SUM %"PRIu32, |
965 | 6.72M | seg, seg->seq, TCP_SEG_LEN(seg), |
966 | 6.72M | (uint32_t)(seg->seq + TCP_SEG_LEN(seg))); |
967 | | |
968 | 6.72M | if (StreamTcpReturnSegmentCheck(stream, seg) == 0) { |
969 | 5.63M | SCLogDebug("not removing segment"); |
970 | 5.63M | break; |
971 | 5.63M | } |
972 | | |
973 | 1.08M | StreamTcpRemoveSegmentFromStream(stream, seg); |
974 | 1.08M | StreamTcpSegmentReturntoPool(seg); |
975 | 1.08M | SCLogDebug("removed segment"); |
976 | 1.08M | continue; |
977 | 6.72M | } |
978 | | |
979 | 6.34M | SCReturn; |
980 | 6.34M | } |
981 | | |
982 | | |
983 | | /* |
984 | | * unittests |
985 | | */ |
986 | | |
987 | | #ifdef UNITTESTS |
988 | | #include "tests/stream-tcp-list.c" |
989 | | #endif |