@ -64,6 +64,7 @@ static int DecodeSCTPChunks(Packet *p, const uint8_t *pkt, uint16_t len)
bool has_init_ack = false ;
bool has_data = false ;
bool has_abort = false ;
uint8_t data_chunk_cnt = 0 ;
int ret = 0 ;
while ( offset < len ) {
@ -112,6 +113,15 @@ static int DecodeSCTPChunks(Packet *p, const uint8_t *pkt, uint16_t len)
has_init_ack = true ;
break ;
case SCTP_CHUNK_TYPE_DATA :
if ( data_chunk_cnt < SCTP_MAX_DATA_CHUNKS & & chunk_len > = SCTP_DATA_CHUNK_HDR_LEN ) {
p - > l4 . vars . sctp . data_offsets [ data_chunk_cnt ] =
( uint16_t ) ( SCTP_HEADER_LEN + offset + SCTP_DATA_CHUNK_HDR_LEN ) ;
p - > l4 . vars . sctp . data_lens [ data_chunk_cnt ] = chunk_len - SCTP_DATA_CHUNK_HDR_LEN ;
data_chunk_cnt + + ;
} else if ( data_chunk_cnt = = SCTP_MAX_DATA_CHUNKS & &
chunk_len > = SCTP_DATA_CHUNK_HDR_LEN ) {
ENGINE_SET_EVENT ( p , SCTP_TOO_MANY_DATA_CHUNKS ) ;
}
has_data = true ;
/* DATA chunks must not have vtag == 0 */
if ( vtag = = 0 ) {
@ -143,6 +153,7 @@ static int DecodeSCTPChunks(Packet *p, const uint8_t *pkt, uint16_t len)
p - > l4 . vars . sctp . hlen = ( uint16_t ) ( SCTP_HEADER_LEN + MIN ( offset , len ) ) ;
p - > l4 . vars . sctp . chunk_cnt = chunk_cnt ;
p - > l4 . vars . sctp . tracked_chunk_cnt = tracked_chunk_cnt ;
p - > l4 . vars . sctp . data_chunk_cnt = data_chunk_cnt ;
p - > l4 . vars . sctp . has_init = has_init ;
p - > l4 . vars . sctp . has_init_ack = has_init_ack ;
p - > l4 . vars . sctp . has_data = has_data ;
@ -173,6 +184,16 @@ static int DecodeSCTPPacket(ThreadVars *tv, Packet *p, const uint8_t *pkt, uint1
p - > payload_len = 0 ;
return - 1 ;
}
if ( p - > l4 . vars . sctp . data_chunk_cnt > 0 ) {
/* Point p->payload at the user data of the first DATA chunk,
* past the SCTP common header and the 16 - byte DATA chunk
* header . This is what content will inspect . */
p - > payload = ( uint8_t * ) pkt + p - > l4 . vars . sctp . data_offsets [ 0 ] ;
p - > payload_len = p - > l4 . vars . sctp . data_lens [ 0 ] ;
} else {
p - > payload_len = 0 ;
}
} else {
p - > l4 . vars . sctp . hlen = SCTP_HEADER_LEN ;
}
@ -463,6 +484,199 @@ static int SCTPDecodeInitNotAloneTest07(void)
PASS ;
}
/** \test DATA chunk payload extraction - p->payload points to user data */
static int SCTPDecodeDataPayloadTest08 ( void )
{
// clang-format off
uint8_t raw_sctp [ ] = {
0x04 , 0xd2 , 0x00 , 0x50 , /* sport=1234, dport=80 */
0x00 , 0x00 , 0x00 , 0x01 , /* vtag=1 */
0x00 , 0x00 , 0x00 , 0x00 , /* checksum=0 */
/* DATA chunk: type=0x00, flags=0x03, len=20 (16 hdr + 4 data) */
0x00 , 0x03 , 0x00 , 0x14 , 0x00 , 0x00 , 0x00 , 0x01 , /* TSN=1 */
0x00 , 0x01 , 0x00 , 0x00 , /* stream_id=1, stream_seq=0 */
0x00 , 0x00 , 0x00 , 0x00 , /* PPID=0 */
0x41 , 0x42 , 0x43 , 0x44 , /* data="ABCD" */
} ;
// clang-format on
Packet * p = PacketGetFromAlloc ( ) ;
FAIL_IF_NULL ( p ) ;
ThreadVars tv ;
DecodeThreadVars dtv ;
memset ( & tv , 0 , sizeof ( ThreadVars ) ) ;
memset ( & dtv , 0 , sizeof ( DecodeThreadVars ) ) ;
FlowInitConfig ( FLOW_QUIET ) ;
DecodeSCTP ( & tv , & dtv , p , raw_sctp , sizeof ( raw_sctp ) ) ;
FAIL_IF_NOT ( PacketIsSCTP ( p ) ) ;
FAIL_IF ( p - > payload_len ! = 4 ) ;
FAIL_IF ( memcmp ( p - > payload , " ABCD " , 4 ) ! = 0 ) ;
PacketFree ( p ) ;
FlowShutdown ( ) ;
PASS ;
}
/** \test INIT-only packet - payload_len must be 0 (no application data) */
static int SCTPDecodeNoDataPayloadTest09 ( void )
{
// clang-format off
uint8_t raw_sctp [ ] = {
0x04 , 0xd2 , 0x00 , 0x50 , /* sport=1234, dport=80 */
0x00 , 0x00 , 0x00 , 0x00 , /* vtag=0 */
0x00 , 0x00 , 0x00 , 0x00 , /* checksum=0 */
0x01 , 0x00 , 0x00 , 0x14 , /* chunk: INIT, flags=0, len=20 */
0x00 , 0x00 , 0x00 , 0x01 , /* initiate_tag=1 */
0x00 , 0x01 , 0x00 , 0x00 , /* a_rwnd=65536 */
0x00 , 0x01 , 0x00 , 0x01 , /* num_outbound=1, num_inbound=1 */
0x00 , 0x00 , 0x00 , 0x01 , /* initial_tsn=1 */
} ;
// clang-format on
Packet * p = PacketGetFromAlloc ( ) ;
FAIL_IF_NULL ( p ) ;
ThreadVars tv ;
DecodeThreadVars dtv ;
memset ( & tv , 0 , sizeof ( ThreadVars ) ) ;
memset ( & dtv , 0 , sizeof ( DecodeThreadVars ) ) ;
FlowInitConfig ( FLOW_QUIET ) ;
DecodeSCTP ( & tv , & dtv , p , raw_sctp , sizeof ( raw_sctp ) ) ;
FAIL_IF_NOT ( PacketIsSCTP ( p ) ) ;
FAIL_IF ( p - > payload_len ! = 0 ) ;
PacketFree ( p ) ;
FlowShutdown ( ) ;
PASS ;
}
/** \test Verify data_offset and data_len in SCTPVars */
static int SCTPDecodeDataOffsetTest10 ( void )
{
// clang-format off
uint8_t raw_sctp [ ] = {
0x04 , 0xd2 , 0x00 , 0x50 , /* sport=1234, dport=80 */
0x00 , 0x00 , 0x00 , 0x01 , /* vtag=1 */
0x00 , 0x00 , 0x00 , 0x00 , /* checksum=0 */
/* DATA chunk: type=0x00, flags=0x03, len=20 (16 hdr + 4 data) */
0x00 , 0x03 , 0x00 , 0x14 , 0x00 , 0x00 , 0x00 , 0x01 , /* TSN=1 */
0x00 , 0x01 , 0x00 , 0x00 , /* stream_id=1, stream_seq=0 */
0x00 , 0x00 , 0x00 , 0x00 , /* PPID=0 */
0x41 , 0x42 , 0x43 , 0x44 , /* data="ABCD" */
} ;
// clang-format on
Packet * p = PacketGetFromAlloc ( ) ;
FAIL_IF_NULL ( p ) ;
ThreadVars tv ;
DecodeThreadVars dtv ;
memset ( & tv , 0 , sizeof ( ThreadVars ) ) ;
memset ( & dtv , 0 , sizeof ( DecodeThreadVars ) ) ;
FlowInitConfig ( FLOW_QUIET ) ;
DecodeSCTP ( & tv , & dtv , p , raw_sctp , sizeof ( raw_sctp ) ) ;
FAIL_IF_NOT ( PacketIsSCTP ( p ) ) ;
/* data_offsets[0] = SCTP_HEADER_LEN(12) + chunk_offset(0) + DATA_CHUNK_HDR_LEN(16) = 28 */
FAIL_IF ( p - > l4 . vars . sctp . data_chunk_cnt ! = 1 ) ;
FAIL_IF ( p - > l4 . vars . sctp . data_offsets [ 0 ] ! = 28 ) ;
FAIL_IF ( p - > l4 . vars . sctp . data_lens [ 0 ] ! = 4 ) ;
PacketFree ( p ) ;
FlowShutdown ( ) ;
PASS ;
}
/** \test Two DATA chunks - both tracked, p->payload is first DATA's user data */
static int SCTPDecodeMultiDataTest11 ( void )
{
// clang-format off
uint8_t raw_sctp [ ] = {
0x04 , 0xd2 , 0x00 , 0x50 , /* sport=1234, dport=80 */
0x00 , 0x00 , 0x00 , 0x01 , /* vtag=1 */
0x00 , 0x00 , 0x00 , 0x00 , /* checksum=0 */
/* DATA chunk 1: type=0x00, flags=0x02, len=20 (16 hdr + 4 data) */
0x00 , 0x02 , 0x00 , 0x14 , 0x00 , 0x00 , 0x00 , 0x01 , /* TSN=1 */
0x00 , 0x01 , 0x00 , 0x00 , /* stream_id=1, stream_seq=0 */
0x00 , 0x00 , 0x00 , 0x00 , /* PPID=0 */
0x41 , 0x42 , 0x43 , 0x44 , /* data="ABCD" */
/* DATA chunk 2: type=0x00, flags=0x01, len=20 (16 hdr + 4 data) */
0x00 , 0x01 , 0x00 , 0x14 , 0x00 , 0x00 , 0x00 , 0x02 , /* TSN=2 */
0x00 , 0x01 , 0x00 , 0x01 , /* stream_id=1, stream_seq=1 */
0x00 , 0x00 , 0x00 , 0x00 , /* PPID=0 */
0x45 , 0x46 , 0x47 , 0x48 , /* data="EFGH" */
} ;
// clang-format on
Packet * p = PacketGetFromAlloc ( ) ;
FAIL_IF_NULL ( p ) ;
ThreadVars tv ;
DecodeThreadVars dtv ;
memset ( & tv , 0 , sizeof ( ThreadVars ) ) ;
memset ( & dtv , 0 , sizeof ( DecodeThreadVars ) ) ;
FlowInitConfig ( FLOW_QUIET ) ;
DecodeSCTP ( & tv , & dtv , p , raw_sctp , sizeof ( raw_sctp ) ) ;
FAIL_IF_NOT ( PacketIsSCTP ( p ) ) ;
FAIL_IF ( p - > l4 . vars . sctp . chunk_cnt ! = 2 ) ;
FAIL_IF ( p - > l4 . vars . sctp . data_chunk_cnt ! = 2 ) ;
/* first DATA chunk: offset = 12 + 0 + 16 = 28, len = 4 */
FAIL_IF ( p - > l4 . vars . sctp . data_offsets [ 0 ] ! = 28 ) ;
FAIL_IF ( p - > l4 . vars . sctp . data_lens [ 0 ] ! = 4 ) ;
/* second DATA chunk: offset = 12 + 20 + 16 = 48, len = 4 */
FAIL_IF ( p - > l4 . vars . sctp . data_offsets [ 1 ] ! = 48 ) ;
FAIL_IF ( p - > l4 . vars . sctp . data_lens [ 1 ] ! = 4 ) ;
/* p->payload still points to first DATA's user data */
FAIL_IF ( p - > payload_len ! = 4 ) ;
FAIL_IF ( memcmp ( p - > payload , " ABCD " , 4 ) ! = 0 ) ;
PacketFree ( p ) ;
FlowShutdown ( ) ;
PASS ;
}
/** \test DATA chunk with chunk_len < 16 - malformed, payload_len must be 0 */
static int SCTPDecodeSmallDataChunkTest12 ( void )
{
// clang-format off
uint8_t raw_sctp [ ] = {
0x04 , 0xd2 , 0x00 , 0x50 , /* sport=1234, dport=80 */
0x00 , 0x00 , 0x00 , 0x01 , /* vtag=1 */
0x00 , 0x00 , 0x00 , 0x00 , /* checksum=0 */
/* DATA chunk: type=0x00, flags=0x00, len=8 (< 16, malformed) */
0x00 , 0x00 , 0x00 , 0x08 , 0x00 , 0x00 , 0x00 ,
0x00 , /* 4 bytes of value (not enough for DATA header) */
} ;
// clang-format on
Packet * p = PacketGetFromAlloc ( ) ;
FAIL_IF_NULL ( p ) ;
ThreadVars tv ;
DecodeThreadVars dtv ;
memset ( & tv , 0 , sizeof ( ThreadVars ) ) ;
memset ( & dtv , 0 , sizeof ( DecodeThreadVars ) ) ;
FlowInitConfig ( FLOW_QUIET ) ;
DecodeSCTP ( & tv , & dtv , p , raw_sctp , sizeof ( raw_sctp ) ) ;
FAIL_IF_NOT ( PacketIsSCTP ( p ) ) ;
FAIL_IF ( p - > l4 . vars . sctp . data_chunk_cnt ! = 0 ) ;
FAIL_IF ( p - > payload_len ! = 0 ) ;
PacketFree ( p ) ;
FlowShutdown ( ) ;
PASS ;
}
# endif /* UNITTESTS */
void DecodeSCTPRegisterTests ( void )
@ -475,6 +689,11 @@ void DecodeSCTPRegisterTests(void)
UtRegisterTest ( " SCTPDecodeInitNonZeroVtagTest05 " , SCTPDecodeInitNonZeroVtagTest05 ) ;
UtRegisterTest ( " SCTPDecodeMultiChunkTest06 " , SCTPDecodeMultiChunkTest06 ) ;
UtRegisterTest ( " SCTPDecodeInitNotAloneTest07 " , SCTPDecodeInitNotAloneTest07 ) ;
UtRegisterTest ( " SCTPDecodeDataPayloadTest08 " , SCTPDecodeDataPayloadTest08 ) ;
UtRegisterTest ( " SCTPDecodeNoDataPayloadTest09 " , SCTPDecodeNoDataPayloadTest09 ) ;
UtRegisterTest ( " SCTPDecodeDataOffsetTest10 " , SCTPDecodeDataOffsetTest10 ) ;
UtRegisterTest ( " SCTPDecodeMultiDataTest11 " , SCTPDecodeMultiDataTest11 ) ;
UtRegisterTest ( " SCTPDecodeSmallDataChunkTest12 " , SCTPDecodeSmallDataChunkTest12 ) ;
# endif
}
/**