stream: improve gap handling with 'incomplete'

Make sure stream requiring more data because of 'incomplete' records
properly move ahead if there is a GAP in the window of required data.
pull/5230/head
Victor Julien 6 years ago
parent c7d59a61ea
commit e822b30cc2

@ -885,16 +885,41 @@ static StreamingBufferBlock *GetBlock(StreamingBuffer *sb, const uint64_t offset
return NULL; return NULL;
} }
static inline uint64_t GetAbsLastAck(const TcpStream *stream)
{
if (STREAM_LASTACK_GT_BASESEQ(stream)) {
return STREAM_BASE_OFFSET(stream) +
(stream->last_ack - stream->base_seq);
} else {
return STREAM_BASE_OFFSET(stream);
}
}
static inline bool GapAhead(TcpStream *stream, StreamingBufferBlock *cur_blk)
{
StreamingBufferBlock *nblk = SBB_RB_NEXT(cur_blk);
if (nblk && (cur_blk->offset + cur_blk->len < nblk->offset) &&
GetAbsLastAck(stream) >= (cur_blk->offset + cur_blk->len)) {
return true;
}
return false;
}
/** \internal /** \internal
* *
* Get buffer, or first part of the buffer if data gaps exist. * Get buffer, or first part of the buffer if data gaps exist.
* *
* \brief get stream data from offset * \brief get stream data from offset
* \param offset stream offset */ * \param offset stream offset
static void GetAppBuffer(TcpStream *stream, const uint8_t **data, uint32_t *data_len, uint64_t offset) * \param check_for_gap check if there is a gap ahead. Optional as it is only
* needed for app-layer incomplete support.
* \retval bool pkt loss ahead */
static bool GetAppBuffer(TcpStream *stream, const uint8_t **data, uint32_t *data_len,
uint64_t offset, const bool check_for_gap)
{ {
const uint8_t *mydata; const uint8_t *mydata;
uint32_t mydata_len; uint32_t mydata_len;
bool gap_ahead = false;
if (RB_EMPTY(&stream->sb.sbb_tree)) { if (RB_EMPTY(&stream->sb.sbb_tree)) {
SCLogDebug("getting one blob"); SCLogDebug("getting one blob");
@ -908,7 +933,7 @@ static void GetAppBuffer(TcpStream *stream, const uint8_t **data, uint32_t *data
if (blk == NULL) { if (blk == NULL) {
*data = NULL; *data = NULL;
*data_len = 0; *data_len = 0;
return; return false;
} }
/* block at expected offset */ /* block at expected offset */
@ -916,6 +941,8 @@ static void GetAppBuffer(TcpStream *stream, const uint8_t **data, uint32_t *data
StreamingBufferSBBGetData(&stream->sb, blk, data, data_len); StreamingBufferSBBGetData(&stream->sb, blk, data, data_len);
gap_ahead = check_for_gap && GapAhead(stream, blk);
/* block past out offset */ /* block past out offset */
} else if (blk->offset > offset) { } else if (blk->offset > offset) {
SCLogDebug("gap, want data at offset %"PRIu64", " SCLogDebug("gap, want data at offset %"PRIu64", "
@ -930,11 +957,15 @@ static void GetAppBuffer(TcpStream *stream, const uint8_t **data, uint32_t *data
offset, blk->offset, blk->len); offset, blk->offset, blk->len);
StreamingBufferSBBGetDataAtOffset(&stream->sb, blk, data, data_len, offset); StreamingBufferSBBGetDataAtOffset(&stream->sb, blk, data, data_len, offset);
SCLogDebug("data %p, data_len %u", *data, *data_len); SCLogDebug("data %p, data_len %u", *data, *data_len);
gap_ahead = check_for_gap && GapAhead(stream, blk);
} else { } else {
*data = NULL; *data = NULL;
*data_len = 0; *data_len = 0;
} }
} }
return gap_ahead;
} }
/** \internal /** \internal
@ -1047,9 +1078,12 @@ static int ReassembleUpdateAppLayer (ThreadVars *tv,
const uint8_t *mydata; const uint8_t *mydata;
uint32_t mydata_len; uint32_t mydata_len;
bool gap_ahead = false;
while (1) { while (1) {
GetAppBuffer(*stream, &mydata, &mydata_len, app_progress); bool check_for_gap_ahead = ((*stream)->data_required > 0);
gap_ahead = GetAppBuffer(*stream, &mydata, &mydata_len,
app_progress, check_for_gap_ahead);
DEBUG_VALIDATE_BUG_ON(mydata_len > (uint32_t)INT_MAX); DEBUG_VALIDATE_BUG_ON(mydata_len > (uint32_t)INT_MAX);
if (mydata == NULL && mydata_len > 0 && CheckGap(ssn, *stream, p)) { if (mydata == NULL && mydata_len > 0 && CheckGap(ssn, *stream, p)) {
SCLogDebug("sending GAP to app-layer (size: %u)", mydata_len); SCLogDebug("sending GAP to app-layer (size: %u)", mydata_len);
@ -1067,12 +1101,13 @@ static int ReassembleUpdateAppLayer (ThreadVars *tv,
/* a GAP also consumes 'data required'. TODO perhaps we can use /* a GAP also consumes 'data required'. TODO perhaps we can use
* this to skip post GAP data until the start of a next record. */ * this to skip post GAP data until the start of a next record. */
if ((*stream)->data_required > 0) {
if ((*stream)->data_required > mydata_len) { if ((*stream)->data_required > mydata_len) {
(*stream)->data_required -= mydata_len; (*stream)->data_required -= mydata_len;
} else { } else {
(*stream)->data_required = 0; (*stream)->data_required = 0;
} }
}
if (r < 0) if (r < 0)
return 0; return 0;
@ -1099,7 +1134,12 @@ static int ReassembleUpdateAppLayer (ThreadVars *tv,
if ((p->flags & PKT_PSEUDO_STREAM_END) == 0 || ssn->state < TCP_CLOSED) { if ((p->flags & PKT_PSEUDO_STREAM_END) == 0 || ssn->state < TCP_CLOSED) {
if (mydata_len < (*stream)->data_required) { if (mydata_len < (*stream)->data_required) {
SCLogDebug("mydata_len %u data_required %u", mydata_len, (*stream)->data_required); if (gap_ahead) {
(*stream)->app_progress_rel += mydata_len;
(*stream)->data_required -= mydata_len;
// TODO send incomplete data to app-layer with special flag
// indicating its all there is for this rec?
}
SCReturnInt(0); SCReturnInt(0);
} }
} }

Loading…
Cancel
Save