suricata
detect-icmp-seq.c
Go to the documentation of this file.
1/* Copyright (C) 2007-2022 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 Breno Silva <breno.silva@gmail.com>
22 *
23 * Implements the icmp_seq keyword
24 */
25
26#include "suricata-common.h"
27#include "decode.h"
28
29#include "detect.h"
30#include "detect-parse.h"
32#include "detect-engine-build.h"
33
34#include "detect-icmp-seq.h"
35
36#include "util-byte.h"
37#include "util-unittest.h"
39#include "util-debug.h"
40
41#define PARSE_REGEX "^\\s*(\"\\s*)?([0-9]+)(\\s*\")?\\s*$"
42
43static DetectParseRegex parse_regex;
44
45static int DetectIcmpSeqMatch(DetectEngineThreadCtx *, Packet *,
46 const Signature *, const SigMatchCtx *);
47static int DetectIcmpSeqSetup(DetectEngineCtx *, Signature *, const char *);
48#ifdef UNITTESTS
49static void DetectIcmpSeqRegisterTests(void);
50#endif
52static int PrefilterSetupIcmpSeq(DetectEngineCtx *de_ctx, SigGroupHead *sgh);
53static bool PrefilterIcmpSeqIsPrefilterable(const Signature *s);
54
55/**
56 * \brief Registration function for icmp_seq
57 */
59{
61 sigmatch_table[DETECT_ICMP_SEQ].desc = "check for a ICMP sequence number";
62 sigmatch_table[DETECT_ICMP_SEQ].url = "/rules/header-keywords.html#icmp-seq";
63 sigmatch_table[DETECT_ICMP_SEQ].Match = DetectIcmpSeqMatch;
64 sigmatch_table[DETECT_ICMP_SEQ].Setup = DetectIcmpSeqSetup;
66#ifdef UNITTESTS
67 sigmatch_table[DETECT_ICMP_SEQ].RegisterTests = DetectIcmpSeqRegisterTests;
68#endif
69 sigmatch_table[DETECT_ICMP_SEQ].SupportsPrefilter = PrefilterIcmpSeqIsPrefilterable;
70 sigmatch_table[DETECT_ICMP_SEQ].SetupPrefilter = PrefilterSetupIcmpSeq;
71
73}
74
75static inline bool GetIcmpSeq(Packet *p, uint16_t *seq)
76{
77 uint16_t seqn;
78
79 if (PacketIsICMPv4(p)) {
80 switch (p->icmp_s.type) {
81 case ICMP_ECHOREPLY:
82 case ICMP_ECHO:
83 case ICMP_TIMESTAMP:
86 case ICMP_INFO_REPLY:
87 case ICMP_ADDRESS:
89 SCLogDebug("ICMPV4_GET_SEQ(p) %"PRIu16" (network byte order), "
90 "%"PRIu16" (host byte order)", ICMPV4_GET_SEQ(p),
92
93 seqn = ICMPV4_GET_SEQ(p);
94 break;
95 default:
96 SCLogDebug("Packet has no seq field");
97 return false;
98 }
99 } else if (PacketIsICMPv6(p)) {
100 switch (ICMPV6_GET_TYPE(PacketGetICMPv6(p))) {
102 case ICMP6_ECHO_REPLY:
103 SCLogDebug("ICMPV6_GET_SEQ(p) %"PRIu16" (network byte order), "
104 "%"PRIu16" (host byte order)", ICMPV6_GET_SEQ(p),
106
107 seqn = ICMPV6_GET_SEQ(p);
108 break;
109 default:
110 SCLogDebug("Packet has no seq field");
111 return false;
112 }
113 } else {
114 SCLogDebug("Packet not ICMPV4 nor ICMPV6");
115 return false;
116 }
117
118 *seq = seqn;
119 return true;
120}
121
122/**
123 * \brief This function is used to match icmp_seq rule option set on a packet
124 *
125 * \param t pointer to thread vars
126 * \param det_ctx pointer to the pattern matcher thread
127 * \param p pointer to the current packet
128 * \param m pointer to the sigmatch that we will cast into DetectIcmpSeqData
129 *
130 * \retval 0 no match
131 * \retval 1 match
132 */
133static int DetectIcmpSeqMatch (DetectEngineThreadCtx *det_ctx, Packet *p,
134 const Signature *s, const SigMatchCtx *ctx)
135{
137 uint16_t seqn;
138
139 if (!GetIcmpSeq(p, &seqn))
140 return 0;
141
142 const DetectIcmpSeqData *iseq = (const DetectIcmpSeqData *)ctx;
143 if (seqn == iseq->seq)
144 return 1;
145
146 return 0;
147}
148
149/**
150 * \brief This function is used to parse icmp_seq option passed via icmp_seq: keyword
151 *
152 * \param de_ctx Pointer to the detection engine context
153 * \param icmpseqstr Pointer to the user provided icmp_seq options
154 *
155 * \retval iseq pointer to DetectIcmpSeqData on success
156 * \retval NULL on failure
157 */
158static DetectIcmpSeqData *DetectIcmpSeqParse (DetectEngineCtx *de_ctx, const char *icmpseqstr)
159{
160 DetectIcmpSeqData *iseq = NULL;
161 char *substr[3] = {NULL, NULL, NULL};
162 int res = 0;
163 size_t pcre2_len;
164 int i;
165 const char *str_ptr;
166
167 pcre2_match_data *match = NULL;
168 int ret = DetectParsePcreExec(&parse_regex, &match, icmpseqstr, 0, 0);
169 if (ret < 1 || ret > 4) {
170 SCLogError("Parse error %s", icmpseqstr);
171 goto error;
172 }
173
174 for (i = 1; i < ret; i++) {
175 res = SC_Pcre2SubstringGet(match, i, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
176 if (res < 0) {
177 SCLogError("pcre2_substring_get_bynumber failed");
178 goto error;
179 }
180 substr[i-1] = (char *)str_ptr;
181 }
182
183 iseq = SCMalloc(sizeof(DetectIcmpSeqData));
184 if (unlikely(iseq == NULL))
185 goto error;
186
187 iseq->seq = 0;
188
189 if (substr[0] != NULL && strlen(substr[0]) != 0) {
190 if (substr[2] == NULL) {
191 SCLogError("Missing quote in input");
192 goto error;
193 }
194 } else {
195 if (substr[2] != NULL) {
196 SCLogError("Missing quote in input");
197 goto error;
198 }
199 }
200
201 uint16_t seq = 0;
202 if (StringParseUint16(&seq, 10, 0, substr[1]) < 0) {
203 SCLogError("specified icmp seq %s is not "
204 "valid",
205 substr[1]);
206 goto error;
207 }
208 iseq->seq = htons(seq);
209
210 for (i = 0; i < 3; i++) {
211 if (substr[i] != NULL)
212 pcre2_substring_free((PCRE2_UCHAR8 *)substr[i]);
213 }
214
215 pcre2_match_data_free(match);
216 return iseq;
217
218error:
219 if (match) {
220 pcre2_match_data_free(match);
221 }
222 for (i = 0; i < 3; i++) {
223 if (substr[i] != NULL)
224 pcre2_substring_free((PCRE2_UCHAR8 *)substr[i]);
225 }
226 if (iseq != NULL) DetectIcmpSeqFree(de_ctx, iseq);
227 return NULL;
228
229}
230
231/**
232 * \brief this function is used to add the parsed icmp_seq data into the current signature
233 *
234 * \param de_ctx pointer to the Detection Engine Context
235 * \param s pointer to the Current Signature
236 * \param icmpseqstr pointer to the user provided icmp_seq option
237 *
238 * \retval 0 on Success
239 * \retval -1 on Failure
240 */
241static int DetectIcmpSeqSetup (DetectEngineCtx *de_ctx, Signature *s, const char *icmpseqstr)
242{
243 DetectIcmpSeqData *iseq = NULL;
244
245 iseq = DetectIcmpSeqParse(de_ctx, icmpseqstr);
246 if (iseq == NULL) goto error;
247
250 goto error;
251 }
253
254 return 0;
255
256error:
257 if (iseq != NULL)
259 return -1;
260
261}
262
263/**
264 * \brief this function will free memory associated with DetectIcmpSeqData
265 *
266 * \param ptr pointer to DetectIcmpSeqData
267 */
269{
271 SCFree(iseq);
272}
273
274/* prefilter code */
275
276static void
277PrefilterPacketIcmpSeqMatch(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
278{
280
281 const PrefilterPacketHeaderCtx *ctx = pectx;
282 uint16_t seqn;
283
284 if (!GetIcmpSeq(p, &seqn))
285 return;
286
287 if (seqn == ctx->v1.u16[0])
288 {
289 SCLogDebug("packet matches ICMP SEQ %u", ctx->v1.u16[0]);
290 PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt);
291 }
292}
293
294static void
295PrefilterPacketIcmpSeqSet(PrefilterPacketHeaderValue *v, void *smctx)
296{
297 const DetectIcmpSeqData *a = smctx;
298 v->u16[0] = a->seq;
299}
300
301static bool
302PrefilterPacketIcmpSeqCompare(PrefilterPacketHeaderValue v, void *smctx)
303{
304 const DetectIcmpSeqData *a = smctx;
305 if (v.u16[0] == a->seq)
306 return true;
307 return false;
308}
309
310static int PrefilterSetupIcmpSeq(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
311{
313 PrefilterPacketIcmpSeqSet, PrefilterPacketIcmpSeqCompare, PrefilterPacketIcmpSeqMatch);
314}
315
316static bool PrefilterIcmpSeqIsPrefilterable(const Signature *s)
317{
318 const SigMatch *sm;
319 for (sm = s->init_data->smlists[DETECT_SM_LIST_MATCH] ; sm != NULL; sm = sm->next) {
320 switch (sm->type) {
321 case DETECT_ICMP_SEQ:
322 return true;
323 }
324 }
325 return false;
326}
327
328#ifdef UNITTESTS
329#include "detect-engine.h"
330#include "detect-engine-mpm.h"
331#include "detect-engine-alert.h"
332
333/**
334 * \test DetectIcmpSeqParseTest01 is a test for setting a valid icmp_seq value
335 */
336static int DetectIcmpSeqParseTest01 (void)
337{
338 DetectIcmpSeqData *iseq = NULL;
339 iseq = DetectIcmpSeqParse(NULL, "300");
340 FAIL_IF_NULL(iseq);
341 FAIL_IF_NOT(htons(iseq->seq) == 300);
342 DetectIcmpSeqFree(NULL, iseq);
343 PASS;
344}
345
346/**
347 * \test DetectIcmpSeqParseTest02 is a test for setting a valid icmp_seq value
348 * with spaces all around
349 */
350static int DetectIcmpSeqParseTest02 (void)
351{
352 DetectIcmpSeqData *iseq = NULL;
353 iseq = DetectIcmpSeqParse(NULL, " 300 ");
354 FAIL_IF_NULL(iseq);
355 FAIL_IF_NOT(htons(iseq->seq) == 300);
356 DetectIcmpSeqFree(NULL, iseq);
357 PASS;
358}
359
360/**
361 * \test DetectIcmpSeqParseTest03 is a test for setting an invalid icmp_seq value
362 */
363static int DetectIcmpSeqParseTest03 (void)
364{
365 DetectIcmpSeqData *iseq = DetectIcmpSeqParse(NULL, "badc");
366 FAIL_IF_NOT_NULL(iseq);
367 PASS;
368}
369
370static void DetectIcmpSeqRegisterTests (void)
371{
372 UtRegisterTest("DetectIcmpSeqParseTest01", DetectIcmpSeqParseTest01);
373 UtRegisterTest("DetectIcmpSeqParseTest02", DetectIcmpSeqParseTest02);
374 UtRegisterTest("DetectIcmpSeqParseTest03", DetectIcmpSeqParseTest03);
375}
376#endif /* UNITTESTS */
#define ICMP_TIMESTAMPREPLY
#define ICMP_ADDRESSREPLY
#define ICMP_ADDRESS
#define ICMP_TIMESTAMP
#define ICMPV4_GET_SEQ(p)
#define ICMP_ECHOREPLY
#define ICMP_INFO_REPLY
#define ICMP_INFO_REQUEST
#define ICMP_ECHO
#define ICMP6_ECHO_REPLY
#define ICMPV6_GET_TYPE(icmp6h)
#define ICMPV6_GET_SEQ(p)
#define ICMP6_ECHO_REQUEST
#define PKT_IS_PSEUDOPKT(p)
return 1 if the packet is a pseudo packet
Definition decode.h:1321
int PrefilterSetupPacketHeader(DetectEngineCtx *de_ctx, SigGroupHead *sgh, int sm_type, SignatureMask mask, void(*Set)(PrefilterPacketHeaderValue *v, void *), bool(*Compare)(PrefilterPacketHeaderValue v, void *), void(*Match)(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx))
@ DETECT_ICMP_SEQ
void DetectIcmpSeqFree(DetectEngineCtx *, void *)
this function will free memory associated with DetectIcmpSeqData
void DetectIcmpSeqRegister(void)
Registration function for icmp_seq.
#define PARSE_REGEX
int SC_Pcre2SubstringGet(pcre2_match_data *match_data, uint32_t number, PCRE2_UCHAR **bufferptr, PCRE2_SIZE *bufflen)
void DetectSetupParseRegexes(const char *parse_str, DetectParseRegex *detect_parse)
int DetectParsePcreExec(DetectParseRegex *parse_regex, pcre2_match_data **match, const char *str, int start_offset, int options)
SigMatch * SCSigMatchAppendSMToList(DetectEngineCtx *de_ctx, Signature *s, uint16_t type, SigMatchCtx *ctx, const int list)
Append a SigMatch to the list type.
SigTableElmt * sigmatch_table
#define SIG_FLAG_REQUIRE_PACKET
Definition detect.h:254
#define SIG_MASK_REQUIRE_REAL_PKT
Definition detect.h:316
@ DETECT_SM_LIST_MATCH
Definition detect.h:117
DetectEngineCtx * de_ctx
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
#define PASS
Pass the test.
#define FAIL_IF_NOT_NULL(expr)
Fail a test if expression evaluates to non-NULL.
struct Thresholds ctx
uint32_t seq
main detection engine ctx
Definition detect.h:932
PrefilterRuleStore pmq
Definition detect.h:1349
struct Packet_::@33::@40 icmp_s
uint8_t type
Definition decode.h:511
Container for matching data for a signature group.
Definition detect.h:1629
Used to start a pointer to SigMatch context Should never be dereferenced without casting to something...
Definition detect.h:351
a single match condition for a signature
Definition detect.h:356
uint16_t type
Definition detect.h:357
struct SigMatch_ * next
Definition detect.h:360
const char * url
Definition detect.h:1462
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition detect.h:1441
int(* SetupPrefilter)(DetectEngineCtx *de_ctx, struct SigGroupHead_ *sgh)
Definition detect.h:1444
void(* Free)(DetectEngineCtx *, void *)
Definition detect.h:1446
const char * desc
Definition detect.h:1461
void(* RegisterTests)(void)
Definition detect.h:1448
int(* Match)(DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)
Definition detect.h:1421
const char * name
Definition detect.h:1459
bool(* SupportsPrefilter)(const Signature *s)
Definition detect.h:1443
struct SigMatch_ * smlists[DETECT_SM_LIST_MAX]
Definition detect.h:642
Signature container.
Definition detect.h:668
uint32_t flags
Definition detect.h:669
SignatureInitData * init_data
Definition detect.h:747
#define SCNtohs(x)
int StringParseUint16(uint16_t *res, int base, size_t len, const char *str)
Definition util-byte.c:337
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267
#define SCMalloc(sz)
Definition util-mem.h:47
#define SCFree(p)
Definition util-mem.h:61
#define unlikely(expr)
#define DEBUG_VALIDATE_BUG_ON(exp)