Coverage Report

Created: 2026-01-16 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/detect-mark.c
Line
Count
Source
1
/* Copyright (C) 2011-2021 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 Eric Leblond <eric@regit.org>
22
 *
23
 * Implements the mark keyword. Based  on detect-gid
24
 * by Breno Silva <breno.silva@gmail.com>
25
 */
26
27
#include "suricata-common.h"
28
#include "suricata.h"
29
#include "decode.h"
30
#include "detect.h"
31
#include "flow-var.h"
32
#include "decode-events.h"
33
34
#include "detect-mark.h"
35
#include "detect-parse.h"
36
37
#include "util-unittest.h"
38
#include "util-debug.h"
39
40
73
#define PARSE_REGEX "([0x]*[0-9a-f]+)/([0x]*[0-9a-f]+)"
41
42
static DetectParseRegex parse_regex;
43
44
static int DetectMarkSetup (DetectEngineCtx *, Signature *, const char *);
45
static int DetectMarkPacket(DetectEngineThreadCtx *det_ctx, Packet *p,
46
        const Signature *s, const SigMatchCtx *ctx);
47
void DetectMarkDataFree(DetectEngineCtx *, void *ptr);
48
#if defined UNITTESTS && defined NFQ
49
static void MarkRegisterTests(void);
50
#endif
51
52
/**
53
 * \brief Registration function for nfq_set_mark: keyword
54
 */
55
56
void DetectMarkRegister (void)
57
73
{
58
73
    sigmatch_table[DETECT_MARK].name = "nfq_set_mark";
59
73
    sigmatch_table[DETECT_MARK].Match = DetectMarkPacket;
60
73
    sigmatch_table[DETECT_MARK].Setup = DetectMarkSetup;
61
73
    sigmatch_table[DETECT_MARK].Free  = DetectMarkDataFree;
62
#if defined UNITTESTS && defined NFQ
63
    sigmatch_table[DETECT_MARK].RegisterTests = MarkRegisterTests;
64
#endif
65
73
    DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
66
73
}
67
68
#ifdef NFQ
69
/**
70
 * \internal
71
 * \brief This function is used to parse mark options passed via mark: keyword
72
 *
73
 * \param rawstr Pointer to the user provided mark options
74
 *
75
 * \retval 0 on success
76
 * \retval < 0 on failure
77
 */
78
static void * DetectMarkParse (const char *rawstr)
79
{
80
    int res = 0;
81
    size_t pcre2_len;
82
    const char *str_ptr = NULL;
83
    char *ptr = NULL;
84
    char *endptr = NULL;
85
    uint32_t mark;
86
    uint32_t mask;
87
    DetectMarkData *data;
88
89
    pcre2_match_data *match = NULL;
90
    int ret = DetectParsePcreExec(&parse_regex, &match, rawstr, 0, 0);
91
    if (ret < 1) {
92
        SCLogError("pcre_exec parse error, ret %" PRId32 ", string %s", ret, rawstr);
93
        pcre2_match_data_free(match);
94
        return NULL;
95
    }
96
97
    res = pcre2_substring_get_bynumber(match, 1, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
98
    if (res < 0) {
99
        SCLogError("pcre2_substring_get_bynumber failed");
100
        goto error;
101
    }
102
103
    ptr = (char *)str_ptr;
104
105
    if (ptr == NULL)
106
        goto error;
107
108
    errno = 0;
109
    mark = strtoul(ptr, &endptr, 0);
110
    if (errno == ERANGE) {
111
        SCLogError("Numeric value out of range");
112
        pcre2_substring_free((PCRE2_UCHAR8 *)ptr);
113
        goto error;
114
    }     /* If there is no numeric value in the given string then strtoull(), makes
115
             endptr equals to ptr and return 0 as result */
116
    else if (endptr == ptr && mark == 0) {
117
        SCLogError("No numeric value");
118
        pcre2_substring_free((PCRE2_UCHAR8 *)ptr);
119
        goto error;
120
    } else if (endptr == ptr) {
121
        SCLogError("Invalid numeric value");
122
        pcre2_substring_free((PCRE2_UCHAR8 *)ptr);
123
        goto error;
124
    }
125
126
    res = pcre2_substring_get_bynumber(match, 2, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
127
    if (res < 0) {
128
        SCLogError("pcre2_substring_get_bynumber failed");
129
        goto error;
130
    }
131
132
    pcre2_substring_free((PCRE2_UCHAR8 *)ptr);
133
    ptr = (char *)str_ptr;
134
135
    if (ptr == NULL) {
136
        data = SCMalloc(sizeof(DetectMarkData));
137
        if (unlikely(data == NULL)) {
138
            goto error;
139
        }
140
        data->mark = mark;
141
        data->mask = 0xffff;
142
        pcre2_match_data_free(match);
143
        return data;
144
    }
145
146
    errno = 0;
147
    mask = strtoul(ptr, &endptr, 0);
148
    if (errno == ERANGE) {
149
        SCLogError("Numeric value out of range");
150
        pcre2_substring_free((PCRE2_UCHAR8 *)ptr);
151
        goto error;
152
    }     /* If there is no numeric value in the given string then strtoull(), makes
153
             endptr equals to ptr and return 0 as result */
154
    else if (endptr == ptr && mask == 0) {
155
        SCLogError("No numeric value");
156
        pcre2_substring_free((PCRE2_UCHAR8 *)ptr);
157
        goto error;
158
    }
159
    else if (endptr == ptr) {
160
        SCLogError("Invalid numeric value");
161
        pcre2_substring_free((PCRE2_UCHAR8 *)ptr);
162
        goto error;
163
    }
164
165
    SCLogDebug("Rule will set mark 0x%x with mask 0x%x", mark, mask);
166
    pcre2_substring_free((PCRE2_UCHAR8 *)ptr);
167
168
    data = SCMalloc(sizeof(DetectMarkData));
169
    if (unlikely(data == NULL)) {
170
        goto error;
171
    }
172
    data->mark = mark;
173
    data->mask = mask;
174
    pcre2_match_data_free(match);
175
    return data;
176
177
error:
178
    if (match) {
179
        pcre2_match_data_free(match);
180
    }
181
    return NULL;
182
}
183
184
#endif /* NFQ */
185
186
/**
187
 * \internal
188
 * \brief this function is used to add the parsed mark into the current signature
189
 *
190
 * \param de_ctx pointer to the Detection Engine Context
191
 * \param s pointer to the Current Signature
192
 * \param rawstr pointer to the user provided mark options
193
 *
194
 * \retval 0 on Success
195
 * \retval -1 on Failure
196
 */
197
static int DetectMarkSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
198
542
{
199
542
#ifndef NFQ
200
542
    return 0;
201
#else
202
    DetectMarkData *data = DetectMarkParse(rawstr);
203
    if (data == NULL) {
204
        return -1;
205
    }
206
    SigMatch *sm = SigMatchAlloc();
207
    if (sm == NULL) {
208
        DetectMarkDataFree(de_ctx, data);
209
        return -1;
210
    }
211
212
    sm->type = DETECT_MARK;
213
    sm->ctx = (SigMatchCtx *)data;
214
215
    /* Append it to the list of post match, so the mark is set if the
216
     * full signature matches. */
217
    SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_POSTMATCH);
218
    return 0;
219
#endif
220
542
}
221
222
void DetectMarkDataFree(DetectEngineCtx *de_ctx, void *ptr)
223
0
{
224
0
    DetectMarkData *data = (DetectMarkData *)ptr;
225
0
    SCFree(data);
226
0
}
227
228
229
static int DetectMarkPacket(DetectEngineThreadCtx *det_ctx, Packet *p,
230
        const Signature *s, const SigMatchCtx *ctx)
231
0
{
232
#ifdef NFQ
233
    const DetectMarkData *nf_data = (const DetectMarkData *)ctx;
234
    if (nf_data->mask) {
235
        if (!(IS_TUNNEL_PKT(p))) {
236
            /* coverity[missing_lock] */
237
            p->nfq_v.mark = (nf_data->mark & nf_data->mask)
238
                | (p->nfq_v.mark & ~(nf_data->mask));
239
            p->flags |= PKT_MARK_MODIFIED;
240
        } else {
241
            /* real tunnels may have multiple flows inside them, so marking
242
             * might 'mark' too much. Rebuilt packets from IP fragments
243
             * are fine. */
244
            if (p->flags & PKT_REBUILT_FRAGMENT) {
245
                Packet *tp = p->root ? p->root : p;
246
                SCSpinLock(&tp->persistent.tunnel_lock);
247
                tp->nfq_v.mark = (nf_data->mark & nf_data->mask)
248
                    | (tp->nfq_v.mark & ~(nf_data->mask));
249
                tp->flags |= PKT_MARK_MODIFIED;
250
                SCSpinUnlock(&tp->persistent.tunnel_lock);
251
            }
252
        }
253
    }
254
#endif
255
0
    return 1;
256
0
}
257
258
/*
259
 * ONLY TESTS BELOW THIS COMMENT
260
 */
261
262
#if defined UNITTESTS && defined NFQ
263
/**
264
 * \test MarkTestParse01 is a test for a valid mark value
265
 *
266
 */
267
static int MarkTestParse01 (void)
268
{
269
    DetectMarkData *data;
270
271
    data = DetectMarkParse("1/1");
272
273
    FAIL_IF_NULL(data);
274
275
    DetectMarkDataFree(NULL, data);
276
    PASS;
277
}
278
279
/**
280
 * \test MarkTestParse02 is a test for an invalid mark value
281
 *
282
 */
283
static int MarkTestParse02 (void)
284
{
285
    DetectMarkData *data;
286
287
    data = DetectMarkParse("4");
288
289
    FAIL_IF_NOT_NULL(data);
290
291
    DetectMarkDataFree(NULL, data);
292
    PASS;
293
}
294
295
/**
296
 * \test MarkTestParse03 is a test for a valid mark value
297
 *
298
 */
299
static int MarkTestParse03 (void)
300
{
301
    DetectMarkData *data;
302
303
    data = DetectMarkParse("0x10/0xff");
304
305
    FAIL_IF_NULL(data);
306
307
    DetectMarkDataFree(NULL, data);
308
    PASS;
309
}
310
311
/**
312
 * \test MarkTestParse04 is a test for a invalid mark value
313
 *
314
 */
315
static int MarkTestParse04 (void)
316
{
317
    DetectMarkData *data;
318
319
    data = DetectMarkParse("0x1g/0xff");
320
321
    FAIL_IF_NOT_NULL(data);
322
323
    DetectMarkDataFree(NULL, data);
324
    PASS;
325
}
326
327
/**
328
 * \brief this function registers unit tests for Mark
329
 */
330
static void MarkRegisterTests(void)
331
{
332
    UtRegisterTest("MarkTestParse01", MarkTestParse01);
333
    UtRegisterTest("MarkTestParse02", MarkTestParse02);
334
    UtRegisterTest("MarkTestParse03", MarkTestParse03);
335
    UtRegisterTest("MarkTestParse04", MarkTestParse04);
336
}
337
#endif /* UNITTESTS */