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