suricata
detect-engine-proto.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 Victor Julien <victor@inliniac.net>
22 *
23 * Proto part of the detection engine.
24 *
25 * \todo move this out of the detection plugin structure
26 */
27
28#include "suricata-common.h"
29
30#include "decode.h"
31#include "detect.h"
32
33#include "app-layer-parser.h"
34
35#include "flow-util.h"
36#include "flow-var.h"
37
39#include "detect-engine-state.h"
40
41#include "util-cidr.h"
42#include "util-byte.h"
43#include "util-unittest.h"
45#include "util-debug.h"
46
47/**
48 * \brief Parses a protocol sent as a string.
49 *
50 * \param dp Pointer to the DetectProto instance which will be updated with the
51 * incoming protocol information.
52 * \param str Pointer to the string containing the protocol name.
53 *
54 * \retval >=0 If proto is detected, -1 otherwise.
55 */
56int DetectProtoParse(DetectProto *dp, const char *str)
57{
58 if (strcasecmp(str, "tcp") == 0) {
59 dp->proto[IPPROTO_TCP / 8] |= 1 << (IPPROTO_TCP % 8);
60 SCLogDebug("TCP protocol detected");
61 } else if (strcasecmp(str, "tcp-pkt") == 0) {
62 dp->proto[IPPROTO_TCP / 8] |= 1 << (IPPROTO_TCP % 8);
63 SCLogDebug("TCP protocol detected, packets only");
65 } else if (strcasecmp(str, "tcp-stream") == 0) {
66 dp->proto[IPPROTO_TCP / 8] |= 1 << (IPPROTO_TCP % 8);
67 SCLogDebug("TCP protocol detected, stream only");
69 } else if (strcasecmp(str, "udp") == 0) {
70 dp->proto[IPPROTO_UDP / 8] |= 1 << (IPPROTO_UDP % 8);
71 SCLogDebug("UDP protocol detected");
72 } else if (strcasecmp(str, "icmpv4") == 0) {
73 dp->proto[IPPROTO_ICMP / 8] |= 1 << (IPPROTO_ICMP % 8);
74 SCLogDebug("ICMPv4 protocol detected");
75 } else if (strcasecmp(str, "icmpv6") == 0) {
76 dp->proto[IPPROTO_ICMPV6 / 8] |= 1 << (IPPROTO_ICMPV6 % 8);
77 SCLogDebug("ICMPv6 protocol detected");
78 } else if (strcasecmp(str, "icmp") == 0) {
79 dp->proto[IPPROTO_ICMP / 8] |= 1 << (IPPROTO_ICMP % 8);
80 dp->proto[IPPROTO_ICMPV6 / 8] |= 1 << (IPPROTO_ICMPV6 % 8);
81 SCLogDebug("ICMP protocol detected, sig applies both to ICMPv4 and ICMPv6");
82 } else if (strcasecmp(str, "sctp") == 0) {
83 dp->proto[IPPROTO_SCTP / 8] |= 1 << (IPPROTO_SCTP % 8);
84 SCLogDebug("SCTP protocol detected");
85 } else if (strcasecmp(str,"ipv4") == 0 ||
86 strcasecmp(str,"ip4") == 0 ) {
88 memset(dp->proto, 0xff, sizeof(dp->proto));
89 SCLogDebug("IPv4 protocol detected");
90 } else if (strcasecmp(str,"ipv6") == 0 ||
91 strcasecmp(str,"ip6") == 0 ) {
93 memset(dp->proto, 0xff, sizeof(dp->proto));
94 SCLogDebug("IPv6 protocol detected");
95 } else if (strcasecmp(str,"ip") == 0 ||
96 strcasecmp(str,"pkthdr") == 0) {
97 /* Proto "ip" is treated as an "any" */
99 memset(dp->proto, 0xff, sizeof(dp->proto));
100 SCLogDebug("IP protocol detected");
101 } else {
102 goto error;
103
104 /** \todo are numeric protocols even valid? */
105#if 0
106 uint8_t proto_u8; /* Used to avoid sign extension */
107
108 /* Extract out a 0-256 value with validation checks */
109 if (ByteExtractStringUint8(&proto_u8, 10, 0, str) == -1) {
110 // XXX
111 SCLogDebug("DetectProtoParse: Error in extracting byte string");
112 goto error;
113 }
114 proto = (int)proto_u8;
115
116 /* Proto 0 is the same as "ip" above */
117 if (proto == IPPROTO_IP) {
118 dp->flags |= DETECT_PROTO_ANY;
119 } else {
120 dp->proto[proto / 8] |= 1<<(proto % 8);
121 }
122#endif
123 }
124
125 return 0;
126error:
127 return -1;
128}
129
130/** \brief see if a DetectProto contains a certain proto
131 * \param dp detect proto to inspect
132 * \param proto protocol (such as IPPROTO_TCP) to look for
133 * \retval 0 protocol not in the set
134 * \retval 1 protocol is in the set */
136{
137 if (dp->flags & DETECT_PROTO_ANY)
138 return 1;
139
140 if (dp->proto[proto / 8] & (1<<(proto % 8)))
141 return 1;
142
143 return 0;
144}
145
146/* TESTS */
147
148#ifdef UNITTESTS
149#include "detect-engine.h"
150#include "detect-parse.h"
151#include "detect-engine-mpm.h"
152/**
153 * \brief this function is used to initialize the detection engine context and
154 * setup the signature with passed values.
155 */
156static int DetectProtoInitTest(DetectEngineCtx **de_ctx, Signature **sig,
157 DetectProto *dp, const char *str)
158{
159 char fullstr[1024];
160 int result = 0;
161
162 *de_ctx = NULL;
163 *sig = NULL;
164
165 if (snprintf(fullstr, 1024, "alert %s any any -> any any (msg:\"DetectProto"
166 " test\"; sid:1;)", str) >= 1024)
167 {
168 goto end;
169 }
170
172 if (*de_ctx == NULL) {
173 goto end;
174 }
175
176 (*de_ctx)->flags |= DE_QUIET;
177
178 (*de_ctx)->sig_list = SigInit(*de_ctx, fullstr);
179 if ((*de_ctx)->sig_list == NULL) {
180 goto end;
181 }
182
183 *sig = (*de_ctx)->sig_list;
184
185 if (DetectProtoParse(dp, str) < 0)
186 goto end;
187
188 result = 1;
189
190end:
191 return result;
192}
193
194/**
195 * \test ProtoTestParse01 is a test to make sure that we parse the
196 * protocol correctly, when given valid proto option.
197 */
198static int ProtoTestParse01 (void)
199{
200 DetectProto dp;
201 memset(&dp,0,sizeof(DetectProto));
202
203 int r = DetectProtoParse(&dp, "6");
204
205 FAIL_IF_NOT(r < 0);
206
207 PASS;
208}
209/**
210 * \test ProtoTestParse02 is a test to make sure that we parse the
211 * protocol correctly, when given "tcp" as proto option.
212 */
213static int ProtoTestParse02 (void)
214{
215 DetectProto dp;
216 memset(&dp,0,sizeof(DetectProto));
217
218 int r = DetectProtoParse(&dp, "tcp");
219
220 FAIL_IF_NOT(r >= 0);
221 FAIL_IF_NOT(dp.proto[(IPPROTO_TCP / 8)] & (1 << (IPPROTO_TCP % 8)));
222
223 PASS;
224}
225/**
226 * \test ProtoTestParse03 is a test to make sure that we parse the
227 * protocol correctly, when given "ip" as proto option.
228 */
229static int ProtoTestParse03 (void)
230{
231 DetectProto dp;
232 memset(&dp,0,sizeof(DetectProto));
233
234 int r = DetectProtoParse(&dp, "ip");
235
236 FAIL_IF_NOT(r >= 0);
238
239 PASS;
240}
241
242/**
243 * \test ProtoTestParse04 is a test to make sure that we do not parse the
244 * protocol, when given an invalid proto option.
245 */
246static int ProtoTestParse04 (void)
247{
248 DetectProto dp;
249 memset(&dp,0,sizeof(DetectProto));
250
251 /* Check for a bad number */
252 int r = DetectProtoParse(&dp, "4242");
253
254 FAIL_IF_NOT(r < 0);
255
256 PASS;
257}
258
259/**
260 * \test ProtoTestParse05 is a test to make sure that we do not parse the
261 * protocol, when given an invalid proto option.
262 */
263static int ProtoTestParse05 (void)
264{
265 DetectProto dp;
266 memset(&dp,0,sizeof(DetectProto));
267
268 /* Check for a bad string */
269 int r = DetectProtoParse(&dp, "tcp/udp");
270
271 FAIL_IF_NOT(r < 0);
272
273 PASS;
274}
275
276/**
277 * \test make sure that we properly parse tcp-pkt
278 */
279static int ProtoTestParse06 (void)
280{
281 DetectProto dp;
282 memset(&dp,0,sizeof(DetectProto));
283
284 /* Check for a bad string */
285 int r = DetectProtoParse(&dp, "tcp-pkt");
286
287 FAIL_IF(r < 0);
289
290 PASS;
291}
292
293/**
294 * \test make sure that we properly parse tcp-stream
295 */
296static int ProtoTestParse07 (void)
297{
298 DetectProto dp;
299 memset(&dp,0,sizeof(DetectProto));
300
301 /* Check for a bad string */
302 int r = DetectProtoParse(&dp, "tcp-stream");
303
304 FAIL_IF(r < 0);
306
307 PASS;
308}
309
310/**
311 * \test DetectIPProtoTestSetup01 is a test for a protocol setting up in
312 * signature.
313 */
314static int DetectProtoTestSetup01(void)
315{
316 DetectProto dp;
317 Signature *sig = NULL;
318 DetectEngineCtx *de_ctx = NULL;
319 int i;
320
321 memset(&dp, 0, sizeof(dp));
322
323 FAIL_IF_NOT(DetectProtoInitTest(&de_ctx, &sig, &dp, "tcp"));
324
325 /* The signature proto should be TCP */
326 FAIL_IF_NOT(sig->proto.proto[(IPPROTO_TCP / 8)] & (1 << (IPPROTO_TCP % 8)));
327
328 for (i = 2; i < 256 / 8; i++) {
329 FAIL_IF(sig->proto.proto[i] != 0);
330 }
331
333
334 PASS;
335}
336
337/**
338 * \test DetectProtoTestSetup02 is a test for a icmpv4 and icmpv6
339 * protocol setting up in signature.
340 */
341static int DetectProtoTestSetup02(void)
342{
343 DetectProto dp;
344 Signature *sig_icmpv4 = NULL;
345 Signature *sig_icmpv6 = NULL;
346 Signature *sig_icmp = NULL;
347 DetectEngineCtx *de_ctx = NULL;
348
349 memset(&dp, 0, sizeof(dp));
350
351 FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmpv4, &dp, "icmpv4") == 0);
352 FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmpv6, &dp, "icmpv6") == 0);
353 FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmp, &dp, "icmp") == 0);
354
355 FAIL_IF_NOT(sig_icmpv4->proto.proto[IPPROTO_ICMP / 8] & (1 << (IPPROTO_ICMP % 8)));
356 FAIL_IF_NOT(sig_icmpv6->proto.proto[IPPROTO_ICMPV6 / 8] & (1 << (IPPROTO_ICMPV6 % 8)));
357
358 FAIL_IF_NOT(sig_icmp->proto.proto[IPPROTO_ICMP / 8] & (1 << (IPPROTO_ICMP % 8)));
359 FAIL_IF_NOT(sig_icmp->proto.proto[IPPROTO_ICMPV6 / 8] & (1 << (IPPROTO_ICMPV6 % 8)));
360
362
363 PASS;
364}
365
366/**
367 * \test signature parsing with tcp-pkt and tcp-stream
368 */
369
370static int DetectProtoTestSig01(void)
371{
374
376
378 de_ctx, "alert tcp-pkt any any -> any any (msg:\"tcp-pkt\"; content:\"blah\"; sid:1;)");
379 FAIL_IF_NULL(s);
380
382 "alert tcp-stream any any -> any any (msg:\"tcp-stream\"; content:\"blah\"; sid:2;)");
383 FAIL_IF_NULL(s);
384
386
387 PASS;
388}
389#endif /* UNITTESTS */
390
391/**
392 * \brief this function registers unit tests for DetectProto
393 */
395{
396#ifdef UNITTESTS
397 UtRegisterTest("ProtoTestParse01", ProtoTestParse01);
398 UtRegisterTest("ProtoTestParse02", ProtoTestParse02);
399 UtRegisterTest("ProtoTestParse03", ProtoTestParse03);
400 UtRegisterTest("ProtoTestParse04", ProtoTestParse04);
401 UtRegisterTest("ProtoTestParse05", ProtoTestParse05);
402 UtRegisterTest("ProtoTestParse06", ProtoTestParse06);
403 UtRegisterTest("ProtoTestParse07", ProtoTestParse07);
404
405 UtRegisterTest("DetectProtoTestSetup01", DetectProtoTestSetup01);
406 UtRegisterTest("DetectProtoTestSetup02", DetectProtoTestSetup02);
407
408 UtRegisterTest("DetectProtoTestSig01", DetectProtoTestSig01);
409#endif /* UNITTESTS */
410}
411
uint8_t proto
#define IPPROTO_SCTP
Definition decode.h:1228
void DetectProtoTests(void)
this function registers unit tests for DetectProto
int DetectProtoParse(DetectProto *dp, const char *str)
Parses a protocol sent as a string.
int DetectProtoContainsProto(const DetectProto *dp, int proto)
see if a DetectProto contains a certain proto
#define DETECT_PROTO_IPV6
#define DETECT_PROTO_IPV4
#define DETECT_PROTO_ONLY_PKT
#define DETECT_PROTO_ONLY_STREAM
#define DETECT_PROTO_ANY
DetectEngineCtx * DetectEngineCtxInit(void)
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
Signature * DetectEngineAppendSig(DetectEngineCtx *, const char *)
Parse and append a Signature into the Detection Engine Context signature list.
Data structures and function prototypes for keeping state for the detection engine.
Signature * SigInit(DetectEngineCtx *de_ctx, const char *sigstr)
Parses a signature and adds it to the Detection Engine Context.
#define DE_QUIET
Definition detect.h:330
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(expr)
Fail a test if expression evaluates to true.
main detection engine ctx
Definition detect.h:932
uint8_t flags
Definition detect.h:934
uint8_t proto[256/8]
Signature container.
Definition detect.h:668
DetectProto proto
Definition detect.h:687
#define str(s)
int ByteExtractStringUint8(uint8_t *res, int base, size_t len, const char *str)
Definition util-byte.c:285
#define SCLogDebug(...)
Definition util-debug.h:275