Coverage Report

Created: 2026-05-16 07:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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);
TCPSEG_RB_INSERT_COLOR
Line
Count
Source
49
RB_GENERATE(TCPSEG, TcpSegment, rb, TcpSegmentCompare);
TCPSEG_RB_REMOVE_COLOR
Line
Count
Source
49
RB_GENERATE(TCPSEG, TcpSegment, rb, TcpSegmentCompare);
TCPSEG_RB_INSERT
Line
Count
Source
49
RB_GENERATE(TCPSEG, TcpSegment, rb, TcpSegmentCompare);
TCPSEG_RB_REMOVE
Line
Count
Source
49
RB_GENERATE(TCPSEG, TcpSegment, rb, TcpSegmentCompare);
Unexecuted instantiation: TCPSEG_RB_FIND
Unexecuted instantiation: TCPSEG_RB_NFIND
TCPSEG_RB_MINMAX
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