decode/sctp: set p->payload to data chunk

Track the first DATA chunk's data offset and length during chunk iteration,
then reassign p->payload to point at the user data.

When no DATA chunk is present (INIT, SACK, HEARTBEAT, etc.),
payload_len is set to 0 since there is no application data.

Ticket #4251
pull/15601/head
Giuseppe Longo 7 months ago committed by Victor Julien
parent ede1bce923
commit 1413b082d3

@ -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
}
/**

@ -38,6 +38,12 @@
* though in practice most packets contain only a few chunks. */
#define SCTP_MAX_TRACKED_CHUNKS 16
/** max number of DATA chunk payloads tracked per packet */
#define SCTP_MAX_DATA_CHUNKS 16
/** DATA chunk overhead before user data (chunk hdr + TSN + SID + SSN + PPID) */
#define SCTP_DATA_CHUNK_HDR_LEN 16
/* SCTP chunk types (RFC 4960 sec 3.2) */
#define SCTP_CHUNK_TYPE_DATA 0x00
#define SCTP_CHUNK_TYPE_INIT 0x01
@ -74,10 +80,13 @@ typedef struct SCTPVars_ {
uint8_t chunk_cnt; /**< number of chunks parsed */
uint8_t tracked_chunk_cnt; /**< number of chunks tracked (capped at SCTP_MAX_TRACKED_CHUNKS) */
uint8_t chunk_types[SCTP_MAX_TRACKED_CHUNKS]; /**< types of first N chunks */
uint8_t data_chunk_cnt; /**< number of DATA chunk payloads tracked */
bool has_init : 1;
bool has_init_ack : 1;
bool has_data : 1;
bool has_abort : 1;
uint16_t data_offsets[SCTP_MAX_DATA_CHUNKS]; /**< offsets of DATA user data from L4 start */
uint16_t data_lens[SCTP_MAX_DATA_CHUNKS]; /**< lengths of DATA user data */
} SCTPVars;
#define SCTP_GET_RAW_SRC_PORT(sctph) SCNtohs((sctph)->sh_sport)

Loading…
Cancel
Save