|
|
|
@ -2846,157 +2846,33 @@ static void GetSessionSize(TcpSession *ssn, Packet *p)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
typedef struct ReassembleData_ {
|
|
|
|
* \brief Update the stream reassembly upon receiving an ACK packet.
|
|
|
|
uint32_t ra_base_seq;
|
|
|
|
*
|
|
|
|
uint32_t data_len;
|
|
|
|
* Stream is in the opposite direction of the packet, as the ACK-packet
|
|
|
|
|
|
|
|
* is ACK'ing the stream.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* One of the utilities call by this function AppLayerHandleTCPData(),
|
|
|
|
|
|
|
|
* has a feature where it will call this very same function for the
|
|
|
|
|
|
|
|
* stream opposing the stream it is called with. This shouldn't cause
|
|
|
|
|
|
|
|
* any issues, since processing of each stream is independent of the
|
|
|
|
|
|
|
|
* other stream.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* \todo this function is too long, we need to break it up. It needs it BAD
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
|
|
|
|
|
|
|
|
TcpSession *ssn, TcpStream *stream,
|
|
|
|
|
|
|
|
Packet *p)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
SCEnter();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* this function can be directly called by app layer protocol
|
|
|
|
|
|
|
|
* detection. */
|
|
|
|
|
|
|
|
if (stream->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) {
|
|
|
|
|
|
|
|
SCLogDebug("stream no reassembly flag set. Mostly called via "
|
|
|
|
|
|
|
|
"app proto detection.");
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t flags = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SCLogDebug("stream->seg_list %p", stream->seg_list);
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
|
|
|
PrintList(stream->seg_list);
|
|
|
|
|
|
|
|
GetSessionSize(ssn, p);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* if no segments are in the list or all are already processed,
|
|
|
|
|
|
|
|
* and state is beyond established, we send an empty msg */
|
|
|
|
|
|
|
|
TcpSegment *seg_tail = stream->seg_list_tail;
|
|
|
|
|
|
|
|
if (seg_tail == NULL ||
|
|
|
|
|
|
|
|
(seg_tail->flags & SEGMENTTCP_FLAG_APPLAYER_PROCESSED))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
/* send an empty EOF msg if we have no segments but TCP state
|
|
|
|
|
|
|
|
* is beyond ESTABLISHED */
|
|
|
|
|
|
|
|
if (ssn->state >= TCP_CLOSING || (p->flags & PKT_PSEUDO_STREAM_END)) {
|
|
|
|
|
|
|
|
SCLogDebug("sending empty eof message");
|
|
|
|
|
|
|
|
/* send EOF to app layer */
|
|
|
|
|
|
|
|
STREAM_SET_FLAGS(ssn, stream, p, flags);
|
|
|
|
|
|
|
|
AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
|
|
|
|
|
|
|
|
NULL, 0, flags);
|
|
|
|
|
|
|
|
AppLayerProfilingStore(ra_ctx->app_tctx, p);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* no segments, nothing to do */
|
|
|
|
|
|
|
|
if (stream->seg_list == NULL) {
|
|
|
|
|
|
|
|
SCLogDebug("no segments in the list to reassemble");
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (stream->flags & STREAMTCP_STREAM_FLAG_GAP) {
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* stream->ra_app_base_seq remains at stream->isn until protocol is
|
|
|
|
|
|
|
|
* detected. */
|
|
|
|
|
|
|
|
uint32_t ra_base_seq = stream->ra_app_base_seq;
|
|
|
|
|
|
|
|
uint8_t data[4096];
|
|
|
|
uint8_t data[4096];
|
|
|
|
uint32_t data_len = 0;
|
|
|
|
int partial; /* last segment was processed only partially */
|
|
|
|
uint16_t payload_offset = 0;
|
|
|
|
} ReassembleData;
|
|
|
|
uint16_t payload_len = 0;
|
|
|
|
|
|
|
|
uint32_t next_seq = ra_base_seq + 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SCLogDebug("ra_base_seq %"PRIu32", last_ack %"PRIu32", next_seq %"PRIu32,
|
|
|
|
|
|
|
|
ra_base_seq, stream->last_ack, next_seq);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* loop through the segments and fill one or more msgs */
|
|
|
|
|
|
|
|
TcpSegment *seg = stream->seg_list;
|
|
|
|
|
|
|
|
SCLogDebug("pre-loop seg %p", seg);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Check if we have a gap at the start of the list. If last_ack is
|
|
|
|
|
|
|
|
* bigger than the list start and the list start is bigger than
|
|
|
|
|
|
|
|
* next_seq, we know we are missing data that has been ack'd. That
|
|
|
|
|
|
|
|
* won't get retransmitted, so it's a data gap.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!(p->flow->flags & FLOW_NO_APPLAYER_INSPECTION)) {
|
|
|
|
|
|
|
|
if (SEQ_GT(seg->seq, next_seq) && SEQ_LT(seg->seq, stream->last_ack)) {
|
|
|
|
|
|
|
|
/* send gap signal */
|
|
|
|
|
|
|
|
STREAM_SET_FLAGS(ssn, stream, p, flags);
|
|
|
|
|
|
|
|
AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
|
|
|
|
|
|
|
|
NULL, 0, flags|STREAM_GAP);
|
|
|
|
|
|
|
|
AppLayerProfilingStore(ra_ctx->app_tctx, p);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* set a GAP flag and make sure not bothering this stream anymore */
|
|
|
|
|
|
|
|
SCLogDebug("STREAMTCP_STREAM_FLAG_GAP set");
|
|
|
|
|
|
|
|
stream->flags |= STREAMTCP_STREAM_FLAG_GAP;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
StreamTcpSetEvent(p, STREAM_REASSEMBLY_SEQ_GAP);
|
|
|
|
|
|
|
|
SCPerfCounterIncr(ra_ctx->counter_tcp_reass_gap, tv->sc_perf_pca);
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
|
|
|
dbg_app_layer_gap++;
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (; seg != NULL && SEQ_LT(seg->seq, stream->last_ack);)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
SCLogDebug("seg %p, SEQ %"PRIu32", LEN %"PRIu16", SUM %"PRIu32,
|
|
|
|
|
|
|
|
seg, seg->seq, seg->payload_len,
|
|
|
|
|
|
|
|
(uint32_t)(seg->seq + seg->payload_len));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (p->flow->flags & FLOW_NO_APPLAYER_INSPECTION) {
|
|
|
|
|
|
|
|
SCLogDebug("FLOW_NO_APPLAYER_INSPECTION set, breaking out");
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (StreamTcpReturnSegmentCheck(p->flow, ssn, stream, seg) == 1) {
|
|
|
|
|
|
|
|
SCLogDebug("removing segment");
|
|
|
|
|
|
|
|
TcpSegment *next_seg = seg->next;
|
|
|
|
|
|
|
|
StreamTcpRemoveSegmentFromStream(stream, seg);
|
|
|
|
|
|
|
|
StreamTcpSegmentReturntoPool(seg);
|
|
|
|
|
|
|
|
seg = next_seg;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
} else if (StreamTcpAppLayerSegmentProcessed(stream, seg)) {
|
|
|
|
|
|
|
|
TcpSegment *next_seg = seg->next;
|
|
|
|
|
|
|
|
seg = next_seg;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int DoHandleGap(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
|
|
|
|
|
|
|
|
TcpSession *ssn, TcpStream *stream, TcpSegment *seg, ReassembleData *rd,
|
|
|
|
|
|
|
|
Packet *p, uint8_t flags, uint32_t next_seq)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (unlikely(SEQ_GT(seg->seq, next_seq))) {
|
|
|
|
/* we've run into a sequence gap */
|
|
|
|
/* we've run into a sequence gap */
|
|
|
|
if (SEQ_GT(seg->seq, next_seq)) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* first, pass on data before the gap */
|
|
|
|
/* first, pass on data before the gap */
|
|
|
|
if (data_len > 0) {
|
|
|
|
if (rd->data_len > 0) {
|
|
|
|
SCLogDebug("pre GAP data");
|
|
|
|
SCLogDebug("pre GAP data");
|
|
|
|
|
|
|
|
|
|
|
|
STREAM_SET_FLAGS(ssn, stream, p, flags);
|
|
|
|
STREAM_SET_FLAGS(ssn, stream, p, flags);
|
|
|
|
|
|
|
|
|
|
|
|
/* process what we have so far */
|
|
|
|
/* process what we have so far */
|
|
|
|
AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
|
|
|
|
AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
|
|
|
|
data, data_len, flags);
|
|
|
|
rd->data, rd->data_len, flags);
|
|
|
|
AppLayerProfilingStore(ra_ctx->app_tctx, p);
|
|
|
|
AppLayerProfilingStore(ra_ctx->app_tctx, p);
|
|
|
|
data_len = 0;
|
|
|
|
rd->data_len = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* see what the length of the gap is, gap length is seg->seq -
|
|
|
|
|
|
|
|
* (ra_base_seq +1) */
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
#ifdef DEBUG
|
|
|
|
uint32_t gap_len = seg->seq - next_seq;
|
|
|
|
uint32_t gap_len = seg->seq - next_seq;
|
|
|
|
SCLogDebug("expected next_seq %" PRIu32 ", got %" PRIu32 " , "
|
|
|
|
SCLogDebug("expected next_seq %" PRIu32 ", got %" PRIu32 " , "
|
|
|
|
@ -3007,9 +2883,9 @@ int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
|
|
|
|
* IDS should advance it's ra_base_seq and should not consider this
|
|
|
|
* IDS should advance it's ra_base_seq and should not consider this
|
|
|
|
* packet any longer, even if it is retransmitted, as end host will
|
|
|
|
* packet any longer, even if it is retransmitted, as end host will
|
|
|
|
* drop it anyway */
|
|
|
|
* drop it anyway */
|
|
|
|
ra_base_seq = seg->seq - 1;
|
|
|
|
rd->ra_base_seq = seg->seq - 1;
|
|
|
|
|
|
|
|
|
|
|
|
/* send gap signal */
|
|
|
|
/* send gap "signal" */
|
|
|
|
STREAM_SET_FLAGS(ssn, stream, p, flags);
|
|
|
|
STREAM_SET_FLAGS(ssn, stream, p, flags);
|
|
|
|
AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
|
|
|
|
AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
|
|
|
|
NULL, 0, flags|STREAM_GAP);
|
|
|
|
NULL, 0, flags|STREAM_GAP);
|
|
|
|
@ -3024,31 +2900,41 @@ int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
|
|
|
|
#ifdef DEBUG
|
|
|
|
#ifdef DEBUG
|
|
|
|
dbg_app_layer_gap++;
|
|
|
|
dbg_app_layer_gap++;
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int partial = FALSE;
|
|
|
|
static inline int DoReassemble(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
|
|
|
|
|
|
|
|
TcpSession *ssn, TcpStream *stream, TcpSegment *seg, ReassembleData *rd,
|
|
|
|
|
|
|
|
Packet *p, uint8_t flags)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
uint16_t payload_offset = 0;
|
|
|
|
|
|
|
|
uint16_t payload_len = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* start clean */
|
|
|
|
|
|
|
|
rd->partial = FALSE;
|
|
|
|
|
|
|
|
|
|
|
|
/* if the segment ends beyond ra_base_seq we need to consider it */
|
|
|
|
/* if the segment ends beyond ra_base_seq we need to consider it */
|
|
|
|
if (SEQ_GT((seg->seq + seg->payload_len), ra_base_seq+1)) {
|
|
|
|
if (SEQ_GT((seg->seq + seg->payload_len), rd->ra_base_seq+1)) {
|
|
|
|
SCLogDebug("seg->seq %" PRIu32 ", seg->payload_len %" PRIu32 ", "
|
|
|
|
SCLogDebug("seg->seq %" PRIu32 ", seg->payload_len %" PRIu32 ", "
|
|
|
|
"ra_base_seq %" PRIu32 ", last_ack %"PRIu32, seg->seq,
|
|
|
|
"ra_base_seq %" PRIu32 ", last_ack %"PRIu32, seg->seq,
|
|
|
|
seg->payload_len, ra_base_seq, stream->last_ack);
|
|
|
|
seg->payload_len, rd->ra_base_seq, stream->last_ack);
|
|
|
|
|
|
|
|
|
|
|
|
/* handle segments partly before ra_base_seq */
|
|
|
|
/* handle segments partly before ra_base_seq */
|
|
|
|
if (SEQ_GT(ra_base_seq, seg->seq)) {
|
|
|
|
if (SEQ_GT(rd->ra_base_seq, seg->seq)) {
|
|
|
|
payload_offset = (ra_base_seq + 1) - seg->seq;
|
|
|
|
payload_offset = (rd->ra_base_seq + 1) - seg->seq;
|
|
|
|
SCLogDebug("payload_offset %u", payload_offset);
|
|
|
|
SCLogDebug("payload_offset %u", payload_offset);
|
|
|
|
|
|
|
|
|
|
|
|
if (SEQ_LT(stream->last_ack, (seg->seq + seg->payload_len))) {
|
|
|
|
if (SEQ_LT(stream->last_ack, (seg->seq + seg->payload_len))) {
|
|
|
|
if (SEQ_LT(stream->last_ack, (ra_base_seq + 1))) {
|
|
|
|
if (SEQ_LT(stream->last_ack, (rd->ra_base_seq + 1))) {
|
|
|
|
payload_len = (stream->last_ack - seg->seq);
|
|
|
|
payload_len = (stream->last_ack - seg->seq);
|
|
|
|
SCLogDebug("payload_len %u", payload_len);
|
|
|
|
SCLogDebug("payload_len %u", payload_len);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
payload_len = (stream->last_ack - seg->seq) - payload_offset;
|
|
|
|
payload_len = (stream->last_ack - seg->seq) - payload_offset;
|
|
|
|
SCLogDebug("payload_len %u", payload_len);
|
|
|
|
SCLogDebug("payload_len %u", payload_len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
partial = TRUE;
|
|
|
|
rd->partial = TRUE;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
payload_len = seg->payload_len - payload_offset;
|
|
|
|
payload_len = seg->payload_len - payload_offset;
|
|
|
|
SCLogDebug("payload_len %u", payload_len);
|
|
|
|
SCLogDebug("payload_len %u", payload_len);
|
|
|
|
@ -3065,7 +2951,7 @@ int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
|
|
|
|
payload_len = stream->last_ack - seg->seq;
|
|
|
|
payload_len = stream->last_ack - seg->seq;
|
|
|
|
SCLogDebug("payload_len %u", payload_len);
|
|
|
|
SCLogDebug("payload_len %u", payload_len);
|
|
|
|
|
|
|
|
|
|
|
|
partial = TRUE;
|
|
|
|
rd->partial = TRUE;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
payload_len = seg->payload_len;
|
|
|
|
payload_len = seg->payload_len;
|
|
|
|
SCLogDebug("payload_len %u", payload_len);
|
|
|
|
SCLogDebug("payload_len %u", payload_len);
|
|
|
|
@ -3077,38 +2963,38 @@ int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
|
|
|
|
|
|
|
|
|
|
|
|
if (payload_len == 0) {
|
|
|
|
if (payload_len == 0) {
|
|
|
|
SCLogDebug("no payload_len, so bail out");
|
|
|
|
SCLogDebug("no payload_len, so bail out");
|
|
|
|
break;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* copy the data into the smsg */
|
|
|
|
/* copy the data into the smsg */
|
|
|
|
uint16_t copy_size = sizeof(data) - data_len;
|
|
|
|
uint16_t copy_size = sizeof(rd->data) - rd->data_len;
|
|
|
|
if (copy_size > payload_len) {
|
|
|
|
if (copy_size > payload_len) {
|
|
|
|
copy_size = payload_len;
|
|
|
|
copy_size = payload_len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (SCLogDebugEnabled()) {
|
|
|
|
if (SCLogDebugEnabled()) {
|
|
|
|
BUG_ON(copy_size > sizeof(data));
|
|
|
|
BUG_ON(copy_size > sizeof(rd->data));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SCLogDebug("copy_size is %"PRIu16"", copy_size);
|
|
|
|
SCLogDebug("copy_size is %"PRIu16"", copy_size);
|
|
|
|
memcpy(data + data_len, seg->payload + payload_offset, copy_size);
|
|
|
|
memcpy(rd->data + rd->data_len, seg->payload + payload_offset, copy_size);
|
|
|
|
data_len += copy_size;
|
|
|
|
rd->data_len += copy_size;
|
|
|
|
ra_base_seq += copy_size;
|
|
|
|
rd->ra_base_seq += copy_size;
|
|
|
|
SCLogDebug("ra_base_seq %"PRIu32", data_len %"PRIu32, ra_base_seq, data_len);
|
|
|
|
SCLogDebug("ra_base_seq %"PRIu32", data_len %"PRIu32, rd->ra_base_seq, rd->data_len);
|
|
|
|
|
|
|
|
|
|
|
|
/* queue the smsg if it's full */
|
|
|
|
/* queue the smsg if it's full */
|
|
|
|
if (data_len == sizeof(data)) {
|
|
|
|
if (rd->data_len == sizeof(rd->data)) {
|
|
|
|
/* process what we have so far */
|
|
|
|
/* process what we have so far */
|
|
|
|
STREAM_SET_FLAGS(ssn, stream, p, flags);
|
|
|
|
STREAM_SET_FLAGS(ssn, stream, p, flags);
|
|
|
|
BUG_ON(data_len > sizeof(data));
|
|
|
|
// BUG_ON(rd->data_len > sizeof(rd->data));
|
|
|
|
AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
|
|
|
|
AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
|
|
|
|
data, data_len, flags);
|
|
|
|
rd->data, rd->data_len, flags);
|
|
|
|
AppLayerProfilingStore(ra_ctx->app_tctx, p);
|
|
|
|
AppLayerProfilingStore(ra_ctx->app_tctx, p);
|
|
|
|
data_len = 0;
|
|
|
|
rd->data_len = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/* if after the first data chunk we have no alproto yet,
|
|
|
|
/* if after the first data chunk we have no alproto yet,
|
|
|
|
* there is no point in continueing here. */
|
|
|
|
* there is no point in continueing here. */
|
|
|
|
if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream)) {
|
|
|
|
if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream)) {
|
|
|
|
SCLogDebug("no alproto after first data chunk");
|
|
|
|
SCLogDebug("no alproto after first data chunk");
|
|
|
|
break;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -3133,42 +3019,42 @@ int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
|
|
|
|
while (segment_done == FALSE) {
|
|
|
|
while (segment_done == FALSE) {
|
|
|
|
SCLogDebug("new msg at offset %" PRIu32 ", payload_len "
|
|
|
|
SCLogDebug("new msg at offset %" PRIu32 ", payload_len "
|
|
|
|
"%" PRIu32 "", payload_offset, payload_len);
|
|
|
|
"%" PRIu32 "", payload_offset, payload_len);
|
|
|
|
data_len = 0;
|
|
|
|
rd->data_len = 0;
|
|
|
|
|
|
|
|
|
|
|
|
copy_size = sizeof(data) - data_len;
|
|
|
|
copy_size = sizeof(rd->data) - rd->data_len;
|
|
|
|
if (copy_size > (seg->payload_len - payload_offset)) {
|
|
|
|
if (copy_size > (seg->payload_len - payload_offset)) {
|
|
|
|
copy_size = (seg->payload_len - payload_offset);
|
|
|
|
copy_size = (seg->payload_len - payload_offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (SCLogDebugEnabled()) {
|
|
|
|
if (SCLogDebugEnabled()) {
|
|
|
|
BUG_ON(copy_size > sizeof(data));
|
|
|
|
BUG_ON(copy_size > sizeof(rd->data));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SCLogDebug("copy payload_offset %" PRIu32 ", data_len "
|
|
|
|
SCLogDebug("copy payload_offset %" PRIu32 ", data_len "
|
|
|
|
"%" PRIu32 ", copy_size %" PRIu32 "",
|
|
|
|
"%" PRIu32 ", copy_size %" PRIu32 "",
|
|
|
|
payload_offset, data_len, copy_size);
|
|
|
|
payload_offset, rd->data_len, copy_size);
|
|
|
|
memcpy(data + data_len, seg->payload +
|
|
|
|
memcpy(rd->data + rd->data_len, seg->payload +
|
|
|
|
payload_offset, copy_size);
|
|
|
|
payload_offset, copy_size);
|
|
|
|
data_len += copy_size;
|
|
|
|
rd->data_len += copy_size;
|
|
|
|
ra_base_seq += copy_size;
|
|
|
|
rd->ra_base_seq += copy_size;
|
|
|
|
SCLogDebug("ra_base_seq %"PRIu32, ra_base_seq);
|
|
|
|
SCLogDebug("ra_base_seq %"PRIu32, rd->ra_base_seq);
|
|
|
|
SCLogDebug("copied payload_offset %" PRIu32 ", "
|
|
|
|
SCLogDebug("copied payload_offset %" PRIu32 ", "
|
|
|
|
"data_len %" PRIu32 ", copy_size %" PRIu32 "",
|
|
|
|
"data_len %" PRIu32 ", copy_size %" PRIu32 "",
|
|
|
|
payload_offset, data_len, copy_size);
|
|
|
|
payload_offset, rd->data_len, copy_size);
|
|
|
|
|
|
|
|
|
|
|
|
if (data_len == sizeof(data)) {
|
|
|
|
if (rd->data_len == sizeof(rd->data)) {
|
|
|
|
/* process what we have so far */
|
|
|
|
/* process what we have so far */
|
|
|
|
STREAM_SET_FLAGS(ssn, stream, p, flags);
|
|
|
|
STREAM_SET_FLAGS(ssn, stream, p, flags);
|
|
|
|
BUG_ON(data_len > sizeof(data));
|
|
|
|
// BUG_ON(rd->data_len > sizeof(rd->data));
|
|
|
|
AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
|
|
|
|
AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
|
|
|
|
data, data_len, flags);
|
|
|
|
rd->data, rd->data_len, flags);
|
|
|
|
AppLayerProfilingStore(ra_ctx->app_tctx, p);
|
|
|
|
AppLayerProfilingStore(ra_ctx->app_tctx, p);
|
|
|
|
data_len = 0;
|
|
|
|
rd->data_len = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/* if after the first data chunk we have no alproto yet,
|
|
|
|
/* if after the first data chunk we have no alproto yet,
|
|
|
|
* there is no point in continueing here. */
|
|
|
|
* there is no point in continueing here. */
|
|
|
|
if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream)) {
|
|
|
|
if (!StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream)) {
|
|
|
|
SCLogDebug("no alproto after first data chunk");
|
|
|
|
SCLogDebug("no alproto after first data chunk");
|
|
|
|
break;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -3188,10 +3074,153 @@ int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* \brief Update the stream reassembly upon receiving an ACK packet.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* Stream is in the opposite direction of the packet, as the ACK-packet
|
|
|
|
|
|
|
|
* is ACK'ing the stream.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* One of the utilities call by this function AppLayerHandleTCPData(),
|
|
|
|
|
|
|
|
* has a feature where it will call this very same function for the
|
|
|
|
|
|
|
|
* stream opposing the stream it is called with. This shouldn't cause
|
|
|
|
|
|
|
|
* any issues, since processing of each stream is independent of the
|
|
|
|
|
|
|
|
* other stream.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* \todo this function is too long, we need to break it up. It needs it BAD
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
|
|
|
|
|
|
|
|
TcpSession *ssn, TcpStream *stream,
|
|
|
|
|
|
|
|
Packet *p)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
SCEnter();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* this function can be directly called by app layer protocol
|
|
|
|
|
|
|
|
* detection. */
|
|
|
|
|
|
|
|
if (stream->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) {
|
|
|
|
|
|
|
|
SCLogDebug("stream no reassembly flag set. Mostly called via "
|
|
|
|
|
|
|
|
"app proto detection.");
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t flags = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SCLogDebug("stream->seg_list %p", stream->seg_list);
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
|
|
|
PrintList(stream->seg_list);
|
|
|
|
|
|
|
|
GetSessionSize(ssn, p);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* if no segments are in the list or all are already processed,
|
|
|
|
|
|
|
|
* and state is beyond established, we send an empty msg */
|
|
|
|
|
|
|
|
TcpSegment *seg_tail = stream->seg_list_tail;
|
|
|
|
|
|
|
|
if (seg_tail == NULL ||
|
|
|
|
|
|
|
|
(seg_tail->flags & SEGMENTTCP_FLAG_APPLAYER_PROCESSED))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
/* send an empty EOF msg if we have no segments but TCP state
|
|
|
|
|
|
|
|
* is beyond ESTABLISHED */
|
|
|
|
|
|
|
|
if (ssn->state >= TCP_CLOSING || (p->flags & PKT_PSEUDO_STREAM_END)) {
|
|
|
|
|
|
|
|
SCLogDebug("sending empty eof message");
|
|
|
|
|
|
|
|
/* send EOF to app layer */
|
|
|
|
|
|
|
|
STREAM_SET_FLAGS(ssn, stream, p, flags);
|
|
|
|
|
|
|
|
AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
|
|
|
|
|
|
|
|
NULL, 0, flags);
|
|
|
|
|
|
|
|
AppLayerProfilingStore(ra_ctx->app_tctx, p);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* no segments, nothing to do */
|
|
|
|
|
|
|
|
if (stream->seg_list == NULL) {
|
|
|
|
|
|
|
|
SCLogDebug("no segments in the list to reassemble");
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (stream->flags & STREAMTCP_STREAM_FLAG_GAP) {
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* stream->ra_app_base_seq remains at stream->isn until protocol is
|
|
|
|
|
|
|
|
* detected. */
|
|
|
|
|
|
|
|
ReassembleData rd;
|
|
|
|
|
|
|
|
rd.ra_base_seq = stream->ra_app_base_seq;
|
|
|
|
|
|
|
|
rd.data_len = 0;
|
|
|
|
|
|
|
|
rd.partial = FALSE;
|
|
|
|
|
|
|
|
uint32_t next_seq = rd.ra_base_seq + 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SCLogDebug("ra_base_seq %"PRIu32", last_ack %"PRIu32", next_seq %"PRIu32,
|
|
|
|
|
|
|
|
rd.ra_base_seq, stream->last_ack, next_seq);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* loop through the segments and fill one or more msgs */
|
|
|
|
|
|
|
|
TcpSegment *seg = stream->seg_list;
|
|
|
|
|
|
|
|
SCLogDebug("pre-loop seg %p", seg);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Check if we have a gap at the start of the list. If last_ack is
|
|
|
|
|
|
|
|
* bigger than the list start and the list start is bigger than
|
|
|
|
|
|
|
|
* next_seq, we know we are missing data that has been ack'd. That
|
|
|
|
|
|
|
|
* won't get retransmitted, so it's a data gap.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!(p->flow->flags & FLOW_NO_APPLAYER_INSPECTION)) {
|
|
|
|
|
|
|
|
if (SEQ_GT(seg->seq, next_seq) && SEQ_LT(seg->seq, stream->last_ack)) {
|
|
|
|
|
|
|
|
/* send gap signal */
|
|
|
|
|
|
|
|
STREAM_SET_FLAGS(ssn, stream, p, flags);
|
|
|
|
|
|
|
|
AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
|
|
|
|
|
|
|
|
NULL, 0, flags|STREAM_GAP);
|
|
|
|
|
|
|
|
AppLayerProfilingStore(ra_ctx->app_tctx, p);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* set a GAP flag and make sure not bothering this stream anymore */
|
|
|
|
|
|
|
|
SCLogDebug("STREAMTCP_STREAM_FLAG_GAP set");
|
|
|
|
|
|
|
|
stream->flags |= STREAMTCP_STREAM_FLAG_GAP;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
StreamTcpSetEvent(p, STREAM_REASSEMBLY_SEQ_GAP);
|
|
|
|
|
|
|
|
SCPerfCounterIncr(ra_ctx->counter_tcp_reass_gap, tv->sc_perf_pca);
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
|
|
|
dbg_app_layer_gap++;
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
SCReturnInt(0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (; seg != NULL && SEQ_LT(seg->seq, stream->last_ack);)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
SCLogDebug("seg %p, SEQ %"PRIu32", LEN %"PRIu16", SUM %"PRIu32,
|
|
|
|
|
|
|
|
seg, seg->seq, seg->payload_len,
|
|
|
|
|
|
|
|
(uint32_t)(seg->seq + seg->payload_len));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (p->flow->flags & FLOW_NO_APPLAYER_INSPECTION) {
|
|
|
|
|
|
|
|
SCLogDebug("FLOW_NO_APPLAYER_INSPECTION set, breaking out");
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (StreamTcpReturnSegmentCheck(p->flow, ssn, stream, seg) == 1) {
|
|
|
|
|
|
|
|
SCLogDebug("removing segment");
|
|
|
|
|
|
|
|
TcpSegment *next_seg = seg->next;
|
|
|
|
|
|
|
|
StreamTcpRemoveSegmentFromStream(stream, seg);
|
|
|
|
|
|
|
|
StreamTcpSegmentReturntoPool(seg);
|
|
|
|
|
|
|
|
seg = next_seg;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
} else if (StreamTcpAppLayerSegmentProcessed(stream, seg)) {
|
|
|
|
|
|
|
|
TcpSegment *next_seg = seg->next;
|
|
|
|
|
|
|
|
seg = next_seg;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* check if we have a sequence gap and if so, handle it */
|
|
|
|
|
|
|
|
if (DoHandleGap(tv, ra_ctx, ssn, stream, seg, &rd, p, flags, next_seq) == 1)
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* process this segment */
|
|
|
|
|
|
|
|
if (DoReassemble(tv, ra_ctx, ssn, stream, seg, &rd, p, flags) == 0)
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
/* done with this segment, return it to the pool */
|
|
|
|
/* done with this segment, return it to the pool */
|
|
|
|
TcpSegment *next_seg = seg->next;
|
|
|
|
TcpSegment *next_seg = seg->next;
|
|
|
|
next_seq = seg->seq + seg->payload_len;
|
|
|
|
next_seq = seg->seq + seg->payload_len;
|
|
|
|
if (partial == FALSE) {
|
|
|
|
if (rd.partial == FALSE) {
|
|
|
|
SCLogDebug("fully done with segment in app layer reassembly (seg %p seq %"PRIu32")",
|
|
|
|
SCLogDebug("fully done with segment in app layer reassembly (seg %p seq %"PRIu32")",
|
|
|
|
seg, seg->seq);
|
|
|
|
seg, seg->seq);
|
|
|
|
seg->flags |= SEGMENTTCP_FLAG_APPLAYER_PROCESSED;
|
|
|
|
seg->flags |= SEGMENTTCP_FLAG_APPLAYER_PROCESSED;
|
|
|
|
@ -3203,19 +3232,19 @@ int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* put the partly filled smsg in the queue to the l7 handler */
|
|
|
|
/* put the partly filled smsg in the queue to the l7 handler */
|
|
|
|
if (data_len > 0) {
|
|
|
|
if (rd.data_len > 0) {
|
|
|
|
SCLogDebug("data_len > 0, %u", data_len);
|
|
|
|
SCLogDebug("data_len > 0, %u", rd.data_len);
|
|
|
|
/* process what we have so far */
|
|
|
|
/* process what we have so far */
|
|
|
|
STREAM_SET_FLAGS(ssn, stream, p, flags);
|
|
|
|
STREAM_SET_FLAGS(ssn, stream, p, flags);
|
|
|
|
BUG_ON(data_len > sizeof(data));
|
|
|
|
BUG_ON(rd.data_len > sizeof(rd.data));
|
|
|
|
AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
|
|
|
|
AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
|
|
|
|
data, data_len, flags);
|
|
|
|
rd.data, rd.data_len, flags);
|
|
|
|
AppLayerProfilingStore(ra_ctx->app_tctx, p);
|
|
|
|
AppLayerProfilingStore(ra_ctx->app_tctx, p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* store ra_base_seq in the stream */
|
|
|
|
/* store ra_base_seq in the stream */
|
|
|
|
if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream)) {
|
|
|
|
if (StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream)) {
|
|
|
|
stream->ra_app_base_seq = ra_base_seq;
|
|
|
|
stream->ra_app_base_seq = rd.ra_base_seq;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
TcpSegment *tmp_seg = stream->seg_list;
|
|
|
|
TcpSegment *tmp_seg = stream->seg_list;
|
|
|
|
while (tmp_seg != NULL) {
|
|
|
|
while (tmp_seg != NULL) {
|
|
|
|
|