From: Victor Julien Date: Sat, 25 Feb 2017 12:33:15 +0000 (+0100) Subject: stream: detect stream GAP also during reassembly X-Git-Tag: suricata-4.0.0-beta1~164 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=89d0267df2ea50ed23432795ad6eee43ee08aad7;p=thirdparty%2Fsuricata.git stream: detect stream GAP also during reassembly --- diff --git a/src/stream-tcp-reassemble.c b/src/stream-tcp-reassemble.c index 9fc814d1f5..60fc2e6b05 100644 --- a/src/stream-tcp-reassemble.c +++ b/src/stream-tcp-reassemble.c @@ -904,6 +904,40 @@ static void GetAppBuffer(TcpStream *stream, const uint8_t **data, uint32_t *data } } +static inline bool CheckGap(TcpStream *stream, Packet *p) +{ + const uint64_t app_progress = STREAM_APP_PROGRESS(stream); + uint64_t last_ack_abs = STREAM_BASE_OFFSET(stream); + + if (STREAM_LASTACK_GT_BASESEQ(stream)) { + /* get window of data that is acked */ + uint32_t delta = stream->last_ack - stream->base_seq; + DEBUG_VALIDATE_BUG_ON(delta > 10000000ULL && delta > stream->window); + /* get max absolute offset */ + last_ack_abs += delta; + + // last_ack > app_progress, but not data. We won't get it either because of last_ack. + if (last_ack_abs > app_progress+1) { + + // account for our too liberal ack acceptance & pseudo packet last_ack hackery + if (SEQ_LT(stream->next_seq, stream->last_ack)) { + if (SEQ_GT(stream->next_seq, stream->base_seq)) { + uint64_t next_seq_abs = STREAM_BASE_OFFSET(stream) + (stream->next_seq - stream->base_seq); + if (next_seq_abs > app_progress+1) { + /* fall through */ + } else { + return false; + } + } + } + + SCLogDebug("packet %u GAP! last_ack_abs %u > app_progress %u, at no data.", (uint)p->pcap_cnt, (uint)last_ack_abs, (uint)app_progress); + return true; + } + } + return false; +} + /** \internal * \brief get stream buffer and update the app-layer * \retval 0 success @@ -914,24 +948,31 @@ static int ReassembleUpdateAppLayer (ThreadVars *tv, Packet *p, enum StreamUpdateDir dir) { const uint64_t app_progress = STREAM_APP_PROGRESS(stream); - uint64_t last_ack_abs = 0; /* absolute right edge of ack'd data */ SCLogDebug("app progress %"PRIu64, app_progress); SCLogDebug("last_ack %u, base_seq %u", stream->last_ack, stream->base_seq); - if (STREAM_LASTACK_GT_BASESEQ(stream)) { - /* get window of data that is acked */ - uint32_t delta = stream->last_ack - stream->base_seq; - DEBUG_VALIDATE_BUG_ON(delta > 10000000ULL && delta > stream->window); - /* get max absolute offset */ - last_ack_abs += delta; - } - const uint8_t *mydata; uint32_t mydata_len; GetAppBuffer(stream, &mydata, &mydata_len, app_progress); - if (mydata == NULL || mydata_len == 0) + if (mydata == NULL || mydata_len == 0) { + if (CheckGap(stream, p)) { + /* send gap signal */ + SCLogDebug("sending GAP to app-layer"); + AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream, + NULL, 0, + StreamGetAppLayerFlags(ssn, stream, p, dir)|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); + StatsIncr(tv, ra_ctx->counter_tcp_reass_gap); + } return 0; + } //PrintRawDataFp(stdout, mydata, mydata_len); @@ -942,6 +983,15 @@ static int ReassembleUpdateAppLayer (ThreadVars *tv, if (StreamTcpInlineMode() == 0 && (p->flags & PKT_PSEUDO_STREAM_END)) { // } else if (StreamTcpInlineMode() == 0) { + uint64_t last_ack_abs = app_progress; /* absolute right edge of ack'd data */ + if (STREAM_LASTACK_GT_BASESEQ(stream)) { + /* get window of data that is acked */ + uint32_t delta = stream->last_ack - stream->base_seq; + DEBUG_VALIDATE_BUG_ON(delta > 10000000ULL && delta > stream->window); + /* get max absolute offset */ + last_ack_abs += delta; + } + /* see if the buffer contains unack'd data as well */ if (app_progress + mydata_len > last_ack_abs) { uint32_t check = mydata_len; @@ -1011,6 +1061,8 @@ int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, * 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. + * 3. check if next_seq is smaller than last_ack, indicating next_seq + * has fallen behind the data that is already acked. */ if (!(ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED)) { int ackadd = (ssn->state >= TCP_FIN_WAIT2) ? 2 : 1; @@ -1018,9 +1070,16 @@ int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, stream->base_seq == stream->isn+1 && SEQ_GT(stream->last_ack, stream->isn + ackadd)) || - (stream->seg_list != NULL && /*2*/ - SEQ_GT(stream->seg_list->seq, stream->base_seq) && - SEQ_LT(stream->seg_list->seq, stream->last_ack))) + (stream->seg_list != NULL && + ( /*2*/ + (SEQ_GT(stream->seg_list->seq, stream->base_seq) && + SEQ_LT(stream->seg_list->seq, stream->last_ack)) + || + (SEQ_GT(stream->seg_list->seq, stream->base_seq) && + SEQ_LT(stream->next_seq, stream->last_ack)) + ) + ) + ) { if (stream->seg_list == NULL) { SCLogDebug("no segs, last_ack moved fwd so GAP "