• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OISF / suricata / 23374838686

21 Mar 2026 07:29AM UTC coverage: 59.341% (-20.0%) from 79.315%
23374838686

Pull #15075

github

web-flow
Merge 90b4e834f into 6587e363a
Pull Request #15075: Stack 8001 v16.4

38 of 70 new or added lines in 10 files covered. (54.29%)

34165 existing lines in 563 files now uncovered.

119621 of 201584 relevant lines covered (59.34%)

650666.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

53.85
/src/detect-icode.c
1
/* Copyright (C) 2007-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 Gerardo Iglesias <iglesiasg@gmail.com>
22
 *
23
 * Implements icode keyword support
24
 */
25

26
#include "suricata-common.h"
27
#include "decode.h"
28

29
#include "detect.h"
30
#include "detect-parse.h"
31
#include "detect-engine-prefilter-common.h"
32
#include "detect-engine-uint.h"
33
#include "detect-engine-build.h"
34

35
#include "detect-icode.h"
36

37
#include "util-byte.h"
38
#include "util-unittest.h"
39
#include "util-unittest-helper.h"
40
#include "util-debug.h"
41

42
/**
43
 *\brief Regex for parsing our icode options
44
 */
45

46
static int DetectICodeMatch(DetectEngineThreadCtx *, Packet *,
47
        const Signature *, const SigMatchCtx *);
48
static int DetectICodeSetup(DetectEngineCtx *, Signature *, const char *);
49
#ifdef UNITTESTS
50
static void DetectICodeRegisterTests(void);
51
#endif
52
void DetectICodeFree(DetectEngineCtx *, void *);
53

54
static int PrefilterSetupICode(DetectEngineCtx *de_ctx, SigGroupHead *sgh);
55
static bool PrefilterICodeIsPrefilterable(const Signature *s);
56

57
/**
58
 * \brief Registration function for icode: keyword
59
 */
60
void DetectICodeRegister (void)
61
{
3✔
62
    sigmatch_table[DETECT_ICODE].name = "icode";
3✔
63
    sigmatch_table[DETECT_ICODE].desc = "match on specific ICMP id-value";
3✔
64
    sigmatch_table[DETECT_ICODE].url = "/rules/header-keywords.html#icode";
3✔
65
    sigmatch_table[DETECT_ICODE].Match = DetectICodeMatch;
3✔
66
    sigmatch_table[DETECT_ICODE].Setup = DetectICodeSetup;
3✔
67
    sigmatch_table[DETECT_ICODE].Free = DetectICodeFree;
3✔
68
#ifdef UNITTESTS
69
    sigmatch_table[DETECT_ICODE].RegisterTests = DetectICodeRegisterTests;
70
#endif
71
    sigmatch_table[DETECT_ICODE].SupportsPrefilter = PrefilterICodeIsPrefilterable;
3✔
72
    sigmatch_table[DETECT_ICODE].SetupPrefilter = PrefilterSetupICode;
3✔
73
    sigmatch_table[DETECT_ICODE].flags = SIGMATCH_SUPPORT_FIREWALL | SIGMATCH_INFO_UINT8;
3✔
74
}
3✔
75

76
/**
77
 * \brief This function is used to match icode rule option set on a packet with those passed via
78
 * icode:
79
 *
80
 * \param t pointer to thread vars
81
 * \param det_ctx pointer to the pattern matcher thread
82
 * \param p pointer to the current packet
83
 * \param ctx pointer to the sigmatch that we will cast into DetectU8Data
84
 *
85
 * \retval 0 no match
86
 * \retval 1 match
87
 */
88
static int DetectICodeMatch (DetectEngineThreadCtx *det_ctx, Packet *p,
89
        const Signature *s, const SigMatchCtx *ctx)
90
{
18✔
91
    DEBUG_VALIDATE_BUG_ON(PKT_IS_PSEUDOPKT(p));
18✔
92

93
    uint8_t picode;
18✔
94
    if (PacketIsICMPv4(p)) {
18✔
UNCOV
95
        picode = p->icmp_s.code;
×
96
    } else if (PacketIsICMPv6(p)) {
18✔
97
        const ICMPV6Hdr *icmpv6h = PacketGetICMPv6(p);
18✔
98
        picode = ICMPV6_GET_CODE(icmpv6h);
18✔
99
    } else {
18✔
100
        /* Packet not ICMPv4 nor ICMPv6 */
101
        return 0;
×
102
    }
×
103

104
    const DetectU8Data *icd = (const DetectU8Data *)ctx;
18✔
105
    return DetectU8Match(picode, icd);
18✔
106
}
18✔
107

108
/**
109
 * \brief this function is used to add the parsed icode data into the current signature
110
 *
111
 * \param de_ctx pointer to the Detection Engine Context
112
 * \param s pointer to the Current Signature
113
 * \param icodestr pointer to the user provided icode options
114
 *
115
 * \retval 0 on Success
116
 * \retval -1 on Failure
117
 */
118
static int DetectICodeSetup(DetectEngineCtx *de_ctx, Signature *s, const char *icodestr)
119
{
963✔
120

121
    DetectU8Data *icd = NULL;
963✔
122

123
    icd = DetectU8Parse(icodestr);
963✔
124
    if (icd == NULL)
963✔
125
        return -1;
226✔
126

127
    if (SCSigMatchAppendSMToList(
737✔
128
                de_ctx, s, DETECT_ICODE, (SigMatchCtx *)icd, DETECT_SM_LIST_MATCH) == NULL) {
737✔
129
        SCDetectU8Free(icd);
×
130
        return -1;
×
131
    }
×
132
    s->flags |= SIG_FLAG_REQUIRE_PACKET;
737✔
133

134
    return 0;
737✔
135
}
737✔
136

137
/**
138
 * \brief this function will free memory associated with DetectU8Data
139
 *
140
 * \param ptr pointer to DetectU8Data
141
 */
142
void DetectICodeFree(DetectEngineCtx *de_ctx, void *ptr)
143
{
737✔
144
    SCDetectU8Free(ptr);
737✔
145
}
737✔
146

147
/* prefilter code */
148

149
static void PrefilterPacketICodeMatch(DetectEngineThreadCtx *det_ctx,
150
        Packet *p, const void *pectx)
151
{
×
152
    DEBUG_VALIDATE_BUG_ON(PKT_IS_PSEUDOPKT(p));
×
153

154
    uint8_t picode;
×
155
    if (PacketIsICMPv4(p)) {
×
156
        picode = p->icmp_s.code;
×
157
    } else if (PacketIsICMPv6(p)) {
×
158
        const ICMPV6Hdr *icmpv6h = PacketGetICMPv6(p);
×
159
        picode = ICMPV6_GET_CODE(icmpv6h);
×
160
    } else {
×
161
        /* Packet not ICMPv4 nor ICMPv6 */
162
        return;
×
163
    }
×
164

165
    const PrefilterPacketU8HashCtx *h = pectx;
×
166
    const SigsArray *sa = h->array[picode];
×
167
    if (sa) {
×
168
        PrefilterAddSids(&det_ctx->pmq, sa->sigs, sa->cnt);
×
169
    }
×
170
}
×
171

172
static int PrefilterSetupICode(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
UNCOV
173
{
×
UNCOV
174
    return PrefilterSetupPacketHeaderU8Hash(de_ctx, sgh, DETECT_ICODE, SIG_MASK_REQUIRE_REAL_PKT,
×
UNCOV
175
            PrefilterPacketU8Set, PrefilterPacketU8Compare, PrefilterPacketICodeMatch);
×
UNCOV
176
}
×
177

178
static bool PrefilterICodeIsPrefilterable(const Signature *s)
179
{
×
180
    return PrefilterIsPrefilterableById(s, DETECT_ICODE);
×
181
}
×
182

183
#ifdef UNITTESTS
184
#include "detect-engine.h"
185
#include "detect-engine-mpm.h"
186
#include "detect-engine-alert.h"
187

188
/**
189
 * \test DetectICodeParseTest01 is a test for setting a valid icode value
190
 */
191
static int DetectICodeParseTest01(void)
192
{
193
    DetectU8Data *icd = DetectU8Parse("8");
194
    FAIL_IF_NULL(icd);
195
    FAIL_IF_NOT(icd->arg1 == 8);
196
    FAIL_IF_NOT(icd->mode == DETECT_UINT_EQ);
197
    DetectICodeFree(NULL, icd);
198

199
    PASS;
200
}
201

202
/**
203
 * \test DetectICodeParseTest02 is a test for setting a valid icode value
204
 *       with ">" operator
205
 */
206
static int DetectICodeParseTest02(void)
207
{
208
    DetectU8Data *icd = DetectU8Parse(">8");
209
    FAIL_IF_NULL(icd);
210
    FAIL_IF_NOT(icd->arg1 == 8);
211
    FAIL_IF_NOT(icd->mode == DETECT_UINT_GT);
212
    DetectICodeFree(NULL, icd);
213

214
    PASS;
215
}
216

217
/**
218
 * \test DetectICodeParseTest03 is a test for setting a valid icode value
219
 *       with "<" operator
220
 */
221
static int DetectICodeParseTest03(void)
222
{
223
    DetectU8Data *icd = DetectU8Parse("<8");
224
    FAIL_IF_NULL(icd);
225
    FAIL_IF_NOT(icd->arg1 == 8);
226
    FAIL_IF_NOT(icd->mode == DETECT_UINT_LT);
227
    DetectICodeFree(NULL, icd);
228

229
    PASS;
230
}
231

232
/**
233
 * \test DetectICodeParseTest04 is a test for setting a valid icode value
234
 *       with "<>" operator
235
 */
236
static int DetectICodeParseTest04(void)
237
{
238
    DetectU8Data *icd = DetectU8Parse("8<>20");
239
    FAIL_IF_NULL(icd);
240
    FAIL_IF_NOT(icd->arg1 == 8);
241
    FAIL_IF_NOT(icd->arg2 == 20);
242
    FAIL_IF_NOT(icd->mode == DETECT_UINT_RA);
243
    DetectICodeFree(NULL, icd);
244

245
    PASS;
246
}
247

248
/**
249
 * \test DetectICodeParseTest05 is a test for setting a valid icode value
250
 *       with spaces all around
251
 */
252
static int DetectICodeParseTest05(void)
253
{
254
    DetectU8Data *icd = DetectU8Parse("  8 ");
255
    FAIL_IF_NULL(icd);
256
    FAIL_IF_NOT(icd->arg1 == 8);
257
    FAIL_IF_NOT(icd->mode == DETECT_UINT_EQ);
258
    DetectICodeFree(NULL, icd);
259

260
    PASS;
261
}
262

263
/**
264
 * \test DetectICodeParseTest06 is a test for setting a valid icode value
265
 *       with ">" operator and spaces all around
266
 */
267
static int DetectICodeParseTest06(void)
268
{
269
    DetectU8Data *icd = DetectU8Parse("  >  8 ");
270
    FAIL_IF_NULL(icd);
271
    FAIL_IF_NOT(icd->arg1 == 8);
272
    FAIL_IF_NOT(icd->mode == DETECT_UINT_GT);
273
    DetectICodeFree(NULL, icd);
274

275
    PASS;
276
}
277

278
/**
279
 * \test DetectICodeParseTest07 is a test for setting a valid icode value
280
 *       with "<>" operator and spaces all around
281
 */
282
static int DetectICodeParseTest07(void)
283
{
284
    DetectU8Data *icd = DetectU8Parse("  8  <>  20 ");
285
    FAIL_IF_NULL(icd);
286
    FAIL_IF_NOT(icd->arg1 == 8);
287
    FAIL_IF_NOT(icd->arg2 == 20);
288
    FAIL_IF_NOT(icd->mode == DETECT_UINT_RA);
289
    DetectICodeFree(NULL, icd);
290

291
    PASS;
292
}
293

294
/**
295
 * \test DetectICodeParseTest08 is a test for setting an invalid icode value
296
 */
297
static int DetectICodeParseTest08(void)
298
{
299
    DetectU8Data *icd = DetectU8Parse("> 8 <> 20");
300
    FAIL_IF_NOT_NULL(icd);
301

302
    PASS;
303
}
304

305
/**
306
 * \test DetectICodeParseTest09 is a test for setting an invalid icode value
307
 *       with "<<" operator
308
 */
309
static int DetectICodeParseTest09(void)
310
{
311
    DetectU8Data *icd = DetectU8Parse("8<<20");
312
    FAIL_IF_NOT_NULL(icd);
313

314
    PASS;
315
}
316

317
/**
318
 * \test DetectICodeMatchTest01 is a test for checking the working of icode
319
 *       keyword by creating 5 rules and matching a crafted packet against
320
 *       them. 4 out of 5 rules shall trigger.
321
 */
322
static int DetectICodeMatchTest01(void)
323
{
324
    Signature *s = NULL;
325
    ThreadVars th_v;
326
    DetectEngineThreadCtx *det_ctx;
327

328
    memset(&th_v, 0, sizeof(th_v));
329
    StatsThreadInit(&th_v.stats);
330

331
    Packet *p = UTHBuildPacket(NULL, 0, IPPROTO_ICMP);
332
    FAIL_IF_NULL(p);
333
    FAIL_IF_NOT(PacketIsICMPv4(p));
334
    p->icmp_s.code = p->l4.hdrs.icmpv4h->code = 10;
335

336
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
337
    FAIL_IF_NULL(de_ctx);
338

339
    de_ctx->flags |= DE_QUIET;
340

341
    s = DetectEngineAppendSig(de_ctx, "alert icmp any any -> any any (icode:10; sid:1;)");
342
    FAIL_IF_NULL(s);
343

344
    s = DetectEngineAppendSig(de_ctx, "alert icmp any any -> any any (icode:<15; sid:2;)");
345
    FAIL_IF_NULL(s);
346

347
    s = DetectEngineAppendSig(de_ctx, "alert icmp any any -> any any (icode:>20; sid:3;)");
348
    FAIL_IF_NULL(s);
349

350
    s = DetectEngineAppendSig(de_ctx, "alert icmp any any -> any any (icode:8<>20; sid:4;)");
351
    FAIL_IF_NULL(s);
352

353
    s = DetectEngineAppendSig(de_ctx, "alert icmp any any -> any any (icode:20<>8; sid:5;)");
354
    FAIL_IF_NOT_NULL(s);
355

356
    SigGroupBuild(de_ctx);
357
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
358

359
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
360

361
    FAIL_IF(PacketAlertCheck(p, 1) == 0);
362
    FAIL_IF(PacketAlertCheck(p, 2) == 0);
363
    FAIL_IF(PacketAlertCheck(p, 3));
364
    FAIL_IF(PacketAlertCheck(p, 4) == 0);
365

366
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
367
    DetectEngineCtxFree(de_ctx);
368

369
    UTHFreePackets(&p, 1);
370
    StatsThreadCleanup(&th_v.stats);
371
    PASS;
372
}
373

374
/**
375
 * \brief this function registers unit tests for DetectICode
376
 */
377
void DetectICodeRegisterTests(void)
378
{
379
    UtRegisterTest("DetectICodeParseTest01", DetectICodeParseTest01);
380
    UtRegisterTest("DetectICodeParseTest02", DetectICodeParseTest02);
381
    UtRegisterTest("DetectICodeParseTest03", DetectICodeParseTest03);
382
    UtRegisterTest("DetectICodeParseTest04", DetectICodeParseTest04);
383
    UtRegisterTest("DetectICodeParseTest05", DetectICodeParseTest05);
384
    UtRegisterTest("DetectICodeParseTest06", DetectICodeParseTest06);
385
    UtRegisterTest("DetectICodeParseTest07", DetectICodeParseTest07);
386
    UtRegisterTest("DetectICodeParseTest08", DetectICodeParseTest08);
387
    UtRegisterTest("DetectICodeParseTest09", DetectICodeParseTest09);
388
    UtRegisterTest("DetectICodeMatchTest01", DetectICodeMatchTest01);
389
}
390
#endif /* UNITTESTS */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc