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 
38 #include "detect-engine-siggroup.h"
39 #include "detect-engine-state.h"
40 
41 #include "util-cidr.h"
42 #include "util-byte.h"
43 #include "util-unittest.h"
44 #include "util-unittest-helper.h"
45 #include "util-debug.h"
46 
47 struct {
48  const char *name;
49  uint8_t proto;
50  uint8_t proto2;
51  uint8_t flags;
52 } proto_table[] = {
53  { "tcp", IPPROTO_TCP, 0, 0 },
54  { "tcp-pkt", IPPROTO_TCP, 0, DETECT_PROTO_ONLY_PKT },
55  { "tcp-stream", IPPROTO_TCP, 0, DETECT_PROTO_ONLY_STREAM },
56  { "udp", IPPROTO_UDP, 0, 0 },
57  { "icmpv4", IPPROTO_ICMP, 0, 0 },
58  { "icmpv6", IPPROTO_ICMPV6, 0, 0 },
59  { "icmp", IPPROTO_ICMP, IPPROTO_ICMPV6, 0 },
60  { "sctp", IPPROTO_SCTP, 0, 0 },
61  { "ipv4", 0, 0, DETECT_PROTO_IPV4 | DETECT_PROTO_ANY },
62  { "ip4", 0, 0, DETECT_PROTO_IPV4 | DETECT_PROTO_ANY },
63  { "ipv6", 0, 0, DETECT_PROTO_IPV6 | DETECT_PROTO_ANY },
64  { "ip6", 0, 0, DETECT_PROTO_IPV6 | DETECT_PROTO_ANY },
65  { "ip", 0, 0, DETECT_PROTO_ANY },
66  { "pkthdr", 0, 0, DETECT_PROTO_ANY },
67 };
68 
70 {
71  for (size_t i = 0; i < ARRAY_SIZE(proto_table); i++) {
72  printf("%s\n", proto_table[i].name);
73  }
74 }
75 
76 /**
77  * \brief Parses a protocol sent as a string.
78  *
79  * \param dp Pointer to the DetectProto instance which will be updated with the
80  * incoming protocol information.
81  * \param str Pointer to the string containing the protocol name.
82  *
83  * \retval >=0 If proto is detected, -1 otherwise.
84  */
85 int DetectProtoParse(DetectProto *dp, const char *str)
86 {
87  int found = -1;
88  for (size_t i = 0; i < ARRAY_SIZE(proto_table); i++) {
89  if (strcasecmp(str, proto_table[i].name) == 0) {
90  if (proto_table[i].proto != 0)
91  dp->proto[proto_table[i].proto / 8] |= 1 << (proto_table[i].proto % 8);
92  if (proto_table[i].proto2 != 0)
93  dp->proto[proto_table[i].proto2 / 8] |= 1 << (proto_table[i].proto2 % 8);
94  dp->flags |= proto_table[i].flags;
96  memset(dp->proto, 0xff, sizeof(dp->proto));
97  found = 0;
98  break;
99  }
100  }
101  return found;
102 }
103 
104 /** \brief see if a DetectProto contains a certain proto
105  * \param dp detect proto to inspect
106  * \param proto protocol (such as IPPROTO_TCP) to look for
107  * \retval 0 protocol not in the set
108  * \retval 1 protocol is in the set */
110 {
111  if (dp->flags & DETECT_PROTO_ANY)
112  return 1;
113 
114  if (dp->proto[proto / 8] & (1<<(proto % 8)))
115  return 1;
116 
117  return 0;
118 }
119 
120 /* TESTS */
121 
122 #ifdef UNITTESTS
123 #include "detect-engine.h"
124 #include "detect-parse.h"
125 #include "detect-engine-mpm.h"
126 
127 /**
128  * \brief this function is used to initialize the detection engine context and
129  * setup the signature with passed values.
130  */
131 static int DetectProtoInitTest(DetectEngineCtx **de_ctx, Signature **sig,
132  DetectProto *dp, const char *str)
133 {
134  char fullstr[1024];
135  int result = 0;
136  static uint32_t test_sid = 1;
137 
138  *sig = NULL;
139 
140  if (snprintf(fullstr, 1024,
141  "alert %s any any -> any any (msg:\"DetectProto"
142  " test\"; sid:%u;)",
143  str, test_sid++) >= 1024) {
144  goto end;
145  }
146 
147  if (*de_ctx == NULL) {
149  if (*de_ctx == NULL) {
150  goto end;
151  }
152 
153  (*de_ctx)->flags |= DE_QUIET;
154  }
155 
156  Signature *s = DetectEngineAppendSig(*de_ctx, fullstr);
157  if (s == NULL) {
158  goto end;
159  }
160  *sig = s;
161 
162  result = 1;
163 
164 end:
165  return result;
166 }
167 
168 /**
169  * \test ProtoTestParse01 is a test to make sure that we parse the
170  * protocol correctly, when given valid proto option.
171  */
172 static int ProtoTestParse01 (void)
173 {
174  DetectProto dp;
175  memset(&dp,0,sizeof(DetectProto));
176 
177  int r = DetectProtoParse(&dp, "6");
178 
179  FAIL_IF_NOT(r < 0);
180 
181  PASS;
182 }
183 /**
184  * \test ProtoTestParse02 is a test to make sure that we parse the
185  * protocol correctly, when given "tcp" as proto option.
186  */
187 static int ProtoTestParse02 (void)
188 {
189  DetectProto dp;
190  memset(&dp,0,sizeof(DetectProto));
191 
192  int r = DetectProtoParse(&dp, "tcp");
193 
194  FAIL_IF_NOT(r >= 0);
195  FAIL_IF_NOT(dp.proto[(IPPROTO_TCP / 8)] & (1 << (IPPROTO_TCP % 8)));
196 
197  PASS;
198 }
199 /**
200  * \test ProtoTestParse03 is a test to make sure that we parse the
201  * protocol correctly, when given "ip" as proto option.
202  */
203 static int ProtoTestParse03 (void)
204 {
205  DetectProto dp;
206  memset(&dp,0,sizeof(DetectProto));
207 
208  int r = DetectProtoParse(&dp, "ip");
209 
210  FAIL_IF_NOT(r >= 0);
212 
213  PASS;
214 }
215 
216 /**
217  * \test ProtoTestParse04 is a test to make sure that we do not parse the
218  * protocol, when given an invalid proto option.
219  */
220 static int ProtoTestParse04 (void)
221 {
222  DetectProto dp;
223  memset(&dp,0,sizeof(DetectProto));
224 
225  /* Check for a bad number */
226  int r = DetectProtoParse(&dp, "4242");
227 
228  FAIL_IF_NOT(r < 0);
229 
230  PASS;
231 }
232 
233 /**
234  * \test ProtoTestParse05 is a test to make sure that we do not parse the
235  * protocol, when given an invalid proto option.
236  */
237 static int ProtoTestParse05 (void)
238 {
239  DetectProto dp;
240  memset(&dp,0,sizeof(DetectProto));
241 
242  /* Check for a bad string */
243  int r = DetectProtoParse(&dp, "tcp/udp");
244 
245  FAIL_IF_NOT(r < 0);
246 
247  PASS;
248 }
249 
250 /**
251  * \test make sure that we properly parse tcp-pkt
252  */
253 static int ProtoTestParse06 (void)
254 {
255  DetectProto dp;
256  memset(&dp,0,sizeof(DetectProto));
257 
258  /* Check for a bad string */
259  int r = DetectProtoParse(&dp, "tcp-pkt");
260 
261  FAIL_IF(r < 0);
263 
264  PASS;
265 }
266 
267 /**
268  * \test make sure that we properly parse tcp-stream
269  */
270 static int ProtoTestParse07 (void)
271 {
272  DetectProto dp;
273  memset(&dp,0,sizeof(DetectProto));
274 
275  /* Check for a bad string */
276  int r = DetectProtoParse(&dp, "tcp-stream");
277 
278  FAIL_IF(r < 0);
280 
281  PASS;
282 }
283 
284 /**
285  * \test DetectIPProtoTestSetup01 is a test for a protocol setting up in
286  * signature.
287  */
288 static int DetectProtoTestSetup01(void)
289 {
290  DetectProto dp;
291  Signature *sig = NULL;
292  DetectEngineCtx *de_ctx = NULL;
293  int i;
294 
295  memset(&dp, 0, sizeof(dp));
296 
297  FAIL_IF_NOT(DetectProtoInitTest(&de_ctx, &sig, &dp, "tcp"));
298 
299  /* The signature proto should be TCP */
300  FAIL_IF_NOT(sig->proto.proto[(IPPROTO_TCP / 8)] & (1 << (IPPROTO_TCP % 8)));
301 
302  for (i = 2; i < 256 / 8; i++) {
303  FAIL_IF(sig->proto.proto[i] != 0);
304  }
305 
307 
308  PASS;
309 }
310 
311 /**
312  * \test DetectProtoTestSetup02 is a test for a icmpv4 and icmpv6
313  * protocol setting up in signature.
314  */
315 static int DetectProtoTestSetup02(void)
316 {
317  DetectProto dp;
318  Signature *sig_icmpv4 = NULL;
319  Signature *sig_icmpv6 = NULL;
320  Signature *sig_icmp = NULL;
321  DetectEngineCtx *de_ctx = NULL;
322 
323  memset(&dp, 0, sizeof(dp));
324 
325  FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmpv4, &dp, "icmpv4") == 0);
326  FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmpv6, &dp, "icmpv6") == 0);
327  FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmp, &dp, "icmp") == 0);
328 
329  FAIL_IF_NOT(sig_icmpv4->proto.proto[IPPROTO_ICMP / 8] & (1 << (IPPROTO_ICMP % 8)));
330  FAIL_IF_NOT(sig_icmpv6->proto.proto[IPPROTO_ICMPV6 / 8] & (1 << (IPPROTO_ICMPV6 % 8)));
331 
332  FAIL_IF_NOT(sig_icmp->proto.proto[IPPROTO_ICMP / 8] & (1 << (IPPROTO_ICMP % 8)));
333  FAIL_IF_NOT(sig_icmp->proto.proto[IPPROTO_ICMPV6 / 8] & (1 << (IPPROTO_ICMPV6 % 8)));
334 
336 
337  PASS;
338 }
339 
340 /**
341  * \test signature parsing with tcp-pkt and tcp-stream
342  */
343 
344 static int DetectProtoTestSig01(void)
345 {
348 
349  de_ctx->flags |= DE_QUIET;
350 
352  de_ctx, "alert tcp-pkt any any -> any any (msg:\"tcp-pkt\"; content:\"blah\"; sid:1;)");
353  FAIL_IF_NULL(s);
354 
356  "alert tcp-stream any any -> any any (msg:\"tcp-stream\"; content:\"blah\"; sid:2;)");
357  FAIL_IF_NULL(s);
358 
360 
361  PASS;
362 }
363 #endif /* UNITTESTS */
364 
365 /**
366  * \brief this function registers unit tests for DetectProto
367  */
369 {
370 #ifdef UNITTESTS
371  UtRegisterTest("ProtoTestParse01", ProtoTestParse01);
372  UtRegisterTest("ProtoTestParse02", ProtoTestParse02);
373  UtRegisterTest("ProtoTestParse03", ProtoTestParse03);
374  UtRegisterTest("ProtoTestParse04", ProtoTestParse04);
375  UtRegisterTest("ProtoTestParse05", ProtoTestParse05);
376  UtRegisterTest("ProtoTestParse06", ProtoTestParse06);
377  UtRegisterTest("ProtoTestParse07", ProtoTestParse07);
378 
379  UtRegisterTest("DetectProtoTestSetup01", DetectProtoTestSetup01);
380  UtRegisterTest("DetectProtoTestSetup02", DetectProtoTestSetup02);
381 
382  UtRegisterTest("DetectProtoTestSig01", DetectProtoTestSig01);
383 #endif /* UNITTESTS */
384 }
385 
util-byte.h
detect-engine.h
FAIL_IF_NULL
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
Definition: util-unittest.h:89
DetectEngineProtoList
void DetectEngineProtoList(void)
Definition: detect-engine-proto.c:69
flow-util.h
DETECT_PROTO_IPV6
#define DETECT_PROTO_IPV6
Definition: detect-engine-proto.h:32
detect-engine-siggroup.h
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
name
const char * name
Definition: detect-engine-proto.c:48
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:933
DETECT_PROTO_ANY
#define DETECT_PROTO_ANY
Definition: detect-engine-proto.h:28
proto
uint8_t proto
Definition: detect-engine-proto.c:49
DetectEngineCtxFree
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
Definition: detect-engine.c:2634
DE_QUIET
#define DE_QUIET
Definition: detect.h:330
DetectEngineAppendSig
Signature * DetectEngineAppendSig(DetectEngineCtx *, const char *)
Parse and append a Signature into the Detection Engine Context signature list.
Definition: detect-parse.c:3447
util-unittest.h
util-unittest-helper.h
FAIL_IF_NOT
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
Definition: util-unittest.h:82
proto_table
struct @61 proto_table[]
util-cidr.h
decode.h
util-debug.h
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:19
DetectProtoParse
int DetectProtoParse(DetectProto *dp, const char *str)
Parses a protocol sent as a string.
Definition: detect-engine-proto.c:85
proto2
uint8_t proto2
Definition: detect-engine-proto.c:50
detect-engine-mpm.h
detect.h
app-layer-parser.h
DetectProto_::proto
uint8_t proto[256/8]
Definition: detect-engine-proto.h:36
detect-engine-state.h
Data structures and function prototypes for keeping state for the detection engine.
ARRAY_SIZE
#define ARRAY_SIZE(arr)
Definition: suricata-common.h:562
DetectProto_::flags
uint8_t flags
Definition: detect-engine-proto.h:37
DETECT_PROTO_IPV4
#define DETECT_PROTO_IPV4
Definition: detect-engine-proto.h:31
FAIL_IF
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
Definition: util-unittest.h:71
Signature_::proto
DetectProto proto
Definition: detect.h:687
suricata-common.h
DETECT_PROTO_ONLY_STREAM
#define DETECT_PROTO_ONLY_STREAM
Definition: detect-engine-proto.h:30
DetectProto_
Definition: detect-engine-proto.h:35
str
#define str(s)
Definition: suricata-common.h:308
detect-parse.h
Signature_
Signature container.
Definition: detect.h:668
DETECT_PROTO_ONLY_PKT
#define DETECT_PROTO_ONLY_PKT
Definition: detect-engine-proto.h:29
flags
uint8_t flags
Definition: detect-engine-proto.c:51
DetectEngineCtxInit
DetectEngineCtx * DetectEngineCtxInit(void)
Definition: detect-engine.c:2595
IPPROTO_SCTP
#define IPPROTO_SCTP
Definition: decode.h:1230
DetectEngineCtx_::flags
uint8_t flags
Definition: detect.h:935
flow-var.h
DetectProtoContainsProto
int DetectProtoContainsProto(const DetectProto *dp, int proto)
see if a DetectProto contains a certain proto
Definition: detect-engine-proto.c:109
DetectProtoTests
void DetectProtoTests(void)
this function registers unit tests for DetectProto
Definition: detect-engine-proto.c:368