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-debug.h"
39 #include "flow.h"
40 
41 /**
42  * \brief Parse SCTP chunks after the common header.
43  *
44  * Iterates over chunks, validates each chunk header, and populates
45  * SCTPVars with chunk metadata.
46  *
47  * \param p Packet to decode
48  * \param pkt Pointer to the start of chunk data (after 12-byte common header)
49  * \param len Length of chunk data remaining
50  *
51  * \retval 0 on success (even if some events were set)
52  * \retval -1 on fatal error (packet should be rejected)
53  */
54 static int DecodeSCTPChunks(Packet *p, const uint8_t *pkt, uint16_t len)
55 {
56  const SCTPHdr *sctph = PacketGetSCTP(p);
57  const uint32_t vtag = SCTP_GET_RAW_VTAG(sctph);
58  uint32_t offset = 0;
59  uint16_t chunk_cnt = 0;
60  uint8_t tracked_chunk_cnt = 0;
61  bool has_init = false;
62  bool has_init_ack = false;
63  bool has_data = false;
64  bool has_abort = false;
65  uint8_t data_chunk_cnt = 0;
66  int ret = 0;
67 
68  while (offset < len) {
69  /* need at least a chunk header */
70  if (len - offset < SCTP_CHUNK_HDR_LEN) {
72  ret = -1;
73  break;
74  }
75 
76  SCTPChunkHdr chunk;
77  memcpy(&chunk, pkt + offset, sizeof(chunk));
78  const uint16_t chunk_len = SCNtohs(chunk.length);
79 
80  /* RFC 4960 sec 3.2: chunk length includes the header and must be >= 4 */
81  if (chunk_len < SCTP_CHUNK_HDR_LEN) {
83  ret = -1;
84  break;
85  }
86 
87  /* chunk must not extend beyond available data */
88  if (chunk_len > (len - offset)) {
90  ret = -1;
91  break;
92  }
93 
94  if (chunk_cnt < SCTP_MAX_TRACKED_CHUNKS) {
95  p->l4.vars.sctp.chunk_types[chunk_cnt] = chunk.type;
96  tracked_chunk_cnt++;
97  } else if (chunk_cnt == SCTP_MAX_TRACKED_CHUNKS) {
99  }
100  chunk_cnt++;
101 
102  switch (chunk.type) {
104  has_init = true;
105  /* RFC 4960 sec 8.5.1: INIT must have vtag == 0 */
106  if (vtag != 0) {
108  }
109  break;
111  has_init_ack = true;
112  break;
114  if (data_chunk_cnt < SCTP_MAX_DATA_CHUNKS && chunk_len >= SCTP_DATA_CHUNK_HDR_LEN) {
115  p->l4.vars.sctp.data_offsets[data_chunk_cnt] =
117  p->l4.vars.sctp.data_lens[data_chunk_cnt] = chunk_len - SCTP_DATA_CHUNK_HDR_LEN;
118  data_chunk_cnt++;
119  } else if (data_chunk_cnt == SCTP_MAX_DATA_CHUNKS &&
120  chunk_len >= SCTP_DATA_CHUNK_HDR_LEN) {
122  }
123  has_data = true;
124  /* DATA chunks must not have vtag == 0 */
125  if (vtag == 0) {
127  }
128  break;
130  has_abort = true;
131  break;
132  default:
133  break;
134  }
135 
136  /* advance to next chunk: padded to 4-byte boundary (RFC 4960 sec 3.2) */
137  uint32_t padded_len = (chunk_len + 3) & ~3U;
138  /* guard against infinite loop with zero-padding overshoot */
139  if (padded_len < SCTP_CHUNK_HDR_LEN) {
140  padded_len = SCTP_CHUNK_HDR_LEN;
141  }
142  offset += padded_len;
143  DEBUG_VALIDATE_BUG_ON(offset > (uint32_t)len + 3);
144  }
145 
146  /* RFC 4960 sec 6.10: INIT/INIT_ACK must be the only chunk in the packet */
147  if ((has_init || has_init_ack) && chunk_cnt > 1) {
149  }
150 
151  p->l4.vars.sctp.hlen = (uint16_t)(SCTP_HEADER_LEN + MIN(offset, len));
152  p->l4.vars.sctp.chunk_cnt = chunk_cnt;
153  p->l4.vars.sctp.tracked_chunk_cnt = tracked_chunk_cnt;
154  p->l4.vars.sctp.data_chunk_cnt = data_chunk_cnt;
155  p->l4.vars.sctp.has_init = has_init;
156  p->l4.vars.sctp.has_init_ack = has_init_ack;
157  p->l4.vars.sctp.has_data = has_data;
158  p->l4.vars.sctp.has_abort = has_abort;
159 
160  return ret;
161 }
162 
163 static int DecodeSCTPPacket(ThreadVars *tv, Packet *p, const uint8_t *pkt, uint16_t len)
164 {
165  DEBUG_VALIDATE_BUG_ON(pkt == NULL);
166 
167  if (unlikely(len < SCTP_HEADER_LEN)) {
169  return -1;
170  }
171 
172  const SCTPHdr *sctph = PacketSetSCTP(p, pkt);
173 
174  p->sp = SCTP_GET_RAW_SRC_PORT(sctph);
175  p->dp = SCTP_GET_RAW_DST_PORT(sctph);
176  p->payload = (uint8_t *)pkt + SCTP_HEADER_LEN;
178  p->proto = IPPROTO_SCTP;
179 
180  if (p->payload_len > 0) {
181  if (DecodeSCTPChunks(p, p->payload, p->payload_len) < 0) {
182  p->payload_len = 0;
183  return -1;
184  }
185 
186  if (p->l4.vars.sctp.data_chunk_cnt > 0) {
187  /* Point p->payload at the user data of the first DATA chunk,
188  * past the SCTP common header and the 16-byte DATA chunk
189  * header. This is what content will inspect. */
190  p->payload = (uint8_t *)pkt + p->l4.vars.sctp.data_offsets[0];
192  } else {
193  p->payload_len = 0;
194  }
195  } else {
197  }
198 
199  return 0;
200 }
201 
202 int DecodeSCTP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint16_t len)
203 {
205 
206  if (unlikely(DecodeSCTPPacket(tv, p, pkt, len) < 0)) {
207  PacketClearL4(p);
208  return TM_ECODE_FAILED;
209  }
210 
211  SCLogDebug("SCTP sp: %u -> dp: %u", p->sp, p->dp);
212 
213  if (p->l4.vars.sctp.has_init) {
215  }
216  if (p->l4.vars.sctp.has_init_ack) {
218  }
219  if (p->l4.vars.sctp.has_data) {
221  }
222  if (p->l4.vars.sctp.has_abort) {
224  }
225  for (uint8_t i = 0; i < p->l4.vars.sctp.tracked_chunk_cnt; i++) {
228  break;
229  }
230  }
231 
233 
234  return TM_ECODE_OK;
235 }
236 
237 #ifdef UNITTESTS
238 
239 /** \test Valid SCTP packet with INIT chunk */
240 static int SCTPDecodeValidInitTest01(void)
241 {
242  /* SCTP common header: sport=1234 dport=80 vtag=0 checksum=0
243  * followed by INIT chunk: type=0x01 flags=0x00 length=20
244  * with 16 bytes of INIT-specific data (initiate_tag, a_rwnd, etc.) */
245  // clang-format off
246  uint8_t raw_sctp[] = {
247  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
248  0x00, 0x00, 0x00, 0x00, /* vtag=0 */
249  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
250  0x01, 0x00, 0x00, 0x14, /* chunk: INIT, flags=0, len=20 */
251  0x00, 0x00, 0x00, 0x01, /* initiate_tag=1 */
252  0x00, 0x01, 0x00, 0x00, /* a_rwnd=65536 */
253  0x00, 0x01, 0x00, 0x01, /* num_outbound=1, num_inbound=1 */
254  0x00, 0x00, 0x00, 0x01, /* initial_tsn=1 */
255  };
256  // clang-format on
257 
259  FAIL_IF_NULL(p);
260  ThreadVars tv;
262 
263  memset(&tv, 0, sizeof(ThreadVars));
264  memset(&dtv, 0, sizeof(DecodeThreadVars));
265 
267  DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
268  FAIL_IF_NOT(PacketIsSCTP(p));
269 
270  FAIL_IF(p->sp != 1234);
271  FAIL_IF(p->dp != 80);
273  FAIL_IF(p->l4.vars.sctp.chunk_cnt != 1);
277 
278  /* no protocol violation events expected */
281 
282  PacketFree(p);
283  FlowShutdown();
284  PASS;
285 }
286 
287 /** \test Packet too small (< 12 bytes) */
288 static int SCTPDecodePktTooSmallTest02(void)
289 {
290  uint8_t raw_sctp[] = { 0x04, 0xd2, 0x00, 0x50, 0x00, 0x00 };
291 
293  FAIL_IF_NULL(p);
294  ThreadVars tv;
296 
297  memset(&tv, 0, sizeof(ThreadVars));
298  memset(&dtv, 0, sizeof(DecodeThreadVars));
299 
301  int ret = DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
302  FAIL_IF(ret != TM_ECODE_FAILED);
304 
305  PacketFree(p);
306  FlowShutdown();
307  PASS;
308 }
309 
310 /** \test Chunk too small - header + 2 bytes garbage (not enough for chunk header) */
311 static int SCTPDecodeChunkTooSmallTest03(void)
312 {
313  // clang-format off
314  uint8_t raw_sctp[] = {
315  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
316  0x00, 0x00, 0x00, 0x01, /* vtag=1 */
317  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
318  0x01, 0x00, /* only 2 bytes of chunk data */
319  };
320  // clang-format on
321 
323  FAIL_IF_NULL(p);
324  ThreadVars tv;
326 
327  memset(&tv, 0, sizeof(ThreadVars));
328  memset(&dtv, 0, sizeof(DecodeThreadVars));
329 
331  int ret = DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
332  FAIL_IF(ret != TM_ECODE_FAILED);
334 
335  PacketFree(p);
336  FlowShutdown();
337  PASS;
338 }
339 
340 /** \test Invalid chunk length (chunk_len < 4) */
341 static int SCTPDecodeChunkLenInvalidTest04(void)
342 {
343  // clang-format off
344  uint8_t raw_sctp[] = {
345  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
346  0x00, 0x00, 0x00, 0x01, /* vtag=1 */
347  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
348  0x00, 0x00, 0x00, 0x02, /* chunk: DATA, flags=0, len=2 (invalid < 4) */
349  };
350  // clang-format on
351 
353  FAIL_IF_NULL(p);
354  ThreadVars tv;
356 
357  memset(&tv, 0, sizeof(ThreadVars));
358  memset(&dtv, 0, sizeof(DecodeThreadVars));
359 
361  int ret = DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
362  FAIL_IF(ret != TM_ECODE_FAILED);
364 
365  PacketFree(p);
366  FlowShutdown();
367  PASS;
368 }
369 
370 /** \test INIT with non-zero verification tag */
371 static int SCTPDecodeInitNonZeroVtagTest05(void)
372 {
373  // clang-format off
374  uint8_t raw_sctp[] = {
375  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
376  0x00, 0x00, 0x00, 0x42, /* vtag=0x42 (non-zero, invalid for INIT) */
377  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
378  0x01, 0x00, 0x00, 0x14, /* chunk: INIT, flags=0, len=20 */
379  0x00, 0x00, 0x00, 0x01, /* initiate_tag=1 */
380  0x00, 0x01, 0x00, 0x00, /* a_rwnd=65536 */
381  0x00, 0x01, 0x00, 0x01, /* num_outbound=1, num_inbound=1 */
382  0x00, 0x00, 0x00, 0x01, /* initial_tsn=1 */
383  };
384  // clang-format on
385 
387  FAIL_IF_NULL(p);
388  ThreadVars tv;
390 
391  memset(&tv, 0, sizeof(ThreadVars));
392  memset(&dtv, 0, sizeof(DecodeThreadVars));
393 
395  DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
396  FAIL_IF_NOT(PacketIsSCTP(p));
398 
399  PacketFree(p);
400  FlowShutdown();
401  PASS;
402 }
403 
404 /** \test Multiple chunks: DATA + SACK */
405 static int SCTPDecodeMultiChunkTest06(void)
406 {
407  // clang-format off
408  uint8_t raw_sctp[] = {
409  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
410  0x00, 0x00, 0x00, 0x01, /* vtag=1 */
411  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
412  /* DATA chunk: type=0x00, flags=0x03, len=20 */
413  0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, /* TSN=0 */
414  0x00, 0x01, 0x00, 0x00, /* stream_id=1, stream_seq=0 */
415  0x00, 0x00, 0x00, 0x00, /* PPID=0 */
416  0x41, 0x42, 0x43, 0x44, /* data="ABCD" */
417  /* SACK chunk: type=0x03, flags=0x00, len=16 */
418  0x03, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, /* cumulative_tsn_ack=1 */
419  0x00, 0x01, 0x00, 0x00, /* a_rwnd=65536 */
420  0x00, 0x00, 0x00, 0x00, /* num_gap_blocks=0, num_dup_tsns=0 */
421  };
422  // clang-format on
423 
425  FAIL_IF_NULL(p);
426  ThreadVars tv;
428 
429  memset(&tv, 0, sizeof(ThreadVars));
430  memset(&dtv, 0, sizeof(DecodeThreadVars));
431 
433  DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
434  FAIL_IF_NOT(PacketIsSCTP(p));
435 
436  FAIL_IF(p->l4.vars.sctp.chunk_cnt != 2);
441 
442  PacketFree(p);
443  FlowShutdown();
444  PASS;
445 }
446 
447 /** \test INIT bundled with another chunk (violates RFC 4960 sec 6.10) */
448 static int SCTPDecodeInitNotAloneTest07(void)
449 {
450  // clang-format off
451  uint8_t raw_sctp[] = {
452  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
453  0x00, 0x00, 0x00, 0x00, /* vtag=0 */
454  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
455  /* INIT chunk: type=0x01, flags=0, len=20 */
456  0x01, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, /* initiate_tag=1 */
457  0x00, 0x01, 0x00, 0x00, /* a_rwnd=65536 */
458  0x00, 0x01, 0x00, 0x01, /* num_outbound=1, num_inbound=1 */
459  0x00, 0x00, 0x00, 0x01, /* initial_tsn=1 */
460  /* DATA chunk: type=0x00, flags=0, len=16 (bundled illegally) */
461  0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
462  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
463  };
464  // clang-format on
465 
467  FAIL_IF_NULL(p);
468  ThreadVars tv;
470 
471  memset(&tv, 0, sizeof(ThreadVars));
472  memset(&dtv, 0, sizeof(DecodeThreadVars));
473 
475  DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
476  FAIL_IF_NOT(PacketIsSCTP(p));
478  FAIL_IF(p->l4.vars.sctp.chunk_cnt != 2);
479 
480  PacketFree(p);
481  FlowShutdown();
482  PASS;
483 }
484 
485 /** \test DATA chunk payload extraction - p->payload points to user data */
486 static int SCTPDecodeDataPayloadTest08(void)
487 {
488  // clang-format off
489  uint8_t raw_sctp[] = {
490  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
491  0x00, 0x00, 0x00, 0x01, /* vtag=1 */
492  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
493  /* DATA chunk: type=0x00, flags=0x03, len=20 (16 hdr + 4 data) */
494  0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, /* TSN=1 */
495  0x00, 0x01, 0x00, 0x00, /* stream_id=1, stream_seq=0 */
496  0x00, 0x00, 0x00, 0x00, /* PPID=0 */
497  0x41, 0x42, 0x43, 0x44, /* data="ABCD" */
498  };
499  // clang-format on
500 
502  FAIL_IF_NULL(p);
503  ThreadVars tv;
505 
506  memset(&tv, 0, sizeof(ThreadVars));
507  memset(&dtv, 0, sizeof(DecodeThreadVars));
508 
510  DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
511  FAIL_IF_NOT(PacketIsSCTP(p));
512 
513  FAIL_IF(p->payload_len != 4);
514  FAIL_IF(memcmp(p->payload, "ABCD", 4) != 0);
515 
516  PacketFree(p);
517  FlowShutdown();
518  PASS;
519 }
520 
521 /** \test INIT-only packet - payload_len must be 0 (no application data) */
522 static int SCTPDecodeNoDataPayloadTest09(void)
523 {
524  // clang-format off
525  uint8_t raw_sctp[] = {
526  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
527  0x00, 0x00, 0x00, 0x00, /* vtag=0 */
528  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
529  0x01, 0x00, 0x00, 0x14, /* chunk: INIT, flags=0, len=20 */
530  0x00, 0x00, 0x00, 0x01, /* initiate_tag=1 */
531  0x00, 0x01, 0x00, 0x00, /* a_rwnd=65536 */
532  0x00, 0x01, 0x00, 0x01, /* num_outbound=1, num_inbound=1 */
533  0x00, 0x00, 0x00, 0x01, /* initial_tsn=1 */
534  };
535  // clang-format on
536 
538  FAIL_IF_NULL(p);
539  ThreadVars tv;
541 
542  memset(&tv, 0, sizeof(ThreadVars));
543  memset(&dtv, 0, sizeof(DecodeThreadVars));
544 
546  DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
547  FAIL_IF_NOT(PacketIsSCTP(p));
548 
549  FAIL_IF(p->payload_len != 0);
550 
551  PacketFree(p);
552  FlowShutdown();
553  PASS;
554 }
555 
556 /** \test Verify data_offset and data_len in SCTPVars */
557 static int SCTPDecodeDataOffsetTest10(void)
558 {
559  // clang-format off
560  uint8_t raw_sctp[] = {
561  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
562  0x00, 0x00, 0x00, 0x01, /* vtag=1 */
563  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
564  /* DATA chunk: type=0x00, flags=0x03, len=20 (16 hdr + 4 data) */
565  0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, /* TSN=1 */
566  0x00, 0x01, 0x00, 0x00, /* stream_id=1, stream_seq=0 */
567  0x00, 0x00, 0x00, 0x00, /* PPID=0 */
568  0x41, 0x42, 0x43, 0x44, /* data="ABCD" */
569  };
570  // clang-format on
571 
573  FAIL_IF_NULL(p);
574  ThreadVars tv;
576 
577  memset(&tv, 0, sizeof(ThreadVars));
578  memset(&dtv, 0, sizeof(DecodeThreadVars));
579 
581  DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
582  FAIL_IF_NOT(PacketIsSCTP(p));
583 
584  /* data_offsets[0] = SCTP_HEADER_LEN(12) + chunk_offset(0) + DATA_CHUNK_HDR_LEN(16) = 28 */
586  FAIL_IF(p->l4.vars.sctp.data_offsets[0] != 28);
587  FAIL_IF(p->l4.vars.sctp.data_lens[0] != 4);
588 
589  PacketFree(p);
590  FlowShutdown();
591  PASS;
592 }
593 
594 /** \test Two DATA chunks - both tracked, p->payload is first DATA's user data */
595 static int SCTPDecodeMultiDataTest11(void)
596 {
597  // clang-format off
598  uint8_t raw_sctp[] = {
599  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
600  0x00, 0x00, 0x00, 0x01, /* vtag=1 */
601  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
602  /* DATA chunk 1: type=0x00, flags=0x02, len=20 (16 hdr + 4 data) */
603  0x00, 0x02, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, /* TSN=1 */
604  0x00, 0x01, 0x00, 0x00, /* stream_id=1, stream_seq=0 */
605  0x00, 0x00, 0x00, 0x00, /* PPID=0 */
606  0x41, 0x42, 0x43, 0x44, /* data="ABCD" */
607  /* DATA chunk 2: type=0x00, flags=0x01, len=20 (16 hdr + 4 data) */
608  0x00, 0x01, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, /* TSN=2 */
609  0x00, 0x01, 0x00, 0x01, /* stream_id=1, stream_seq=1 */
610  0x00, 0x00, 0x00, 0x00, /* PPID=0 */
611  0x45, 0x46, 0x47, 0x48, /* data="EFGH" */
612  };
613  // clang-format on
614 
616  FAIL_IF_NULL(p);
617  ThreadVars tv;
619 
620  memset(&tv, 0, sizeof(ThreadVars));
621  memset(&dtv, 0, sizeof(DecodeThreadVars));
622 
624  DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
625  FAIL_IF_NOT(PacketIsSCTP(p));
626 
627  FAIL_IF(p->l4.vars.sctp.chunk_cnt != 2);
629  /* first DATA chunk: offset = 12 + 0 + 16 = 28, len = 4 */
630  FAIL_IF(p->l4.vars.sctp.data_offsets[0] != 28);
631  FAIL_IF(p->l4.vars.sctp.data_lens[0] != 4);
632  /* second DATA chunk: offset = 12 + 20 + 16 = 48, len = 4 */
633  FAIL_IF(p->l4.vars.sctp.data_offsets[1] != 48);
634  FAIL_IF(p->l4.vars.sctp.data_lens[1] != 4);
635  /* p->payload still points to first DATA's user data */
636  FAIL_IF(p->payload_len != 4);
637  FAIL_IF(memcmp(p->payload, "ABCD", 4) != 0);
638 
639  PacketFree(p);
640  FlowShutdown();
641  PASS;
642 }
643 
644 /** \test DATA chunk with chunk_len < 16 - malformed, payload_len must be 0 */
645 static int SCTPDecodeSmallDataChunkTest12(void)
646 {
647  // clang-format off
648  uint8_t raw_sctp[] = {
649  0x04, 0xd2, 0x00, 0x50, /* sport=1234, dport=80 */
650  0x00, 0x00, 0x00, 0x01, /* vtag=1 */
651  0x00, 0x00, 0x00, 0x00, /* checksum=0 */
652  /* DATA chunk: type=0x00, flags=0x00, len=8 (< 16, malformed) */
653  0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
654  0x00, /* 4 bytes of value (not enough for DATA header) */
655  };
656  // clang-format on
657 
659  FAIL_IF_NULL(p);
660  ThreadVars tv;
662 
663  memset(&tv, 0, sizeof(ThreadVars));
664  memset(&dtv, 0, sizeof(DecodeThreadVars));
665 
667  DecodeSCTP(&tv, &dtv, p, raw_sctp, sizeof(raw_sctp));
668  FAIL_IF_NOT(PacketIsSCTP(p));
669 
671  FAIL_IF(p->payload_len != 0);
672 
673  PacketFree(p);
674  FlowShutdown();
675  PASS;
676 }
677 
678 #endif /* UNITTESTS */
679 
681 {
682 #ifdef UNITTESTS
683  UtRegisterTest("SCTPDecodeValidInitTest01", SCTPDecodeValidInitTest01);
684  UtRegisterTest("SCTPDecodePktTooSmallTest02", SCTPDecodePktTooSmallTest02);
685  UtRegisterTest("SCTPDecodeChunkTooSmallTest03", SCTPDecodeChunkTooSmallTest03);
686  UtRegisterTest("SCTPDecodeChunkLenInvalidTest04", SCTPDecodeChunkLenInvalidTest04);
687  UtRegisterTest("SCTPDecodeInitNonZeroVtagTest05", SCTPDecodeInitNonZeroVtagTest05);
688  UtRegisterTest("SCTPDecodeMultiChunkTest06", SCTPDecodeMultiChunkTest06);
689  UtRegisterTest("SCTPDecodeInitNotAloneTest07", SCTPDecodeInitNotAloneTest07);
690  UtRegisterTest("SCTPDecodeDataPayloadTest08", SCTPDecodeDataPayloadTest08);
691  UtRegisterTest("SCTPDecodeNoDataPayloadTest09", SCTPDecodeNoDataPayloadTest09);
692  UtRegisterTest("SCTPDecodeDataOffsetTest10", SCTPDecodeDataOffsetTest10);
693  UtRegisterTest("SCTPDecodeMultiDataTest11", SCTPDecodeMultiDataTest11);
694  UtRegisterTest("SCTPDecodeSmallDataChunkTest12", SCTPDecodeSmallDataChunkTest12);
695 #endif
696 }
697 /**
698  * @}
699  */
ENGINE_SET_EVENT
#define ENGINE_SET_EVENT(p, e)
Definition: decode.h:1231
SCTP_CHUNK_TYPE_SHUTDOWN
#define SCTP_CHUNK_TYPE_SHUTDOWN
Definition: decode-sctp.h:55
Packet_::proto
uint8_t proto
Definition: decode.h:538
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:101
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:1244
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:620
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:413
p
Packet * p
Definition: fuzz_dataset.c:30
SCTPVars_::has_init
bool has_init
Definition: decode-sctp.h:84
DecodeThreadVars_::counter_sctp_init_ack
StatsCounterId counter_sctp_init_ack
Definition: decode.h:1031
TM_ECODE_FAILED
@ TM_ECODE_FAILED
Definition: tm-threads-common.h:82
DecodeThreadVars_::counter_sctp
StatsCounterId counter_sctp
Definition: decode.h:1029
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:621
DecodeSCTP
int DecodeSCTP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint16_t len)
Definition: decode-sctp.c:202
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:574
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:57
SCTP_GET_RAW_VTAG
#define SCTP_GET_RAW_VTAG(sctph)
Definition: decode-sctp.h:94
Packet_::sp
Port sp
Definition: decode.h:523
StatsCounterIncr
void StatsCounterIncr(StatsThreadContext *stats, StatsCounterId id)
Increments the local counter.
Definition: counters.c:163
PacketFree
void PacketFree(Packet *p)
Return a malloced packet.
Definition: decode.c:221
DecodeThreadVars_::counter_sctp_init
StatsCounterId counter_sctp_init
Definition: decode.h:1030
DecodeSCTPRegisterTests
void DecodeSCTPRegisterTests(void)
Definition: decode-sctp.c:680
Packet_
Definition: decode.h:516
Packet_::l4
struct PacketL4 l4
Definition: decode.h:616
SCTP_CHUNK_LEN_INVALID
@ SCTP_CHUNK_LEN_INVALID
Definition: decode-events.h:177
DecodeThreadVars_::counter_sctp_shutdown
StatsCounterId counter_sctp_shutdown
Definition: decode.h:1034
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:35
SCTP_DATA_CHUNK_HDR_LEN
#define SCTP_DATA_CHUNK_HDR_LEN
Definition: decode-sctp.h:45
PacketL4::L4Vars::sctp
SCTPVars sctp
Definition: decode.h:495
DecodeThreadVars_::counter_sctp_abort
StatsCounterId counter_sctp_abort
Definition: decode.h:1033
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:436
suricata-common.h
FlowShutdown
void FlowShutdown(void)
shutdown the flow engine
Definition: flow.c:718
decode-sctp.h
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:34
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:260
DecodeThreadVars_
Structure to hold thread specific data for all decode modules.
Definition: decode.h:995
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:1239
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:1273
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:531
ThreadVars_::stats
StatsThreadContext stats
Definition: threadvars.h:120
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:531
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:1032
SCTPVars_::data_chunk_cnt
uint8_t data_chunk_cnt
Definition: decode-sctp.h:83