suricata
stream-tcp-list.c
Go to the documentation of this file.
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"
29#include "stream-tcp-inline.h"
30#include "stream-tcp-list.h"
32#include "util-print.h"
33#include "util-validate.h"
34#include "app-layer-frames.h"
35
36static void StreamTcpRemoveSegmentFromStream(TcpStream *stream, TcpSegment *seg);
37
38static int check_overlap_different_data = 0;
39
41{
42 check_overlap_different_data = 1;
43}
44
45/*
46 * Inserts and overlap handling
47 */
48
50
51int TcpSegmentCompare(struct TcpSegment *a, struct TcpSegment *b)
52{
53 if (SEQ_GT(a->seq, b->seq))
54 return 1;
55 else if (SEQ_LT(a->seq, b->seq))
56 return -1;
57 else {
58 if (a->payload_len == b->payload_len)
59 return 0;
60 else if (a->payload_len > b->payload_len)
61 return 1;
62 else
63 return -1;
64 }
65}
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 */
76static inline int InsertSegmentDataCustom(TcpStream *stream, TcpSegment *seg, uint8_t *data, uint16_t data_len)
77{
78 uint64_t stream_offset;
79 uint32_t data_offset;
80
81 if (likely(SEQ_GEQ(seg->seq, stream->base_seq))) {
82 stream_offset = STREAM_BASE_OFFSET(stream) + (seg->seq - stream->base_seq);
83 data_offset = 0;
84 } else {
85 /* segment is partly before base_seq */
86 data_offset = stream->base_seq - seg->seq;
88 }
89
90 SCLogDebug("stream %p buffer %p, stream_offset %"PRIu64", "
91 "data_offset %"PRIu16", SEQ %u BASE %u, data_len %u",
92 stream, &stream->sb, stream_offset,
93 data_offset, seg->seq, stream->base_seq, data_len);
94 DEBUG_VALIDATE_BUG_ON(data_offset > data_len);
95 if (data_len <= data_offset) {
97 }
98
99 int ret = StreamingBufferInsertAt(&stream->sb, &stream_config.sbcnf, &seg->sbseg,
100 data + data_offset, data_len - data_offset, stream_offset);
101 if (ret != SC_OK) {
102 SCReturnInt(ret);
103 }
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
117}
118
119/** \internal
120 * \brief check if this segments overlaps with an in-tree seg.
121 * \retval true
122 * \retval false
123 */
124static inline bool CheckOverlap(struct TCPSEG *tree, TcpSegment *seg)
125{
126 const uint32_t re = SEG_SEQ_RIGHT_EDGE(seg);
127 SCLogDebug("start. SEQ %u payload_len %u. Right edge: %u. Seg %p",
128 seg->seq, seg->payload_len, re, seg);
129
130 /* check forward */
131 TcpSegment *next = TCPSEG_RB_NEXT(seg);
132 if (next) {
133 // next has same seq, so data must overlap
134 if (SEQ_EQ(next->seq, seg->seq))
135 return true;
136 // our right edge is beyond next seq, overlap
137 if (SEQ_GT(re, next->seq))
138 return true;
139 }
140 /* check backwards */
141 TcpSegment *prev = TCPSEG_RB_PREV(seg);
142 if (prev) {
143 // prev has same seq, so data must overlap
144 if (SEQ_EQ(prev->seq, seg->seq))
145 return true;
146 // prev's right edge is beyond our seq, overlap
147 const uint32_t prev_re = SEG_SEQ_RIGHT_EDGE(prev);
148 if (SEQ_GT(prev_re, seg->seq))
149 return true;
150 }
151
152 SCLogDebug("no overlap");
153 return false;
154}
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 */
165static int DoInsertSegment (TcpStream *stream, TcpSegment *seg, TcpSegment **dup_seg, Packet *p)
166{
167 /* in lossy traffic, we can get here with the wrong sequence numbers */
168 if (SEQ_LEQ(SEG_SEQ_RIGHT_EDGE(seg), stream->base_seq)) {
169 return -EINVAL;
170 }
171
172 /* fast track */
173 if (RB_EMPTY(&stream->seg_tree)) {
174 SCLogDebug("empty tree, inserting seg %p seq %" PRIu32 ", "
175 "len %" PRIu32 "", seg, seg->seq, TCP_SEG_LEN(seg));
176 TCPSEG_RB_INSERT(&stream->seg_tree, seg);
177 stream->segs_right_edge = SEG_SEQ_RIGHT_EDGE(seg);
178 return 0;
179 }
180
181 /* insert and then check if there was any overlap with other segments */
182 TcpSegment *res = TCPSEG_RB_INSERT(&stream->seg_tree, seg);
183 if (res) {
184 SCLogDebug("seg has a duplicate in the tree seq %u/%u",
185 res->seq, res->payload_len);
186 /* exact duplicate SEQ + payload_len */
187 *dup_seg = res;
188 return 2; // duplicate has overlap by definition.
189 } else {
190 if (SEQ_GT(SEG_SEQ_RIGHT_EDGE(seg), stream->segs_right_edge))
191 stream->segs_right_edge = SEG_SEQ_RIGHT_EDGE(seg);
192
193 /* insert succeeded, now check if we overlap with someone */
194 if (CheckOverlap(&stream->seg_tree, seg)) {
195 SCLogDebug("seg %u has overlap in the tree", seg->seq);
196 return 1;
197 }
198 }
199 SCLogDebug("seg %u: no overlap", seg->seq);
200 return 0;
201}
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 */
221static int DoHandleDataOverlap(TcpStream *stream, const TcpSegment *list,
222 const TcpSegment *seg, uint8_t *buf, Packet *p)
223{
224 SCLogDebug("handle overlap for segment %p seq %u len %u re %u, "
225 "list segment %p seq %u len %u re %u", seg, seg->seq,
227 list, list->seq, TCP_SEG_LEN(list), SEG_SEQ_RIGHT_EDGE(list));
228
229 int data_is_different = 0;
230 int use_new_data = 0;
231
232 if (StreamTcpInlineMode()) {
233 SCLogDebug("inline mode");
234 if (StreamTcpInlineSegmentCompare(stream, p, list) != 0) {
235 SCLogDebug("already accepted data not the same as packet data, rewrite packet");
237 data_is_different = 1;
238
239 /* in inline mode we check for different data unconditionally,
240 * but setting events still depends on config */
241 if (check_overlap_different_data) {
243 }
244 }
245
246 /* IDS mode */
247 } else {
248 if (check_overlap_different_data) {
249 if (StreamTcpInlineSegmentCompare(stream, p, list) != 0) {
250 SCLogDebug("data is different from what is in the list");
251 data_is_different = 1;
252 }
253 } else {
254 /* if we're not checking, assume it's different */
255 data_is_different = 1;
256 }
257
258 /* apply overlap policies */
259
260 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 return (check_overlap_different_data && data_is_different);
264 }
265
266 /* start at the same seq */
267 if (SEQ_EQ(seg->seq, list->seq)) {
268 SCLogDebug("seg starts at list segment");
269
271 SCLogDebug("seg ends before list end, end overlapped by list");
272 } else {
274 SCLogDebug("seg ends beyond list end, list overlapped and more");
275 switch (stream->os_policy) {
276 case OS_POLICY_LINUX:
277 if (data_is_different) {
278 use_new_data = 1;
279 }
280 break;
281 }
282 } else {
283 SCLogDebug("full overlap");
284 }
285
286 switch (stream->os_policy) {
289 case OS_POLICY_HPUX11:
290 if (data_is_different) {
291 use_new_data = 1;
292 }
293 break;
294 }
295 }
296
297 /* new seg starts before list segment */
298 } else if (SEQ_LT(seg->seq, list->seq)) {
299 SCLogDebug("seg starts before list segment");
300
302 SCLogDebug("seg ends before list end, end overlapped by list");
303 } else {
305 SCLogDebug("seg starts before and fully overlaps list and beyond");
306 } else {
307 SCLogDebug("seg starts before and fully overlaps list");
308 }
309
310 switch (stream->os_policy) {
312 case OS_POLICY_HPUX11:
313 if (data_is_different) {
314 use_new_data = 1;
315 }
316 break;
317 }
318 }
319
320 switch (stream->os_policy) {
321 case OS_POLICY_BSD:
322 case OS_POLICY_HPUX10:
323 case OS_POLICY_IRIX:
327 case OS_POLICY_LINUX:
328 case OS_POLICY_MACOS:
329 if (data_is_different) {
330 use_new_data = 1;
331 }
332 break;
333 }
334
335 /* new seg starts after list segment */
336 } else { //if (SEQ_GT(seg->seq, list->seq)) {
337 SCLogDebug("seg starts after list segment");
338
340 SCLogDebug("seg after and is fully overlapped by list");
341 } else if (SEQ_GT(SEG_SEQ_RIGHT_EDGE(seg), SEG_SEQ_RIGHT_EDGE(list))) {
342 SCLogDebug("seg starts after list and ends after list");
343
344 switch (stream->os_policy) {
346 case OS_POLICY_HPUX11:
347 if (data_is_different) {
348 use_new_data = 1;
349 }
350 break;
351 }
352 } else {
353 SCLogDebug("seg starts after list and ends before list end");
354
355 }
356 }
357 }
358
359 SCLogDebug("data_is_different %s, use_new_data %s",
360 data_is_different ? "yes" : "no",
361 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 if (data_is_different && !use_new_data) {
366 /* we need to copy list into seg */
367 uint32_t list_offset = 0;
368 uint32_t seg_offset = 0;
369 uint32_t list_len;
370 uint16_t seg_len = p->payload_len;
371 uint32_t list_seq = list->seq;
372
373 const uint8_t *list_data;
374 StreamingBufferSegmentGetData(&stream->sb, &list->sbseg, &list_data, &list_len);
375 DEBUG_VALIDATE_BUG_ON(list_len > USHRT_MAX);
376 if (list_data == NULL || list_len == 0 || list_len > USHRT_MAX)
377 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 if (SEQ_GEQ(list->seq, stream->base_seq)) {
382 ;
383 } else {
384 list_seq = stream->base_seq;
385 list_len = SEG_SEQ_RIGHT_EDGE(list) - stream->base_seq;
386 }
387
388 if (SEQ_LT(seg->seq, list_seq)) {
389 seg_offset = list_seq - seg->seq;
390 seg_len -= seg_offset;
391 } else if (SEQ_GT(seg->seq, list_seq)) {
392 list_offset = seg->seq - list_seq;
393 list_len -= list_offset;
394 }
395
396 if (SEQ_LT(seg->seq + seg_offset + seg_len, list_seq + list_offset + list_len)) {
397 list_len -= (list_seq + list_offset + list_len) - (seg->seq + seg_offset + seg_len);
398 }
399 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 memcpy(buf + seg_offset, list_data + list_offset, list_len);
405 //PrintRawDataFp(stdout, buf, p->payload_len);
406 }
407 return (check_overlap_different_data && data_is_different);
408}
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 */
416static int DoHandleDataCheckBackwards(TcpStream *stream,
417 TcpSegment *seg, uint8_t *buf, Packet *p)
418{
419 int retval = 0;
420
421 SCLogDebug("check tree backwards: insert data for segment %p seq %u len %u re %u",
422 seg, seg->seq, TCP_SEG_LEN(seg), SEG_SEQ_RIGHT_EDGE(seg));
423
424 /* check backwards */
425 TcpSegment *tree_seg = NULL, *s = seg;
426 RB_FOREACH_REVERSE_FROM(tree_seg, TCPSEG, s) {
427 if (tree_seg == seg)
428 continue;
429
430 int overlap = 0;
431 if (SEQ_LEQ(SEG_SEQ_RIGHT_EDGE(tree_seg), stream->base_seq)) {
432 // segment entirely before base_seq
433 ;
434 } else if (SEQ_LEQ(tree_seg->seq + tree_seg->payload_len, seg->seq)) {
435 SCLogDebug("list segment too far to the left, no more overlap will be found");
436 break;
437 } else if (SEQ_GT(SEG_SEQ_RIGHT_EDGE(tree_seg), seg->seq)) {
438 overlap = 1;
439 }
440
441 SCLogDebug("(back) tree seg %u len %u re %u overlap? %s",
442 tree_seg->seq, TCP_SEG_LEN(tree_seg),
443 SEG_SEQ_RIGHT_EDGE(tree_seg), overlap ? "yes" : "no");
444
445 if (overlap) {
446 retval |= DoHandleDataOverlap(stream, tree_seg, seg, buf, p);
447 }
448 }
449 return retval;
450}
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 */
461static int DoHandleDataCheckForward(TcpStream *stream,
462 TcpSegment *seg, uint8_t *buf, Packet *p)
463{
464 int retval = 0;
465
466 uint32_t seg_re = SEG_SEQ_RIGHT_EDGE(seg);
467
468 SCLogDebug("check list forward: insert data for segment %p seq %u len %u re %u",
469 seg, seg->seq, TCP_SEG_LEN(seg), seg_re);
470
471 TcpSegment *tree_seg = NULL, *s = seg;
472 RB_FOREACH_FROM(tree_seg, TCPSEG, s) {
473 if (tree_seg == seg)
474 continue;
475
476 int overlap = 0;
477 if (SEQ_GT(seg_re, tree_seg->seq))
478 overlap = 1;
479 else if (SEQ_LEQ(seg_re, tree_seg->seq)) {
480 SCLogDebug("tree segment %u too far ahead, "
481 "no more overlaps can happen", tree_seg->seq);
482 break;
483 }
484
485 SCLogDebug("(fwd) in-tree seg %u len %u re %u overlap? %s",
486 tree_seg->seq, TCP_SEG_LEN(tree_seg),
487 SEG_SEQ_RIGHT_EDGE(tree_seg), overlap ? "yes" : "no");
488
489 if (overlap) {
490 retval |= DoHandleDataOverlap(stream, tree_seg, seg, buf, p);
491 }
492 }
493 return retval;
494}
495
496/**
497 * \param tree_seg in-tree duplicate of `seg`
498 * \retval res 0 ok, -1 insertion error due to memcap
499 */
500static int DoHandleData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
501 TcpStream *stream, TcpSegment *seg, TcpSegment *tree_seg, Packet *p)
502{
503 int result = 0;
504 TcpSegment *handle = seg;
505
506 SCLogDebug("insert data for segment %p seq %u len %u re %u",
507 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 uint8_t buf[p->payload_len];
513 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 if (tree_seg) {
517 DoHandleDataOverlap(stream, tree_seg, seg, buf, p);
518 handle = tree_seg;
519 }
520
521 const bool is_head = !(TCPSEG_RB_PREV(handle));
522 const bool is_tail = !(TCPSEG_RB_NEXT(handle));
523
524 /* new list head */
525 if (is_head && !is_tail) {
526 result = DoHandleDataCheckForward(stream, handle, buf, p);
527
528 /* new list tail */
529 } else if (!is_head && is_tail) {
530 result = DoHandleDataCheckBackwards(stream, handle, buf, p);
531
532 /* middle of the list */
533 } else if (!is_head && !is_tail) {
534 result = DoHandleDataCheckBackwards(stream, handle, buf, p);
535 result |= DoHandleDataCheckForward(stream, handle, buf, p);
536 }
537
538 /* we had an overlap with different data */
539 if (result) {
542 }
543
544 /* insert the temp buffer now that we've (possibly) updated
545 * it to account for the overlap policies */
546 int res = InsertSegmentDataCustom(stream, handle, buf, p->payload_len);
547 if (res != SC_OK) {
548 if (res == SC_ENOMEM) {
551 } else if (res == SC_ELIMIT) {
553 } else if (res == SC_EINVAL) {
555 } else {
557 }
558 return -1;
559 }
560
561 return 0;
562}
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 */
569static void StreamTcpSegmentAddPacketDataDo(TcpSegment *seg, const Packet *rp, const Packet *pp)
570{
571 if (GET_PKT_DATA(rp) != NULL && GET_PKT_LEN(rp) > pp->payload_len) {
572 seg->pcap_hdr_storage->ts = rp->ts;
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 */
580 uint8_t *tmp_pkt_hdr = StreamTcpReassembleRealloc(seg->pcap_hdr_storage->pkt_hdr,
582 if (tmp_pkt_hdr == NULL) {
583 SCLogDebug("Failed to realloc");
585 seg->pcap_hdr_storage->pktlen = 0;
586 return;
587 } else {
588 seg->pcap_hdr_storage->pkt_hdr = tmp_pkt_hdr;
590 }
591 }
592 memcpy(seg->pcap_hdr_storage->pkt_hdr, GET_PKT_DATA(rp),
593 (size_t)GET_PKT_LEN(rp) - pp->payload_len);
594 } else {
596 seg->pcap_hdr_storage->pktlen = 0;
597 }
598}
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 */
611static void StreamTcpSegmentAddPacketData(
613{
614 if (seg->pcap_hdr_storage == NULL || seg->pcap_hdr_storage->pkt_hdr == NULL) {
615 return;
616 }
617
618 if (PacketIsTunnelChild(p)) {
619 Packet *rp = p->root;
620 StreamTcpSegmentAddPacketDataDo(seg, rp, p);
621 } else {
622 StreamTcpSegmentAddPacketDataDo(seg, p, p);
623 }
624}
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 */
635 TcpStream *stream, TcpSegment *seg, Packet *p, uint8_t *pkt_data, uint16_t pkt_datalen)
636{
637 SCEnter();
638
639 TcpSegment *dup_seg = NULL;
640
641 /* insert segment into list. Note: doesn't handle the data */
642 int r = DoInsertSegment (stream, seg, &dup_seg, p);
643
645 StreamTcpSegmentAddPacketData(seg, p, tv, ra_ctx);
646 }
647
648 if (likely(r == 0)) {
649 /* no overlap, straight data insert */
650 int res = InsertSegmentDataCustom(stream, seg, pkt_data, pkt_datalen);
651 if (res != SC_OK) {
653 StreamTcpRemoveSegmentFromStream(stream, seg);
655 if (res == SC_ENOMEM) {
658 }
659 SCReturnInt(-1);
660 }
661
662 } else if (r == 1 || r == 2) {
663 SCLogDebug("overlap (%s%s)", r == 1 ? "normal" : "", r == 2 ? "duplicate" : "");
664
665 if (r == 2) {
666 SCLogDebug("dup_seg %p", dup_seg);
667 }
668
669 /* XXX should we exclude 'retransmissions' here? */
671
672 /* now let's consider the data in the overlap case */
673 int res = DoHandleData(tv, ra_ctx, stream, seg, dup_seg, p);
674 if (res < 0) {
676
677 if (r == 1) // r == 2 mean seg wasn't added to stream
678 StreamTcpRemoveSegmentFromStream(stream, seg);
679
681 SCReturnInt(-1);
682 }
683 if (r == 2) {
684 SCLogDebug("duplicate segment %u/%u, discard it",
685 seg->seq, seg->payload_len);
686
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 }
703 } else {
704 // EINVAL
706 }
707
708 SCReturnInt(0);
709}
710
711
712/*
713 * Pruning & removal
714 */
715
716
717static inline bool SegmentInUse(const TcpStream *stream, const TcpSegment *seg)
718{
719 /* if proto detect isn't done, we're not returning */
720 if (!(stream->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
722 SCReturnInt(true);
723 }
724 }
725
726 SCReturnInt(false);
727}
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 */
736static inline bool StreamTcpReturnSegmentCheck(const TcpStream *stream, const TcpSegment *seg)
737{
738 if (SegmentInUse(stream, seg)) {
739 SCReturnInt(false);
740 }
741
742 if (!(StreamingBufferSegmentIsBeforeWindow(&stream->sb, &seg->sbseg))) {
743 SCReturnInt(false);
744 }
745
746 SCReturnInt(true);
747}
748
749static inline uint64_t GetLeftEdgeForApp(Flow *f, TcpSession *ssn, TcpStream *stream)
750{
751 const FramesContainer *frames_container = AppLayerFramesGetContainer(f);
752 if (frames_container == NULL)
753 return STREAM_APP_PROGRESS(stream);
754
755 const Frames *frames =
756 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 const uint64_t o = (uint64_t)frames->left_edge_rel + STREAM_BASE_OFFSET(stream);
762 "%s: frames left edge: %" PRIu64, &ssn->client == stream ? "toserver" : "toclient", o);
763 return o;
764}
765
766static inline uint64_t GetLeftEdge(Flow *f, TcpSession *ssn, TcpStream *stream)
767{
768 uint64_t left_edge = 0;
769 const bool use_app = !(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED);
770 const bool use_raw = !(stream->flags & STREAMTCP_STREAM_FLAG_DISABLE_RAW);
771 const bool use_log = stream_config.streaming_log_api;
772 SCLogDebug("use_app %d use_raw %d use_log %d tcp win %u", use_app, use_raw, use_log,
773 stream->window);
774
775 if (use_raw) {
776 uint64_t raw_progress = STREAM_RAW_PROGRESS(stream);
777
778 if (StreamTcpInlineMode()) {
779 uint32_t chunk_size = (stream == &ssn->client) ?
781 stream_config.reassembly_toclient_chunk_size;
782 if (raw_progress < (uint64_t)chunk_size) {
783 raw_progress = 0;
784 } else {
785 raw_progress -= (uint64_t)chunk_size;
786 }
787 }
788
789 /* apply min inspect depth: if it is set we need to keep data
790 * before the raw progress. */
791 if (use_app && stream->min_inspect_depth && ssn->state < TCP_CLOSED) {
792 if (raw_progress < stream->min_inspect_depth)
793 raw_progress = 0;
794 else
795 raw_progress -= stream->min_inspect_depth;
796
797 SCLogDebug("stream->min_inspect_depth %u, raw_progress %"PRIu64,
798 stream->min_inspect_depth, raw_progress);
799 }
800
801 if (use_app) {
802 const uint64_t app_le = GetLeftEdgeForApp(f, ssn, stream);
803 left_edge = MIN(app_le, raw_progress);
804 SCLogDebug("left_edge %" PRIu64 ", using both app:%" PRIu64 ", raw:%" PRIu64, left_edge,
805 app_le, raw_progress);
806 } else {
807 left_edge = raw_progress;
808 SCLogDebug("left_edge %"PRIu64", using only raw:%"PRIu64,
809 left_edge, raw_progress);
810 }
811 } else if (use_app) {
812 const uint64_t app_le = GetLeftEdgeForApp(f, ssn, stream);
813 left_edge = app_le;
814 SCLogDebug("left_edge %" PRIu64 ", using only app:%" PRIu64, left_edge, app_le);
815 } else {
816 left_edge = StreamingBufferGetConsecutiveDataRightEdge(&stream->sb);
817 SCLogDebug("no app & raw: left_edge %"PRIu64" (full stream)", left_edge);
818 }
819
820 if (use_log) {
821 if (use_app || use_raw) {
822 left_edge = MIN(left_edge, STREAM_LOG_PROGRESS(stream));
823 } else {
824 left_edge = STREAM_LOG_PROGRESS(stream);
825 }
826 }
827
828 uint64_t last_ack_abs = STREAM_BASE_OFFSET(stream);
829 if (STREAM_LASTACK_GT_BASESEQ(stream)) {
830 last_ack_abs += (stream->last_ack - stream->base_seq);
831 }
832 /* in IDS mode we shouldn't see the base_seq pass last_ack */
833 DEBUG_VALIDATE_BUG_ON(last_ack_abs < left_edge && !StreamTcpInlineMode() && !f->ffr &&
834 ssn->state < TCP_CLOSED);
835 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 if (!StreamTcpInlineMode() && check_overlap_different_data) {
841 const uint32_t window = stream->window ? stream->window : 4096;
842 if (window < left_edge)
843 left_edge -= window;
844 else
845 left_edge = 0;
846
847 SCLogDebug("stream:%p left_edge %"PRIu64, stream, left_edge);
848 }
849
850 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 TcpSegment *seg = NULL;
855 RB_FOREACH(seg, TCPSEG, &stream->seg_tree) {
856 if (TCP_SEG_OFFSET(seg) > left_edge) {
857 SCLogDebug("seg beyond left_edge, we're done");
858 break;
859 }
860
861 if (SegmentInUse(stream, seg)) {
862 left_edge = TCP_SEG_OFFSET(seg);
863 SCLogDebug("in-use seg before left_edge, adjust to %"PRIu64" and bail", left_edge);
864 break;
865 }
866 }
867 }
868
869 return left_edge;
870}
871
872static void StreamTcpRemoveSegmentFromStream(TcpStream *stream, TcpSegment *seg)
873{
874 RB_REMOVE(TCPSEG, &stream->seg_tree, seg);
875}
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 */
887{
888 SCEnter();
889
890 if (f == NULL || f->protoctx == NULL) {
891 SCReturn;
892 }
893
894 TcpSession *ssn = f->protoctx;
895 TcpStream *stream = NULL;
896
897 if (flags & STREAM_TOSERVER) {
898 stream = &ssn->client;
899 } else if (flags & STREAM_TOCLIENT) {
900 stream = &ssn->server;
901 } else {
902 SCReturn;
903 }
904
906 return;
907 }
908
911 SCLogDebug("ssn %p / stream %p: reassembly depth reached, "
912 "STREAMTCP_STREAM_FLAG_NOREASSEMBLY set", ssn, stream);
915 return;
916
917 } else if ((ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) &&
919 SCLogDebug("ssn %p / stream %p: both app and raw are done, "
920 "STREAMTCP_STREAM_FLAG_NOREASSEMBLY set", ssn, stream);
924 return;
925 }
926
927 const uint64_t left_edge = GetLeftEdge(f, ssn, stream);
928 SCLogDebug("buffer left_edge %" PRIu64, left_edge);
929 if (left_edge && left_edge > STREAM_BASE_OFFSET(stream)) {
930 DEBUG_VALIDATE_BUG_ON(left_edge - STREAM_BASE_OFFSET(stream) > UINT32_MAX);
931 uint32_t slide = (uint32_t)(left_edge - STREAM_BASE_OFFSET(stream));
932 SCLogDebug("buffer sliding %u to offset %"PRIu64, slide, left_edge);
933
935 AppLayerFramesSlide(f, slide, flags & (STREAM_TOSERVER | STREAM_TOCLIENT));
936 }
937 StreamingBufferSlideToOffset(&stream->sb, &stream_config.sbcnf, left_edge);
938 stream->base_seq += slide;
939
940 if (slide <= stream->app_progress_rel) {
941 stream->app_progress_rel -= slide;
942 } else {
943 stream->app_progress_rel = 0;
944 }
945 if (slide <= stream->raw_progress_rel) {
946 stream->raw_progress_rel -= slide;
947 } else {
948 stream->raw_progress_rel = 0;
949 }
950 if (slide <= stream->log_progress_rel) {
951 stream->log_progress_rel -= slide;
952 } else {
953 stream->log_progress_rel = 0;
954 }
955
956 SCLogDebug("stream base_seq %u at stream offset %"PRIu64,
957 stream->base_seq, STREAM_BASE_OFFSET(stream));
958 }
959
960 /* loop through the segments and remove all not in use */
961 TcpSegment *seg = NULL, *safe = NULL;
962 RB_FOREACH_SAFE(seg, TCPSEG, &stream->seg_tree, safe)
963 {
964 SCLogDebug("seg %p, SEQ %"PRIu32", LEN %"PRIu16", SUM %"PRIu32,
965 seg, seg->seq, TCP_SEG_LEN(seg),
966 (uint32_t)(seg->seq + TCP_SEG_LEN(seg)));
967
968 if (StreamTcpReturnSegmentCheck(stream, seg) == 0) {
969 SCLogDebug("not removing segment");
970 break;
971 }
972
973 StreamTcpRemoveSegmentFromStream(stream, seg);
975 SCLogDebug("removed segment");
976 continue;
977 }
978
979 SCReturn;
980}
981
982
983/*
984 * unittests
985 */
986
987#ifdef UNITTESTS
989#endif
void AppLayerFramesSlide(Flow *f, const uint32_t slide, const uint8_t direction)
FramesContainer * AppLayerFramesGetContainer(Flow *f)
struct HtpBodyChunk_ * next
void StatsIncr(ThreadVars *tv, uint16_t id)
Increments the local counter.
Definition counters.c:166
@ STREAM_REASSEMBLY_INSERT_INVALID
@ STREAM_REASSEMBLY_INSERT_LIMIT
@ STREAM_REASSEMBLY_INSERT_MEMCAP
@ STREAM_REASSEMBLY_OVERLAP_DIFFERENT_DATA
uint8_t flags
Definition decode-gre.h:0
#define GET_PKT_DATA(p)
Definition decode.h:209
#define GET_PKT_LEN(p)
Definition decode.h:208
TcpStreamCnf stream_config
Definition stream-tcp.c:219
ThreadVars * tv
void StreamTcpInlineSegmentReplacePacket(const TcpStream *stream, Packet *p, const TcpSegment *seg)
Replace (part of) the payload portion of a packet by the data in a TCP segment.
int StreamTcpInlineSegmentCompare(const TcpStream *stream, const Packet *p, const TcpSegment *seg)
Compare the shared data portion of two segments.
void StreamTcpReassembleConfigEnableOverlapCheck(void)
int StreamTcpReassembleInsertSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, TcpStream *stream, TcpSegment *seg, Packet *p, uint8_t *pkt_data, uint16_t pkt_datalen)
int TcpSegmentCompare(struct TcpSegment *a, struct TcpSegment *b)
compare function for the Segment tree
void StreamTcpPruneSession(Flow *f, uint8_t flags)
Remove idle TcpSegments from TcpSession.
#define STREAMTCP_STREAM_FLAG_NOREASSEMBLY
#define SEQ_GEQ(a, b)
#define SEQ_GT(a, b)
#define STREAMTCP_STREAM_FLAG_DISABLE_RAW
#define TCP_SEG_LEN(seg)
#define SEQ_EQ(a, b)
#define SEG_SEQ_RIGHT_EDGE(seg)
#define StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream)
#define STREAM_APP_PROGRESS(stream)
@ TCP_CLOSED
#define STREAM_BASE_OFFSET(stream)
#define SEQ_LEQ(a, b)
#define StreamTcpSetEvent(p, e)
#define TCP_SEG_OFFSET(seg)
#define STREAM_LOG_PROGRESS(stream)
#define SEQ_LT(a, b)
#define STREAMTCP_FLAG_APP_LAYER_DISABLED
#define STREAMTCP_STREAM_FLAG_DEPTH_REACHED
#define STREAM_RAW_PROGRESS(stream)
void StreamTcpSegmentReturntoPool(TcpSegment *seg)
Function to return the segment back to the pool.
void * StreamTcpReassembleRealloc(void *optr, size_t orig_size, size_t size)
bool IsTcpSessionDumpingEnabled(void)
void StreamTcpReturnStreamSegments(TcpStream *stream)
return all segments in this stream into the pool(s)
@ OS_POLICY_LAST
@ OS_POLICY_MACOS
@ OS_POLICY_WINDOWS
@ OS_POLICY_IRIX
@ OS_POLICY_HPUX10
@ OS_POLICY_LINUX
@ OS_POLICY_OLD_LINUX
@ OS_POLICY_BSD
@ OS_POLICY_WINDOWS2K3
@ OS_POLICY_HPUX11
@ OS_POLICY_SOLARIS
bool StreamTcpInlineMode(void)
See if stream engine is operating in inline mode.
Flow data structure.
Definition flow.h:356
void * protoctx
Definition flow.h:441
uint8_t ffr
Definition flow.h:390
uint32_t left_edge_rel
SCTime_t ts
Definition decode.h:555
uint8_t * payload
Definition decode.h:605
struct Packet_ * root
Definition decode.h:653
uint16_t payload_len
Definition decode.h:606
StreamingBufferSegment sbseg
TcpSegmentPcapHdrStorage * pcap_hdr_storage
uint16_t payload_len
uint16_t reassembly_toserver_chunk_size
Definition stream-tcp.h:77
bool streaming_log_api
Definition stream-tcp.h:72
StreamingBufferConfig sbcnf
Definition stream-tcp.h:89
uint32_t log_progress_rel
uint32_t min_inspect_depth
StreamingBuffer sb
uint32_t raw_progress_rel
struct TCPSEG seg_tree
uint32_t segs_right_edge
uint32_t app_progress_rel
Per thread variable structure.
Definition threadvars.h:58
#define MIN(x, y)
#define RB_EMPTY(head)
Definition tree.h:327
#define RB_FOREACH(x, name, head)
Definition tree.h:781
#define RB_FOREACH_FROM(x, name, y)
Definition tree.h:786
#define RB_FOREACH_SAFE(x, name, head, y)
Definition tree.h:791
#define RB_FOREACH_REVERSE_FROM(x, name, y)
Definition tree.h:801
#define RB_REMOVE(name, x, y)
Definition tree.h:773
#define RB_GENERATE(name, type, field, cmp)
Definition tree.h:421
int SCLogDebugEnabled(void)
Returns whether debug messages are enabled to be logged or not.
Definition util-debug.c:767
#define SCEnter(...)
Definition util-debug.h:277
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCReturnInt(x)
Definition util-debug.h:281
#define SCReturn
Definition util-debug.h:279
@ SC_ELIMIT
Definition util-error.h:31
@ SC_EINVAL
Definition util-error.h:30
@ SC_ENOMEM
Definition util-error.h:29
@ SC_OK
Definition util-error.h:27
#define likely(expr)
void StreamingBufferSegmentGetData(const StreamingBuffer *sb, const StreamingBufferSegment *seg, const uint8_t **data, uint32_t *data_len)
int StreamingBufferInsertAt(StreamingBuffer *sb, const StreamingBufferConfig *cfg, StreamingBufferSegment *seg, const uint8_t *data, uint32_t data_len, uint64_t offset)
void StreamingBufferSlideToOffset(StreamingBuffer *sb, const StreamingBufferConfig *cfg, uint64_t offset)
slide to absolute offset
void StreamingBufferClear(StreamingBuffer *sb, const StreamingBufferConfig *cfg)
int StreamingBufferSegmentIsBeforeWindow(const StreamingBuffer *sb, const StreamingBufferSegment *seg)
int StreamingBufferGetData(const StreamingBuffer *sb, const uint8_t **data, uint32_t *data_len, uint64_t *stream_offset)
uint64_t stream_offset
#define SCTIME_INITIALIZER
Definition util-time.h:51
#define DEBUG_VALIDATE_BUG_ON(exp)