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 /**
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  */
56 int 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" */
98  dp->flags |= DETECT_PROTO_ANY;
99  memset(dp->proto, 0xff, sizeof(dp->proto));
100  SCLogDebug("IP protocol detected");
101  } else {
102  goto error;
103  }
104 
105  return 0;
106 error:
107  return -1;
108 }
109 
110 /** \brief see if a DetectProto contains a certain proto
111  * \param dp detect proto to inspect
112  * \param proto protocol (such as IPPROTO_TCP) to look for
113  * \retval 0 protocol not in the set
114  * \retval 1 protocol is in the set */
116 {
117  if (dp->flags & DETECT_PROTO_ANY)
118  return 1;
119 
120  if (dp->proto[proto / 8] & (1<<(proto % 8)))
121  return 1;
122 
123  return 0;
124 }
125 
126 /* TESTS */
127 
128 #ifdef UNITTESTS
129 #include "detect-engine.h"
130 #include "detect-parse.h"
131 #include "detect-engine-mpm.h"
132 
133 /**
134  * \brief this function is used to initialize the detection engine context and
135  * setup the signature with passed values.
136  */
137 static int DetectProtoInitTest(DetectEngineCtx **de_ctx, Signature **sig,
138  DetectProto *dp, const char *str)
139 {
140  char fullstr[1024];
141  int result = 0;
142  static uint32_t test_sid = 1;
143 
144  *sig = NULL;
145 
146  if (snprintf(fullstr, 1024,
147  "alert %s any any -> any any (msg:\"DetectProto"
148  " test\"; sid:%u;)",
149  str, test_sid++) >= 1024) {
150  goto end;
151  }
152 
153  if (*de_ctx == NULL) {
155  if (*de_ctx == NULL) {
156  goto end;
157  }
158 
159  (*de_ctx)->flags |= DE_QUIET;
160  }
161 
162  Signature *s = DetectEngineAppendSig(*de_ctx, fullstr);
163  if (s == NULL) {
164  goto end;
165  }
166  *sig = s;
167 
168  result = 1;
169 
170 end:
171  return result;
172 }
173 
174 /**
175  * \test ProtoTestParse01 is a test to make sure that we parse the
176  * protocol correctly, when given valid proto option.
177  */
178 static int ProtoTestParse01 (void)
179 {
180  DetectProto dp;
181  memset(&dp,0,sizeof(DetectProto));
182 
183  int r = DetectProtoParse(&dp, "6");
184 
185  FAIL_IF_NOT(r < 0);
186 
187  PASS;
188 }
189 /**
190  * \test ProtoTestParse02 is a test to make sure that we parse the
191  * protocol correctly, when given "tcp" as proto option.
192  */
193 static int ProtoTestParse02 (void)
194 {
195  DetectProto dp;
196  memset(&dp,0,sizeof(DetectProto));
197 
198  int r = DetectProtoParse(&dp, "tcp");
199 
200  FAIL_IF_NOT(r >= 0);
201  FAIL_IF_NOT(dp.proto[(IPPROTO_TCP / 8)] & (1 << (IPPROTO_TCP % 8)));
202 
203  PASS;
204 }
205 /**
206  * \test ProtoTestParse03 is a test to make sure that we parse the
207  * protocol correctly, when given "ip" as proto option.
208  */
209 static int ProtoTestParse03 (void)
210 {
211  DetectProto dp;
212  memset(&dp,0,sizeof(DetectProto));
213 
214  int r = DetectProtoParse(&dp, "ip");
215 
216  FAIL_IF_NOT(r >= 0);
218 
219  PASS;
220 }
221 
222 /**
223  * \test ProtoTestParse04 is a test to make sure that we do not parse the
224  * protocol, when given an invalid proto option.
225  */
226 static int ProtoTestParse04 (void)
227 {
228  DetectProto dp;
229  memset(&dp,0,sizeof(DetectProto));
230 
231  /* Check for a bad number */
232  int r = DetectProtoParse(&dp, "4242");
233 
234  FAIL_IF_NOT(r < 0);
235 
236  PASS;
237 }
238 
239 /**
240  * \test ProtoTestParse05 is a test to make sure that we do not parse the
241  * protocol, when given an invalid proto option.
242  */
243 static int ProtoTestParse05 (void)
244 {
245  DetectProto dp;
246  memset(&dp,0,sizeof(DetectProto));
247 
248  /* Check for a bad string */
249  int r = DetectProtoParse(&dp, "tcp/udp");
250 
251  FAIL_IF_NOT(r < 0);
252 
253  PASS;
254 }
255 
256 /**
257  * \test make sure that we properly parse tcp-pkt
258  */
259 static int ProtoTestParse06 (void)
260 {
261  DetectProto dp;
262  memset(&dp,0,sizeof(DetectProto));
263 
264  /* Check for a bad string */
265  int r = DetectProtoParse(&dp, "tcp-pkt");
266 
267  FAIL_IF(r < 0);
269 
270  PASS;
271 }
272 
273 /**
274  * \test make sure that we properly parse tcp-stream
275  */
276 static int ProtoTestParse07 (void)
277 {
278  DetectProto dp;
279  memset(&dp,0,sizeof(DetectProto));
280 
281  /* Check for a bad string */
282  int r = DetectProtoParse(&dp, "tcp-stream");
283 
284  FAIL_IF(r < 0);
286 
287  PASS;
288 }
289 
290 /**
291  * \test DetectIPProtoTestSetup01 is a test for a protocol setting up in
292  * signature.
293  */
294 static int DetectProtoTestSetup01(void)
295 {
296  DetectProto dp;
297  Signature *sig = NULL;
298  DetectEngineCtx *de_ctx = NULL;
299  int i;
300 
301  memset(&dp, 0, sizeof(dp));
302 
303  FAIL_IF_NOT(DetectProtoInitTest(&de_ctx, &sig, &dp, "tcp"));
304 
305  /* The signature proto should be TCP */
306  FAIL_IF_NOT(sig->proto.proto[(IPPROTO_TCP / 8)] & (1 << (IPPROTO_TCP % 8)));
307 
308  for (i = 2; i < 256 / 8; i++) {
309  FAIL_IF(sig->proto.proto[i] != 0);
310  }
311 
313 
314  PASS;
315 }
316 
317 /**
318  * \test DetectProtoTestSetup02 is a test for a icmpv4 and icmpv6
319  * protocol setting up in signature.
320  */
321 static int DetectProtoTestSetup02(void)
322 {
323  DetectProto dp;
324  Signature *sig_icmpv4 = NULL;
325  Signature *sig_icmpv6 = NULL;
326  Signature *sig_icmp = NULL;
327  DetectEngineCtx *de_ctx = NULL;
328 
329  memset(&dp, 0, sizeof(dp));
330 
331  FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmpv4, &dp, "icmpv4") == 0);
332  FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmpv6, &dp, "icmpv6") == 0);
333  FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmp, &dp, "icmp") == 0);
334 
335  FAIL_IF_NOT(sig_icmpv4->proto.proto[IPPROTO_ICMP / 8] & (1 << (IPPROTO_ICMP % 8)));
336  FAIL_IF_NOT(sig_icmpv6->proto.proto[IPPROTO_ICMPV6 / 8] & (1 << (IPPROTO_ICMPV6 % 8)));
337 
338  FAIL_IF_NOT(sig_icmp->proto.proto[IPPROTO_ICMP / 8] & (1 << (IPPROTO_ICMP % 8)));
339  FAIL_IF_NOT(sig_icmp->proto.proto[IPPROTO_ICMPV6 / 8] & (1 << (IPPROTO_ICMPV6 % 8)));
340 
342 
343  PASS;
344 }
345 
346 /**
347  * \test signature parsing with tcp-pkt and tcp-stream
348  */
349 
350 static int DetectProtoTestSig01(void)
351 {
354 
355  de_ctx->flags |= DE_QUIET;
356 
358  de_ctx, "alert tcp-pkt any any -> any any (msg:\"tcp-pkt\"; content:\"blah\"; sid:1;)");
359  FAIL_IF_NULL(s);
360 
362  "alert tcp-stream any any -> any any (msg:\"tcp-stream\"; content:\"blah\"; sid:2;)");
363  FAIL_IF_NULL(s);
364 
366 
367  PASS;
368 }
369 #endif /* UNITTESTS */
370 
371 /**
372  * \brief this function registers unit tests for DetectProto
373  */
375 {
376 #ifdef UNITTESTS
377  UtRegisterTest("ProtoTestParse01", ProtoTestParse01);
378  UtRegisterTest("ProtoTestParse02", ProtoTestParse02);
379  UtRegisterTest("ProtoTestParse03", ProtoTestParse03);
380  UtRegisterTest("ProtoTestParse04", ProtoTestParse04);
381  UtRegisterTest("ProtoTestParse05", ProtoTestParse05);
382  UtRegisterTest("ProtoTestParse06", ProtoTestParse06);
383  UtRegisterTest("ProtoTestParse07", ProtoTestParse07);
384 
385  UtRegisterTest("DetectProtoTestSetup01", DetectProtoTestSetup01);
386  UtRegisterTest("DetectProtoTestSetup02", DetectProtoTestSetup02);
387 
388  UtRegisterTest("DetectProtoTestSig01", DetectProtoTestSig01);
389 #endif /* UNITTESTS */
390 }
391 
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
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
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:279
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:932
DETECT_PROTO_ANY
#define DETECT_PROTO_ANY
Definition: detect-engine-proto.h:28
DetectEngineCtxFree
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
Definition: detect-engine.c:2634
DE_QUIET
#define DE_QUIET
Definition: detect.h:330
proto
uint8_t proto
Definition: decode-template.h:0
DetectEngineAppendSig
Signature * DetectEngineAppendSig(DetectEngineCtx *, const char *)
Parse and append a Signature into the Detection Engine Context signature list.
Definition: detect-parse.c:3439
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:18
DetectProtoParse
int DetectProtoParse(DetectProto *dp, const char *str)
Parses a protocol sent as a string.
Definition: detect-engine-proto.c:56
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.
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
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:934
flow-var.h
DetectProtoContainsProto
int DetectProtoContainsProto(const DetectProto *dp, int proto)
see if a DetectProto contains a certain proto
Definition: detect-engine-proto.c:115
DetectProtoTests
void DetectProtoTests(void)
this function registers unit tests for DetectProto
Definition: detect-engine-proto.c:374