Coverage Report

Created: 2026-03-31 07:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/detect-stream_size.c
Line
Count
Source
1
/* Copyright (C) 2007-2020 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
/**
19
 * \file
20
 *
21
 * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
22
 *
23
 * Stream size for the engine.
24
 */
25
26
#include "suricata-common.h"
27
#include "stream-tcp.h"
28
#include "util-unittest.h"
29
30
#include "detect.h"
31
#include "detect-parse.h"
32
33
#include "flow.h"
34
#include "detect-stream_size.h"
35
#include "stream-tcp-private.h"
36
#include "detect-engine-prefilter-common.h"
37
#include "detect-engine-uint.h"
38
#include "util-debug.h"
39
#include "util-byte.h"
40
41
42
/*prototypes*/
43
static int DetectStreamSizeMatch (DetectEngineThreadCtx *, Packet *,
44
        const Signature *, const SigMatchCtx *);
45
static int DetectStreamSizeSetup (DetectEngineCtx *, Signature *, const char *);
46
void DetectStreamSizeFree(DetectEngineCtx *de_ctx, void *);
47
#ifdef UNITTESTS
48
static void DetectStreamSizeRegisterTests(void);
49
#endif
50
static int PrefilterSetupStreamSize(DetectEngineCtx *de_ctx, SigGroupHead *sgh);
51
static bool PrefilterStreamSizeIsPrefilterable(const Signature *s);
52
53
/**
54
 * \brief Registration function for stream_size: keyword
55
 */
56
57
void DetectStreamSizeRegister(void)
58
73
{
59
73
    sigmatch_table[DETECT_STREAM_SIZE].name = "stream_size";
60
73
    sigmatch_table[DETECT_STREAM_SIZE].desc = "match on amount of bytes of a stream";
61
73
    sigmatch_table[DETECT_STREAM_SIZE].url = "/rules/flow-keywords.html#stream-size";
62
73
    sigmatch_table[DETECT_STREAM_SIZE].Match = DetectStreamSizeMatch;
63
73
    sigmatch_table[DETECT_STREAM_SIZE].Setup = DetectStreamSizeSetup;
64
73
    sigmatch_table[DETECT_STREAM_SIZE].Free = DetectStreamSizeFree;
65
#ifdef UNITTESTS
66
    sigmatch_table[DETECT_STREAM_SIZE].RegisterTests = DetectStreamSizeRegisterTests;
67
#endif
68
73
    sigmatch_table[DETECT_STREAM_SIZE].SupportsPrefilter = PrefilterStreamSizeIsPrefilterable;
69
73
    sigmatch_table[DETECT_STREAM_SIZE].SetupPrefilter = PrefilterSetupStreamSize;
70
73
}
71
72
static int DetectStreamSizeMatchAux(const DetectStreamSizeData *sd, const TcpSession *ssn)
73
1.47k
{
74
1.47k
    int ret = 0;
75
1.47k
    uint32_t csdiff = 0;
76
1.47k
    uint32_t ssdiff = 0;
77
78
1.47k
    if (sd->flags == StreamSizeServer) {
79
        /* get the server stream size */
80
1.47k
        ssdiff = ssn->server.next_seq - ssn->server.isn;
81
1.47k
        ret = DetectU32Match(ssdiff, &sd->du32);
82
83
1.47k
    } else if (sd->flags == StreamSizeClient) {
84
        /* get the client stream size */
85
0
        csdiff = ssn->client.next_seq - ssn->client.isn;
86
0
        ret = DetectU32Match(csdiff, &sd->du32);
87
88
0
    } else if (sd->flags == StreamSizeBoth) {
89
0
        ssdiff = ssn->server.next_seq - ssn->server.isn;
90
0
        csdiff = ssn->client.next_seq - ssn->client.isn;
91
92
0
        if (DetectU32Match(ssdiff, &sd->du32) && DetectU32Match(csdiff, &sd->du32))
93
0
            ret = 1;
94
95
0
    } else if (sd->flags == StreamSizeEither) {
96
0
        ssdiff = ssn->server.next_seq - ssn->server.isn;
97
0
        csdiff = ssn->client.next_seq - ssn->client.isn;
98
99
0
        if (DetectU32Match(ssdiff, &sd->du32) || DetectU32Match(csdiff, &sd->du32))
100
0
            ret = 1;
101
0
    }
102
1.47k
    return ret;
103
1.47k
}
104
105
/**
106
 * \brief This function is used to match Stream size rule option on a packet with those passed via
107
 * stream_size:
108
 *
109
 * \param t pointer to thread vars
110
 * \param det_ctx pointer to the pattern matcher thread
111
 * \param p pointer to the current packet
112
 * \param m pointer to the sigmatch that we will cast into DetectStreamSizeData
113
 *
114
 * \retval 0 no match
115
 * \retval 1 match
116
 */
117
static int DetectStreamSizeMatch(
118
        DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx)
119
20
{
120
121
20
    const DetectStreamSizeData *sd = (const DetectStreamSizeData *)ctx;
122
123
20
    if (!(PKT_IS_TCP(p)))
124
0
        return 0;
125
20
    if (p->flow == NULL || p->flow->protoctx == NULL)
126
0
        return 0;
127
128
20
    const TcpSession *ssn = (TcpSession *)p->flow->protoctx;
129
130
20
    SCReturnInt(DetectStreamSizeMatchAux(sd, ssn));
131
20
}
132
133
/**
134
 * \brief this function is used to add the parsed stream size data into the current signature
135
 *
136
 * \param de_ctx pointer to the Detection Engine Context
137
 * \param s pointer to the Current Signature
138
 * \param streamstr pointer to the user provided stream size options
139
 *
140
 * \retval 0 on Success
141
 * \retval -1 on Failure
142
 */
143
static int DetectStreamSizeSetup (DetectEngineCtx *de_ctx, Signature *s, const char *streamstr)
144
13.3k
{
145
13.3k
    DetectStreamSizeData *sd = rs_detect_stream_size_parse(streamstr);
146
13.3k
    if (sd == NULL)
147
5.70k
        return -1;
148
149
7.65k
    SigMatch *sm = SigMatchAlloc();
150
7.65k
    if (sm == NULL) {
151
0
        DetectStreamSizeFree(de_ctx, sd);
152
0
        return -1;
153
0
    }
154
155
7.65k
    sm->type = DETECT_STREAM_SIZE;
156
7.65k
    sm->ctx = (SigMatchCtx *)sd;
157
158
7.65k
    SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
159
7.65k
    return 0;
160
7.65k
}
161
162
/**
163
 * \brief this function will free memory associated with DetectStreamSizeData
164
 *
165
 * \param ptr pointer to DetectStreamSizeData
166
 */
167
void DetectStreamSizeFree(DetectEngineCtx *de_ctx, void *ptr)
168
7.65k
{
169
7.65k
    rs_detect_stream_size_free(ptr);
170
7.65k
}
171
172
/* prefilter code */
173
174
static void PrefilterPacketStreamsizeMatch(
175
        DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
176
1.55k
{
177
1.55k
    if (!(PKT_IS_TCP(p)) || PKT_IS_PSEUDOPKT(p))
178
57
        return;
179
180
1.49k
    if (p->flow == NULL || p->flow->protoctx == NULL)
181
42
        return;
182
183
    /* during setup Suricata will automatically see if there is another
184
     * check that can be added: alproto, sport or dport */
185
1.45k
    const PrefilterPacketHeaderCtx *ctx = pectx;
186
1.45k
    if (!PrefilterPacketHeaderExtraMatch(ctx, p))
187
93
        return;
188
189
1.36k
    DetectStreamSizeData dsd;
190
1.36k
    dsd.du32.mode = ctx->v1.u8[0];
191
1.36k
    dsd.flags = ctx->v1.u8[1];
192
1.36k
    dsd.du32.arg1 = ctx->v1.u32[2];
193
1.36k
    const TcpSession *ssn = (TcpSession *)p->flow->protoctx;
194
    /* if we match, add all the sigs that use this prefilter. This means
195
     * that these will be inspected further */
196
1.36k
    if (DetectStreamSizeMatchAux(&dsd, ssn)) {
197
1.36k
        PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt);
198
1.36k
    }
199
1.36k
}
200
201
static void PrefilterPacketStreamSizeSet(PrefilterPacketHeaderValue *v, void *smctx)
202
184
{
203
184
    const DetectStreamSizeData *a = smctx;
204
184
    v->u8[0] = a->du32.mode;
205
184
    v->u8[1] = a->flags;
206
184
    v->u32[2] = a->du32.arg1;
207
184
}
208
209
static bool PrefilterPacketStreamSizeCompare(PrefilterPacketHeaderValue v, void *smctx)
210
33
{
211
33
    const DetectStreamSizeData *a = smctx;
212
33
    if (v.u8[0] == a->du32.mode && v.u8[1] == a->flags && v.u32[2] == a->du32.arg1)
213
33
        return true;
214
0
    return false;
215
33
}
216
217
static int PrefilterSetupStreamSize(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
218
226
{
219
226
    return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_STREAM_SIZE, PrefilterPacketStreamSizeSet,
220
226
            PrefilterPacketStreamSizeCompare, PrefilterPacketStreamsizeMatch);
221
226
}
222
223
static bool PrefilterStreamSizeIsPrefilterable(const Signature *s)
224
0
{
225
0
    const SigMatch *sm;
226
0
    for (sm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; sm != NULL; sm = sm->next) {
227
0
        switch (sm->type) {
228
0
            case DETECT_STREAM_SIZE:
229
0
                return true;
230
0
        }
231
0
    }
232
0
    return false;
233
0
}
234
235
#ifdef UNITTESTS
236
/**
237
 * \test DetectStreamSizeParseTest01 is a test to make sure that we parse the
238
 *  user options correctly, when given valid stream_size options.
239
 */
240
241
static int DetectStreamSizeParseTest01 (void)
242
{
243
    int result = 0;
244
    DetectStreamSizeData *sd = NULL;
245
    sd = rs_detect_stream_size_parse("server,<,6");
246
    if (sd != NULL) {
247
        if (sd->flags & StreamSizeServer && sd->du32.mode == DETECT_UINT_LT && sd->du32.arg1 == 6)
248
            result = 1;
249
        DetectStreamSizeFree(NULL, sd);
250
    }
251
252
    return result;
253
}
254
255
/**
256
 * \test DetectStreamSizeParseTest02 is a test to make sure that we detect the
257
 *  invalid stream_size options.
258
 */
259
260
static int DetectStreamSizeParseTest02 (void)
261
{
262
    int result = 1;
263
    DetectStreamSizeData *sd = NULL;
264
    sd = rs_detect_stream_size_parse("invalidoption,<,6");
265
    if (sd != NULL) {
266
        printf("expected: NULL got 0x%02X %" PRIu32 ": ", sd->flags, sd->du32.arg1);
267
        result = 0;
268
        DetectStreamSizeFree(NULL, sd);
269
    }
270
271
    return result;
272
}
273
274
/**
275
 * \test DetectStreamSizeParseTest03 is a test to make sure that we match the
276
 *  packet correctly provided valid stream size.
277
 */
278
279
static int DetectStreamSizeParseTest03 (void)
280
{
281
282
    int result = 0;
283
    DetectStreamSizeData *sd = NULL;
284
    TcpSession ssn;
285
    ThreadVars tv;
286
    DetectEngineThreadCtx dtx;
287
    Packet *p = PacketGetFromAlloc();
288
    if (unlikely(p == NULL))
289
        return 0;
290
    Signature s;
291
    SigMatch sm;
292
    TcpStream client;
293
    Flow f;
294
    TCPHdr tcph;
295
296
    memset(&ssn, 0, sizeof(TcpSession));
297
    memset(&tv, 0, sizeof(ThreadVars));
298
    memset(&dtx, 0, sizeof(DetectEngineThreadCtx));
299
    memset(&s, 0, sizeof(Signature));
300
    memset(&sm, 0, sizeof(SigMatch));
301
    memset(&client, 0, sizeof(TcpStream));
302
    memset(&f, 0, sizeof(Flow));
303
    memset(&tcph, 0, sizeof(TCPHdr));
304
305
    sd = rs_detect_stream_size_parse("client,>,8");
306
    if (sd != NULL) {
307
        if (!(sd->flags & StreamSizeClient)) {
308
            printf("sd->flags not STREAM_SIZE_CLIENT: ");
309
            DetectStreamSizeFree(NULL, sd);
310
            SCFree(p);
311
            return 0;
312
        }
313
314
        if (sd->du32.mode != DETECT_UINT_GT) {
315
            printf("sd->mode not DETECTSSIZE_GT: ");
316
            DetectStreamSizeFree(NULL, sd);
317
            SCFree(p);
318
            return 0;
319
        }
320
321
        if (sd->du32.arg1 != 8) {
322
            printf("sd->ssize is %" PRIu32 ", not 8: ", sd->du32.arg1);
323
            DetectStreamSizeFree(NULL, sd);
324
            SCFree(p);
325
            return 0;
326
        }
327
    } else {
328
        printf("sd == NULL: ");
329
        SCFree(p);
330
        return 0;
331
    }
332
333
    client.next_seq = 20;
334
    client.isn = 10;
335
    ssn.client = client;
336
    f.protoctx = &ssn;
337
    p->flow = &f;
338
    p->tcph = &tcph;
339
    sm.ctx = (SigMatchCtx*)sd;
340
341
    result = DetectStreamSizeMatch(&dtx, p, &s, sm.ctx);
342
    if (result == 0) {
343
        printf("result 0 != 1: ");
344
    }
345
    DetectStreamSizeFree(NULL, sd);
346
    SCFree(p);
347
    return result;
348
}
349
350
/**
351
 * \test DetectStreamSizeParseTest04 is a test to make sure that we match the
352
 *  stream_size against invalid packet parameters.
353
 */
354
355
static int DetectStreamSizeParseTest04 (void)
356
{
357
358
    int result = 0;
359
    DetectStreamSizeData *sd = NULL;
360
    TcpSession ssn;
361
    ThreadVars tv;
362
    DetectEngineThreadCtx dtx;
363
    Packet *p = PacketGetFromAlloc();
364
    if (unlikely(p == NULL))
365
        return 0;
366
    Signature s;
367
    SigMatch sm;
368
    TcpStream client;
369
    Flow f;
370
    IPV4Hdr ip4h;
371
372
    memset(&ssn, 0, sizeof(TcpSession));
373
    memset(&tv, 0, sizeof(ThreadVars));
374
    memset(&dtx, 0, sizeof(DetectEngineThreadCtx));
375
    memset(&s, 0, sizeof(Signature));
376
    memset(&sm, 0, sizeof(SigMatch));
377
    memset(&client, 0, sizeof(TcpStream));
378
    memset(&f, 0, sizeof(Flow));
379
    memset(&ip4h, 0, sizeof(IPV4Hdr));
380
381
    sd = rs_detect_stream_size_parse(" client , > , 8 ");
382
    if (sd != NULL) {
383
        if (!(sd->flags & StreamSizeClient) && sd->du32.mode != DETECT_UINT_GT &&
384
                sd->du32.arg1 != 8) {
385
            SCFree(p);
386
            return 0;
387
        }
388
    } else
389
        {
390
        SCFree(p);
391
        return 0;
392
        }
393
394
    client.next_seq = 20;
395
    client.isn = 12;
396
    ssn.client = client;
397
    f.protoctx = &ssn;
398
    p->flow = &f;
399
    p->ip4h = &ip4h;
400
    sm.ctx = (SigMatchCtx*)sd;
401
402
    if (!DetectStreamSizeMatch(&dtx, p, &s, sm.ctx))
403
        result = 1;
404
405
    SCFree(p);
406
    return result;
407
}
408
409
/**
410
 * \brief this function registers unit tests for DetectStreamSize
411
 */
412
void DetectStreamSizeRegisterTests(void)
413
{
414
    UtRegisterTest("DetectStreamSizeParseTest01", DetectStreamSizeParseTest01);
415
    UtRegisterTest("DetectStreamSizeParseTest02", DetectStreamSizeParseTest02);
416
    UtRegisterTest("DetectStreamSizeParseTest03", DetectStreamSizeParseTest03);
417
    UtRegisterTest("DetectStreamSizeParseTest04", DetectStreamSizeParseTest04);
418
}
419
#endif /* UNITTESTS */