suricata
decode-sctp.c
Go to the documentation of this file.
1 /* Copyright (C) 2011-2021 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  * \ingroup decode
20  *
21  * @{
22  */
23 
24 /**
25  * \file
26  *
27  * \author Eric Leblond <eric@regit.org>
28  *
29  * Decode SCTP
30  */
31 
32 #include "suricata-common.h"
33 #include "decode.h"
34 #include "decode-sctp.h"
35 #include "decode-events.h"
36 
37 #include "util-validate.h"
38 #include "util-unittest.h"
39 #include "util-debug.h"
40 #include "util-optimize.h"
41 #include "flow.h"
42 
43 /**
44  * \brief Parse SCTP chunks after the common header.
45  *
46  * Iterates over chunks, validates each chunk header, and populates
47  * SCTPVars with chunk metadata.
48  *
49  * \param p Packet to decode
50  * \param pkt Pointer to the start of chunk data (after 12-byte common header)
51  * \param len Length of chunk data remaining
52  *
53  * \retval 0 on success (even if some events were set)
54  * \retval -1 on fatal error (packet should be rejected)
55  */
56 static int DecodeSCTPChunks(Packet *p, const uint8_t *pkt, uint16_t len)
57 {
58  const SCTPHdr *sctph = PacketGetSCTP(p);
59  const uint32_t vtag = SCTP_GET_RAW_VTAG(sctph);
60  uint32_t offset = 0;
61  uint16_t chunk_cnt = 0;
62  uint8_t tracked_chunk_cnt = 0;
63  bool has_init = false;
64  bool has_init_ack = false;
65  bool has_data = false;
66  bool has_abort = false;
67  uint8_t data_chunk_cnt = 0;
68  int ret = 0;
69 
70  while (offset < len) {
71  /* need at least a chunk header */
72  if (len - offset < SCTP_CHUNK_HDR_LEN) {
74  ret = -1;
75  break;
76  }
77 
78  SCTPChunkHdr chunk;
79  memcpy(&chunk, pkt + offset, sizeof(chunk));
80  const uint16_t chunk_len = SCNtohs(chunk.length);
81 
82  /* RFC 4960 sec 3.2: chunk length includes the header and must be >= 4 */
83  if (chunk_len < SCTP_CHUNK_HDR_LEN) {
85  ret = -1;
86  break;
87  }
88 
89  /* chunk must not extend beyond available data */
90  if (chunk_len > (len - offset)) {
92  ret = -1;
93  break;
94  }
95 
96  if (chunk_cnt < SCTP_MAX_TRACKED_CHUNKS) {
97  p->l4.vars.sctp.chunk_types[chunk_cnt] = chunk.type;
98  tracked_chunk_cnt++;
99  } else if (chunk_cnt == SCTP_MAX_TRACKED_CHUNKS) {
101  }
102  chunk_cnt++;
103 
104  switch (chunk.type) {
106  has_init = true;
107  /* RFC 4960 sec 8.5.1: INIT must have vtag == 0 */
108  if (vtag != 0) {
110  }
111  break;
113  has_init_ack = true;
114  break;
116  if (data_chunk_cnt < SCTP_MAX_DATA_CHUNKS && chunk_len >= SCTP_DATA_CHUNK_HDR_LEN) {
117  p->l4.vars.sctp.data_offsets[data_chunk_cnt] =
119  p->l4.vars.sctp.data_lens[data_chunk_cnt] = chunk_len - SCTP_DATA_CHUNK_HDR_LEN;
120  data_chunk_cnt++;
121  } else if (data_chunk_cnt == SCTP_MAX_DATA_CHUNKS &&
122  chunk_len >= SCTP_DATA_CHUNK_HDR_LEN) {
124  }
125  has_data = true;
126  /* DATA chunks must not have vtag == 0 */
127  if (vtag == 0) {
129  }
130  break;
132  has_abort = true;
133  break;
134  default:
135  break;
136  }
137 
138  /* advance to next chunk: padded to 4-byte boundary (RFC 4960 sec 3.2) */
139  uint32_t padded_len = (chunk_len + 3) & ~3U;
140  /* guard against infinite loop with zero-padding overshoot */
141  if (padded_len < SCTP_CHUNK_HDR_LEN) {
142  padded_len = SCTP_CHUNK_HDR_LEN;
143  }
144  offset += padded_len;
145  DEBUG_VALIDATE_BUG_ON(offset > (uint32_t)len + 3);
146  }
147 
148  /* RFC 4960 sec 6.10: INIT/INIT_ACK must be the only chunk in the packet */
149  if ((has_init || has_init_ack) && chunk_cnt > 1) {
151  }
152 
153  p->l4.vars.sctp.hlen = (uint16_t)(SCTP_HEADER_LEN + MIN(offset, len));
154  p->l4.vars.sctp.chunk_cnt = chunk_cnt;
155  p->l4.vars.sctp.tracked_chunk_cnt = tracked_chunk_cnt;
156  p->l4.vars.sctp.data_chunk_cnt = data_chunk_cnt;
157  p->l4.vars.sctp.has_init = has_init;
158  p->l4.vars.sctp.has_init_ack = has_init_ack;
159  p->l4.vars.sctp.has_data = has_data;
160  p->l4.vars.sctp.has_abort = has_abort;
161 
162  return ret;
163 }
164 
165 static int DecodeSCTPPacket(ThreadVars *tv, Packet *p, const uint8_t *pkt, uint16_t len)
166 {
167  DEBUG_VALIDATE_BUG_ON(pkt == NULL);
168 
169  if (unlikely(len < SCTP_HEADER_LEN)) {
171  return -1;
172  }
173 
174  const SCTPHdr *sctph = PacketSetSCTP(p, pkt);
175 
176  p->sp = SCTP_GET_RAW_SRC_PORT(sctph);
177  p->dp = SCTP_GET_RAW_DST_PORT(sctph);
178  p->payload = (uint8_t *)pkt + SCTP_HEADER_LEN;
180  p->proto = IPPROTO_SCTP;
181 
182  if (p->payload_len > 0) {
183  if (DecodeSCTPChunks(p, p->payload, p->payload_len) < 0) {
184  p->payload_len = 0;
185  return -1;
186  }
187 
188  if (p->l4.vars.sctp.data_chunk_cnt > 0) {
189  /* Point p->payload at the user data of the first DATA chunk,
190  * past the SCTP common header and the 16-byte DATA chunk
191  * header. This is what content will inspect. */
192  p->payload = (uint8_t *)pkt + p->l4.vars.sctp.data_offsets[0];
194  } else {
195  p->payload_len = 0;
196  }
197  } else {
199  }
200 
201  return 0;
202 }
203 
204 int DecodeSCTP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint16_t len)
205 {
207 
208  if (unlikely(DecodeSCTPPacket(tv, p, pkt, len) < 0)) {
209  PacketClearL4(p);
210  return TM_ECODE_FAILED;
211  }
212 
213  SCLogDebug("SCTP sp: %u -> dp: %u", p->sp, p->dp);
214 
215  if (p->l4.vars.sctp.has_init) {
217  }
218  if (p->l4.vars.sctp.has_init_ack) {
220  }
221  if (p->l4.vars.sctp.has_data) {
223  }
224  if (p->l4.vars.sctp.has_abort) {
226  }
227  for (uint8_t i = 0; i < p->l4.vars.sctp.tracked_chunk_cnt; i++) {
230  break;
231  }
232  }
233 
235 
236  return TM_ECODE_OK;
237 }
238 
239 #ifdef UNITTESTS
240 
241 /** \test Valid SCTP packet with INIT chunk */
242 static int SCTPDecodeValidInitTest01(void)
243 {
244  /* SCTP common header: sport=1234 dport=80 vtag=0 checksum=0
245  * followed by INIT chunk: type=0x01 flags=0x00 length=20
246  * with 16 bytes of INIT-specific data (initiate_tag, a_rwnd, etc.) */
247  // clang-format off
248  uint8_t raw_sctp[] = {
249  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
250  0x00, 0x00, 0x00, 0x00, /* vtag=0 */
251  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
252  0x01, 0x00, 0x00, 0x14, /* chunk: INIT, flags=0, len=20 */
253  0x00, 0x00, 0x00, 0x01, /* initiate_tag=1 */
254  0x00, 0x01, 0x00, 0x00, /* a_rwnd=65536 */
255  0x00, 0x01, 0x00, 0x01, /* num_outbound=1, num_inbound=1 */
256  0x00, 0x00, 0x00, 0x01, /* initial_tsn=1 */
257  };
258  // clang-format on
259 
261  FAIL_IF_NULL(p);
262  ThreadVars tv;
264 
265  memset(&tv, 0, sizeof(ThreadVars));
266  memset(&dtv, 0, sizeof(DecodeThreadVars));
267 
269  DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
270  FAIL_IF_NOT(PacketIsSCTP(p));
271 
272  FAIL_IF(p->sp != 1234);
273  FAIL_IF(p->dp != 80);
275  FAIL_IF(p->l4.vars.sctp.chunk_cnt != 1);
279 
280  /* no protocol violation events expected */
283 
284  PacketFree(p);
285  FlowShutdown();
286  PASS;
287 }
288 
289 /** \test Packet too small (< 12 bytes) */
290 static int SCTPDecodePktTooSmallTest02(void)
291 {
292  uint8_t raw_sctp[] = { 0x04, 0xd2, 0x00, 0x50, 0x00, 0x00 };
293 
295  FAIL_IF_NULL(p);
296  ThreadVars tv;
298 
299  memset(&tv, 0, sizeof(ThreadVars));
300  memset(&dtv, 0, sizeof(DecodeThreadVars));
301 
303  int ret = DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
304  FAIL_IF(ret != TM_ECODE_FAILED);
306 
307  PacketFree(p);
308  FlowShutdown();
309  PASS;
310 }
311 
312 /** \test Chunk too small - header + 2 bytes garbage (not enough for chunk header) */
313 static int SCTPDecodeChunkTooSmallTest03(void)
314 {
315  // clang-format off
316  uint8_t raw_sctp[] = {
317  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
318  0x00, 0x00, 0x00, 0x01, /* vtag=1 */
319  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
320  0x01, 0x00, /* only 2 bytes of chunk data */
321  };
322  // clang-format on
323 
325  FAIL_IF_NULL(p);
326  ThreadVars tv;
328 
329  memset(&tv, 0, sizeof(ThreadVars));
330  memset(&dtv, 0, sizeof(DecodeThreadVars));
331 
333  int ret = DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
334  FAIL_IF(ret != TM_ECODE_FAILED);
336 
337  PacketFree(p);
338  FlowShutdown();
339  PASS;
340 }
341 
342 /** \test Invalid chunk length (chunk_len < 4) */
343 static int SCTPDecodeChunkLenInvalidTest04(void)
344 {
345  // clang-format off
346  uint8_t raw_sctp[] = {
347  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
348  0x00, 0x00, 0x00, 0x01, /* vtag=1 */
349  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
350  0x00, 0x00, 0x00, 0x02, /* chunk: DATA, flags=0, len=2 (invalid < 4) */
351  };
352  // clang-format on
353 
355  FAIL_IF_NULL(p);
356  ThreadVars tv;
358 
359  memset(&tv, 0, sizeof(ThreadVars));
360  memset(&dtv, 0, sizeof(DecodeThreadVars));
361 
363  int ret = DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
364  FAIL_IF(ret != TM_ECODE_FAILED);
366 
367  PacketFree(p);
368  FlowShutdown();
369  PASS;
370 }
371 
372 /** \test INIT with non-zero verification tag */
373 static int SCTPDecodeInitNonZeroVtagTest05(void)
374 {
375  // clang-format off
376  uint8_t raw_sctp[] = {
377  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
378  0x00, 0x00, 0x00, 0x42, /* vtag=0x42 (non-zero, invalid for INIT) */
379  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
380  0x01, 0x00, 0x00, 0x14, /* chunk: INIT, flags=0, len=20 */
381  0x00, 0x00, 0x00, 0x01, /* initiate_tag=1 */
382  0x00, 0x01, 0x00, 0x00, /* a_rwnd=65536 */
383  0x00, 0x01, 0x00, 0x01, /* num_outbound=1, num_inbound=1 */
384  0x00, 0x00, 0x00, 0x01, /* initial_tsn=1 */
385  };
386  // clang-format on
387 
389  FAIL_IF_NULL(p);
390  ThreadVars tv;
392 
393  memset(&tv, 0, sizeof(ThreadVars));
394  memset(&dtv, 0, sizeof(DecodeThreadVars));
395 
397  DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
398  FAIL_IF_NOT(PacketIsSCTP(p));
400 
401  PacketFree(p);
402  FlowShutdown();
403  PASS;
404 }
405 
406 /** \test Multiple chunks: DATA + SACK */
407 static int SCTPDecodeMultiChunkTest06(void)
408 {
409  // clang-format off
410  uint8_t raw_sctp[] = {
411  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
412  0x00, 0x00, 0x00, 0x01, /* vtag=1 */
413  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
414  /* DATA chunk: type=0x00, flags=0x03, len=20 */
415  0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, /* TSN=0 */
416  0x00, 0x01, 0x00, 0x00, /* stream_id=1, stream_seq=0 */
417  0x00, 0x00, 0x00, 0x00, /* PPID=0 */
418  0x41, 0x42, 0x43, 0x44, /* data="ABCD" */
419  /* SACK chunk: type=0x03, flags=0x00, len=16 */
420  0x03, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, /* cumulative_tsn_ack=1 */
421  0x00, 0x01, 0x00, 0x00, /* a_rwnd=65536 */
422  0x00, 0x00, 0x00, 0x00, /* num_gap_blocks=0, num_dup_tsns=0 */
423  };
424  // clang-format on
425 
427  FAIL_IF_NULL(p);
428  ThreadVars tv;
430 
431  memset(&tv, 0, sizeof(ThreadVars));
432  memset(&dtv, 0, sizeof(DecodeThreadVars));
433 
435  DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
436  FAIL_IF_NOT(PacketIsSCTP(p));
437 
438  FAIL_IF(p->l4.vars.sctp.chunk_cnt != 2);
443 
444  PacketFree(p);
445  FlowShutdown();
446  PASS;
447 }
448 
449 /** \test INIT bundled with another chunk (violates RFC 4960 sec 6.10) */
450 static int SCTPDecodeInitNotAloneTest07(void)
451 {
452  // clang-format off
453  uint8_t raw_sctp[] = {
454  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
455  0x00, 0x00, 0x00, 0x00, /* vtag=0 */
456  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
457  /* INIT chunk: type=0x01, flags=0, len=20 */
458  0x01, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, /* initiate_tag=1 */
459  0x00, 0x01, 0x00, 0x00, /* a_rwnd=65536 */
460  0x00, 0x01, 0x00, 0x01, /* num_outbound=1, num_inbound=1 */
461  0x00, 0x00, 0x00, 0x01, /* initial_tsn=1 */
462  /* DATA chunk: type=0x00, flags=0, len=16 (bundled illegally) */
463  0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
464  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
465  };
466  // clang-format on
467 
469  FAIL_IF_NULL(p);
470  ThreadVars tv;
472 
473  memset(&tv, 0, sizeof(ThreadVars));
474  memset(&dtv, 0, sizeof(DecodeThreadVars));
475 
477  DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
478  FAIL_IF_NOT(PacketIsSCTP(p));
480  FAIL_IF(p->l4.vars.sctp.chunk_cnt != 2);
481 
482  PacketFree(p);
483  FlowShutdown();
484  PASS;
485 }
486 
487 /** \test DATA chunk payload extraction - p->payload points to user data */
488 static int SCTPDecodeDataPayloadTest08(void)
489 {
490  // clang-format off
491  uint8_t raw_sctp[] = {
492  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
493  0x00, 0x00, 0x00, 0x01, /* vtag=1 */
494  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
495  /* DATA chunk: type=0x00, flags=0x03, len=20 (16 hdr + 4 data) */
496  0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, /* TSN=1 */
497  0x00, 0x01, 0x00, 0x00, /* stream_id=1, stream_seq=0 */
498  0x00, 0x00, 0x00, 0x00, /* PPID=0 */
499  0x41, 0x42, 0x43, 0x44, /* data="ABCD" */
500  };
501  // clang-format on
502 
504  FAIL_IF_NULL(p);
505  ThreadVars tv;
507 
508  memset(&tv, 0, sizeof(ThreadVars));
509  memset(&dtv, 0, sizeof(DecodeThreadVars));
510 
512  DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
513  FAIL_IF_NOT(PacketIsSCTP(p));
514 
515  FAIL_IF(p->payload_len != 4);
516  FAIL_IF(memcmp(p->payload, "ABCD", 4) != 0);
517 
518  PacketFree(p);
519  FlowShutdown();
520  PASS;
521 }
522 
523 /** \test INIT-only packet - payload_len must be 0 (no application data) */
524 static int SCTPDecodeNoDataPayloadTest09(void)
525 {
526  // clang-format off
527  uint8_t raw_sctp[] = {
528  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
529  0x00, 0x00, 0x00, 0x00, /* vtag=0 */
530  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
531  0x01, 0x00, 0x00, 0x14, /* chunk: INIT, flags=0, len=20 */
532  0x00, 0x00, 0x00, 0x01, /* initiate_tag=1 */
533  0x00, 0x01, 0x00, 0x00, /* a_rwnd=65536 */
534  0x00, 0x01, 0x00, 0x01, /* num_outbound=1, num_inbound=1 */
535  0x00, 0x00, 0x00, 0x01, /* initial_tsn=1 */
536  };
537  // clang-format on
538 
540  FAIL_IF_NULL(p);
541  ThreadVars tv;
543 
544  memset(&tv, 0, sizeof(ThreadVars));
545  memset(&dtv, 0, sizeof(DecodeThreadVars));
546 
548  DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
549  FAIL_IF_NOT(PacketIsSCTP(p));
550 
551  FAIL_IF(p->payload_len != 0);
552 
553  PacketFree(p);
554  FlowShutdown();
555  PASS;
556 }
557 
558 /** \test Verify data_offset and data_len in SCTPVars */
559 static int SCTPDecodeDataOffsetTest10(void)
560 {
561  // clang-format off
562  uint8_t raw_sctp[] = {
563  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
564  0x00, 0x00, 0x00, 0x01, /* vtag=1 */
565  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
566  /* DATA chunk: type=0x00, flags=0x03, len=20 (16 hdr + 4 data) */
567  0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, /* TSN=1 */
568  0x00, 0x01, 0x00, 0x00, /* stream_id=1, stream_seq=0 */
569  0x00, 0x00, 0x00, 0x00, /* PPID=0 */
570  0x41, 0x42, 0x43, 0x44, /* data="ABCD" */
571  };
572  // clang-format on
573 
575  FAIL_IF_NULL(p);
576  ThreadVars tv;
578 
579  memset(&tv, 0, sizeof(ThreadVars));
580  memset(&dtv, 0, sizeof(DecodeThreadVars));
581 
583  DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
584  FAIL_IF_NOT(PacketIsSCTP(p));
585 
586  /* data_offsets[0] = SCTP_HEADER_LEN(12) + chunk_offset(0) + DATA_CHUNK_HDR_LEN(16) = 28 */
588  FAIL_IF(p->l4.vars.sctp.data_offsets[0] != 28);
589  FAIL_IF(p->l4.vars.sctp.data_lens[0] != 4);
590 
591  PacketFree(p);
592  FlowShutdown();
593  PASS;
594 }
595 
596 /** \test Two DATA chunks - both tracked, p->payload is first DATA's user data */
597 static int SCTPDecodeMultiDataTest11(void)
598 {
599  // clang-format off
600  uint8_t raw_sctp[] = {
601  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
602  0x00, 0x00, 0x00, 0x01, /* vtag=1 */
603  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
604  /* DATA chunk 1: type=0x00, flags=0x02, len=20 (16 hdr + 4 data) */
605  0x00, 0x02, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, /* TSN=1 */
606  0x00, 0x01, 0x00, 0x00, /* stream_id=1, stream_seq=0 */
607  0x00, 0x00, 0x00, 0x00, /* PPID=0 */
608  0x41, 0x42, 0x43, 0x44, /* data="ABCD" */
609  /* DATA chunk 2: type=0x00, flags=0x01, len=20 (16 hdr + 4 data) */
610  0x00, 0x01, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, /* TSN=2 */
611  0x00, 0x01, 0x00, 0x01, /* stream_id=1, stream_seq=1 */
612  0x00, 0x00, 0x00, 0x00, /* PPID=0 */
613  0x45, 0x46, 0x47, 0x48, /* data="EFGH" */
614  };
615  // clang-format on
616 
618  FAIL_IF_NULL(p);
619  ThreadVars tv;
621 
622  memset(&tv, 0, sizeof(ThreadVars));
623  memset(&dtv, 0, sizeof(DecodeThreadVars));
624 
626  DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
627  FAIL_IF_NOT(PacketIsSCTP(p));
628 
629  FAIL_IF(p->l4.vars.sctp.chunk_cnt != 2);
631  /* first DATA chunk: offset = 12 + 0 + 16 = 28, len = 4 */
632  FAIL_IF(p->l4.vars.sctp.data_offsets[0] != 28);
633  FAIL_IF(p->l4.vars.sctp.data_lens[0] != 4);
634  /* second DATA chunk: offset = 12 + 20 + 16 = 48, len = 4 */
635  FAIL_IF(p->l4.vars.sctp.data_offsets[1] != 48);
636  FAIL_IF(p->l4.vars.sctp.data_lens[1] != 4);
637  /* p->payload still points to first DATA's user data */
638  FAIL_IF(p->payload_len != 4);
639  FAIL_IF(memcmp(p->payload, "ABCD", 4) != 0);
640 
641  PacketFree(p);
642  FlowShutdown();
643  PASS;
644 }
645 
646 /** \test DATA chunk with chunk_len < 16 - malformed, payload_len must be 0 */
647 static int SCTPDecodeSmallDataChunkTest12(void)
648 {
649  // clang-format off
650  uint8_t raw_sctp[] = {
651  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
652  0x00, 0x00, 0x00, 0x01, /* vtag=1 */
653  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
654  /* DATA chunk: type=0x00, flags=0x00, len=8 (< 16, malformed) */
655  0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
656  0x00, /* 4 bytes of value (not enough for DATA header) */
657  };
658  // clang-format on
659 
661  FAIL_IF_NULL(p);
662  ThreadVars tv;
664 
665  memset(&tv, 0, sizeof(ThreadVars));
666  memset(&dtv, 0, sizeof(DecodeThreadVars));
667 
669  DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
670  FAIL_IF_NOT(PacketIsSCTP(p));
671 
673  FAIL_IF(p->payload_len != 0);
674 
675  PacketFree(p);
676  FlowShutdown();
677  PASS;
678 }
679 
680 #endif /* UNITTESTS */
681 
683 {
684 #ifdef UNITTESTS
685  UtRegisterTest("SCTPDecodeValidInitTest01", SCTPDecodeValidInitTest01);
686  UtRegisterTest("SCTPDecodePktTooSmallTest02", SCTPDecodePktTooSmallTest02);
687  UtRegisterTest("SCTPDecodeChunkTooSmallTest03", SCTPDecodeChunkTooSmallTest03);
688  UtRegisterTest("SCTPDecodeChunkLenInvalidTest04", SCTPDecodeChunkLenInvalidTest04);
689  UtRegisterTest("SCTPDecodeInitNonZeroVtagTest05", SCTPDecodeInitNonZeroVtagTest05);
690  UtRegisterTest("SCTPDecodeMultiChunkTest06", SCTPDecodeMultiChunkTest06);
691  UtRegisterTest("SCTPDecodeInitNotAloneTest07", SCTPDecodeInitNotAloneTest07);
692  UtRegisterTest("SCTPDecodeDataPayloadTest08", SCTPDecodeDataPayloadTest08);
693  UtRegisterTest("SCTPDecodeNoDataPayloadTest09", SCTPDecodeNoDataPayloadTest09);
694  UtRegisterTest("SCTPDecodeDataOffsetTest10", SCTPDecodeDataOffsetTest10);
695  UtRegisterTest("SCTPDecodeMultiDataTest11", SCTPDecodeMultiDataTest11);
696  UtRegisterTest("SCTPDecodeSmallDataChunkTest12", SCTPDecodeSmallDataChunkTest12);
697 #endif
698 }
699 /**
700  * @}
701  */
ENGINE_SET_EVENT
#define ENGINE_SET_EVENT(p, e)
Definition: decode.h:1230
SCTP_CHUNK_TYPE_SHUTDOWN
#define SCTP_CHUNK_TYPE_SHUTDOWN
Definition: decode-sctp.h:55
Packet_::proto
uint8_t proto
Definition: decode.h:537
len
uint8_t len
Definition: app-layer-dnp3.h:2
FAIL_IF_NULL
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
Definition: util-unittest.h:89
offset
uint64_t offset
Definition: util-streaming-buffer.h:0
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
SCTP_DATA_WITH_ZERO_VTAG
@ SCTP_DATA_WITH_ZERO_VTAG
Definition: decode-events.h:180
ENGINE_ISSET_EVENT
#define ENGINE_ISSET_EVENT(p, e)
Definition: decode.h:1243
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:282
SCTPVars_::chunk_types
uint8_t chunk_types[SCTP_MAX_TRACKED_CHUNKS]
Definition: decode-sctp.h:82
SCTP_TOO_MANY_CHUNKS
@ SCTP_TOO_MANY_CHUNKS
Definition: decode-events.h:181
Packet_::payload
uint8_t * payload
Definition: decode.h:619
SCTP_HEADER_LEN
#define SCTP_HEADER_LEN
Definition: decode-sctp.h:28
SCTP_GET_RAW_SRC_PORT
#define SCTP_GET_RAW_SRC_PORT(sctph)
Definition: decode-sctp.h:92
SCTP_CHUNK_TYPE_DATA
#define SCTP_CHUNK_TYPE_DATA
Definition: decode-sctp.h:48
SCTPVars_::chunk_cnt
uint16_t chunk_cnt
Definition: decode-sctp.h:80
MIN
#define MIN(x, y)
Definition: suricata-common.h:416
SCTPVars_::has_init
bool has_init
Definition: decode-sctp.h:84
p
Packet * p
Definition: fuzz_iprep.c:21
DecodeThreadVars_::counter_sctp_init_ack
StatsCounterId counter_sctp_init_ack
Definition: decode.h:1030
TM_ECODE_FAILED
@ TM_ECODE_FAILED
Definition: tm-threads-common.h:82
DecodeThreadVars_::counter_sctp
StatsCounterId counter_sctp
Definition: decode.h:1028
SCTP_TOO_MANY_DATA_CHUNKS
@ SCTP_TOO_MANY_DATA_CHUNKS
Definition: decode-events.h:182
Packet_::payload_len
uint16_t payload_len
Definition: decode.h:620
util-unittest.h
DecodeSCTP
int DecodeSCTP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint16_t len)
Definition: decode-sctp.c:204
FAIL_IF_NOT
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
Definition: util-unittest.h:82
TM_ECODE_OK
@ TM_ECODE_OK
Definition: tm-threads-common.h:81
SCTPVars_::data_offsets
uint16_t data_offsets[SCTP_MAX_DATA_CHUNKS]
Definition: decode-sctp.h:88
FlowInitConfig
void FlowInitConfig(bool quiet)
initialize the configuration
Definition: flow.c:577
SCTP_MAX_DATA_CHUNKS
#define SCTP_MAX_DATA_CHUNKS
Definition: decode-sctp.h:42
decode.h
util-debug.h
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
SCTP_CHUNK_TYPE_ABORT
#define SCTP_CHUNK_TYPE_ABORT
Definition: decode-sctp.h:54
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:58
SCTP_GET_RAW_VTAG
#define SCTP_GET_RAW_VTAG(sctph)
Definition: decode-sctp.h:94
Packet_::sp
Port sp
Definition: decode.h:522
StatsCounterIncr
void StatsCounterIncr(StatsThreadContext *stats, StatsCounterId id)
Increments the local counter.
Definition: counters.c:164
PacketFree
void PacketFree(Packet *p)
Return a malloced packet.
Definition: decode.c:222
DecodeThreadVars_::counter_sctp_init
StatsCounterId counter_sctp_init
Definition: decode.h:1029
DecodeSCTPRegisterTests
void DecodeSCTPRegisterTests(void)
Definition: decode-sctp.c:682
Packet_
Definition: decode.h:515
Packet_::l4
struct PacketL4 l4
Definition: decode.h:615
SCTP_CHUNK_LEN_INVALID
@ SCTP_CHUNK_LEN_INVALID
Definition: decode-events.h:177
DecodeThreadVars_::counter_sctp_shutdown
StatsCounterId counter_sctp_shutdown
Definition: decode.h:1033
SCTP_CHUNK_HDR_LEN
#define SCTP_CHUNK_HDR_LEN
Definition: decode-sctp.h:31
SCTP_CHUNK_TYPE_INIT_ACK
#define SCTP_CHUNK_TYPE_INIT_ACK
Definition: decode-sctp.h:50
SCTPVars_::has_abort
bool has_abort
Definition: decode-sctp.h:87
SCTP_MAX_TRACKED_CHUNKS
#define SCTP_MAX_TRACKED_CHUNKS
Definition: decode-sctp.h:39
decode-events.h
dtv
DecodeThreadVars * dtv
Definition: fuzz_decodepcapfile.c:34
SCTP_DATA_CHUNK_HDR_LEN
#define SCTP_DATA_CHUNK_HDR_LEN
Definition: decode-sctp.h:45
PacketL4::L4Vars::sctp
SCTPVars sctp
Definition: decode.h:494
DecodeThreadVars_::counter_sctp_abort
StatsCounterId counter_sctp_abort
Definition: decode.h:1032
SCTPVars_::tracked_chunk_cnt
uint8_t tracked_chunk_cnt
Definition: decode-sctp.h:81
FAIL_IF
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
Definition: util-unittest.h:71
SCNtohs
#define SCNtohs(x)
Definition: suricata-common.h:439
suricata-common.h
FlowShutdown
void FlowShutdown(void)
shutdown the flow engine
Definition: flow.c:721
decode-sctp.h
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:33
util-optimize.h
SCTP_CHUNK_TYPE_INIT
#define SCTP_CHUNK_TYPE_INIT
Definition: decode-sctp.h:49
util-validate.h
PacketGetFromAlloc
Packet * PacketGetFromAlloc(void)
Get a malloced packet.
Definition: decode.c:261
DecodeThreadVars_
Structure to hold thread specific data for all decode modules.
Definition: decode.h:994
SCTPVars_::has_data
bool has_data
Definition: decode-sctp.h:86
SCTP_INIT_CHUNK_BUNDLED
@ SCTP_INIT_CHUNK_BUNDLED
Definition: decode-events.h:178
SCTP_PKT_TOO_SMALL
@ SCTP_PKT_TOO_SMALL
Definition: decode-events.h:175
SCTP_CHUNK_TYPE_SACK
#define SCTP_CHUNK_TYPE_SACK
Definition: decode-sctp.h:51
SCTP_GET_RAW_DST_PORT
#define SCTP_GET_RAW_DST_PORT(sctph)
Definition: decode-sctp.h:93
ENGINE_SET_INVALID_EVENT
#define ENGINE_SET_INVALID_EVENT(p, e)
Definition: decode.h:1238
FLOW_QUIET
#define FLOW_QUIET
Definition: flow.h:43
SCTPVars_::hlen
uint16_t hlen
Definition: decode-sctp.h:79
IPPROTO_SCTP
#define IPPROTO_SCTP
Definition: decode.h:1272
SCTP_CHUNK_TOO_SMALL
@ SCTP_CHUNK_TOO_SMALL
Definition: decode-events.h:176
SCTP_INIT_WITH_NON_ZERO_VTAG
@ SCTP_INIT_WITH_NON_ZERO_VTAG
Definition: decode-events.h:179
SCTPVars_::data_lens
uint16_t data_lens[SCTP_MAX_DATA_CHUNKS]
Definition: decode-sctp.h:89
flow.h
Packet_::dp
Port dp
Definition: decode.h:530
ThreadVars_::stats
StatsThreadContext stats
Definition: threadvars.h:121
DEBUG_VALIDATE_BUG_ON
#define DEBUG_VALIDATE_BUG_ON(exp)
Definition: util-validate.h:109
FlowSetupPacket
void FlowSetupPacket(Packet *p)
prepare packet for a life with flow Set PKT_WANTS_FLOW flag to indicate workers should do a flow look...
Definition: flow-hash.c:516
SCTPVars_::has_init_ack
bool has_init_ack
Definition: decode-sctp.h:85
PacketL4::vars
union PacketL4::L4Vars vars
DecodeThreadVars_::counter_sctp_data
StatsCounterId counter_sctp_data
Definition: decode.h:1031
SCTPVars_::data_chunk_cnt
uint8_t data_chunk_cnt
Definition: decode-sctp.h:83