diff --git a/src/decode-events.h b/src/decode-events.h index 69ea27d7d2..0c21cddefd 100644 --- a/src/decode-events.h +++ b/src/decode-events.h @@ -173,6 +173,9 @@ enum { STREAM_PKT_INVALID_ACK, STREAM_RST_INVALID_ACK, + STREAM_REASSEMBLY_SEGMENT_BEFORE_BASE_SEQ, + STREAM_REASSEMBLY_NO_SEGMENT, + /* should always be last! */ DECODE_EVENT_MAX, }; diff --git a/src/stream-tcp-reassemble.c b/src/stream-tcp-reassemble.c index a60a671342..5c98b84b0e 100644 --- a/src/stream-tcp-reassemble.c +++ b/src/stream-tcp-reassemble.c @@ -24,8 +24,7 @@ * Reference: * Judy Novak, Steve Sturges: Target-Based TCP Stream Reassembly August, 2007 * - * \todo segment insert fasttrack: most pkts are in order -*/ + */ #include @@ -461,14 +460,17 @@ static inline uint32_t StreamTcpReassembleGetRaBaseSeq(TcpStream *stream) } /** + * \internal * \brief Function to handle the insertion newly arrived segment, * The packet is handled based on its target OS. * * \param stream The given TCP stream to which this new segment belongs * \param seg Newly arrived segment * \param p received packet - * \retval 0 success - * \retval -1 error + * + * \retval 0 success + * \retval -1 error -- either we hit a memory issue (OOM/memcap) or we received + * a segment before ra_base_seq. */ static int ReassembleInsertSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, @@ -479,7 +481,9 @@ static int ReassembleInsertSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ct TcpSegment *list_seg = stream->seg_list; TcpSegment *next_list_seg = NULL; +#if DEBUG PrintList(stream->seg_list); +#endif int ret_value = 0; char return_seg = FALSE; @@ -493,6 +497,8 @@ static int ReassembleInsertSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ct stream->last_ack, StreamTcpReassembleGetRaBaseSeq(stream)); return_seg = TRUE; ret_value = -1; + + StreamTcpSetEvent(p, STREAM_REASSEMBLY_SEGMENT_BEFORE_BASE_SEQ); goto end; } @@ -500,6 +506,7 @@ static int ReassembleInsertSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ct "ra_app_base_seq %"PRIu32, (TCP_GET_SEQ(p)+p->payload_len), stream->last_ack, stream->ra_app_base_seq); + /* fast track */ if (list_seg == NULL) { SCLogDebug("empty list, inserting seg %p seq %" PRIu32 ", " "len %" PRIu32 "", seg, seg->seq, seg->payload_len); @@ -639,9 +646,9 @@ end: * \param prev_seg Previous segment in the stream segment list * \param p Packet * - * \retval 1 done - * \retval 0 not done yet - * \retval -1 error + * \retval 1 success and done + * \retval 0 success, but not done yet + * \retval -1 error, will *only* happen on memory errors */ static int HandleSegmentStartsBeforeListSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, @@ -729,6 +736,8 @@ static int HandleSegmentStartsBeforeListSegment(ThreadVars *tv, TcpReassemblyThr TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length); if (new_seg == NULL) { SCLogDebug("segment_pool[%"PRIu16"] is empty", segment_pool_idx[packet_length]); + + StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT); SCReturnInt(-1); } new_seg->payload_len = packet_length; @@ -791,6 +800,8 @@ static int HandleSegmentStartsBeforeListSegment(ThreadVars *tv, TcpReassemblyThr TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length); if (new_seg == NULL) { SCLogDebug("segment_pool[%"PRIu16"] is empty", segment_pool_idx[packet_length]); + + StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT); SCReturnInt(-1); } @@ -849,6 +860,8 @@ static int HandleSegmentStartsBeforeListSegment(ThreadVars *tv, TcpReassemblyThr TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length); if (new_seg == NULL) { SCLogDebug("segment_pool[%"PRIu16"] is empty", segment_pool_idx[packet_length]); + + StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT); SCReturnInt(-1); } new_seg->payload_len = packet_length; @@ -900,64 +913,66 @@ static int HandleSegmentStartsBeforeListSegment(ThreadVars *tv, TcpReassemblyThr } else { if (SEQ_GT(seg->seq, (list_seg->prev->seq + list_seg->prev->payload_len))) - { - packet_length = list_seg->payload_len + (list_seg->seq - - seg->seq); - } else { - packet_length = list_seg->payload_len + (list_seg->seq - - (list_seg->prev->seq + - list_seg->prev->payload_len)); - } + { + packet_length = list_seg->payload_len + (list_seg->seq - + seg->seq); + } else { + packet_length = list_seg->payload_len + (list_seg->seq - + (list_seg->prev->seq + + list_seg->prev->payload_len)); + } - packet_length += (seg->seq + seg->payload_len) - - (list_seg->seq + list_seg->payload_len); + packet_length += (seg->seq + seg->payload_len) - + (list_seg->seq + list_seg->payload_len); - TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length); - if (new_seg == NULL) { - SCLogDebug("segment_pool[%"PRIu16"] is empty", - segment_pool_idx[packet_length]); - SCReturnInt(-1); - } - new_seg->payload_len = packet_length; + TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length); + if (new_seg == NULL) { + SCLogDebug("segment_pool[%"PRIu16"] is empty", + segment_pool_idx[packet_length]); - if (SEQ_GT((list_seg->prev->seq + - list_seg->prev->payload_len), seg->seq)) - { - new_seg->seq = (list_seg->prev->seq + - list_seg->prev->payload_len); - } else { - new_seg->seq = seg->seq; - } - SCLogDebug("new_seg->seq %"PRIu32" and new->payload_len " - "%" PRIu16"", new_seg->seq, new_seg->payload_len); - new_seg->next = list_seg->next; - new_seg->prev = list_seg->prev; + StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT); + SCReturnInt(-1); + } + new_seg->payload_len = packet_length; - /* create a new seg, copy the list_seg data over */ - StreamTcpSegmentDataCopy(new_seg, list_seg); + if (SEQ_GT((list_seg->prev->seq + + list_seg->prev->payload_len), seg->seq)) + { + new_seg->seq = (list_seg->prev->seq + + list_seg->prev->payload_len); + } else { + new_seg->seq = seg->seq; + } + SCLogDebug("new_seg->seq %"PRIu32" and new->payload_len " + "%" PRIu16"", new_seg->seq, new_seg->payload_len); + new_seg->next = list_seg->next; + new_seg->prev = list_seg->prev; - /* copy the part before list_seg */ - uint16_t copy_len = list_seg->seq - new_seg->seq; - StreamTcpSegmentDataReplace(new_seg, seg, new_seg->seq, - copy_len); + /* create a new seg, copy the list_seg data over */ + StreamTcpSegmentDataCopy(new_seg, list_seg); - /* copy the part after list_seg */ - copy_len = (seg->seq + seg->payload_len) - - (list_seg->seq + list_seg->payload_len); - StreamTcpSegmentDataReplace(new_seg, seg, (list_seg->seq + - list_seg->payload_len), copy_len); + /* copy the part before list_seg */ + uint16_t copy_len = list_seg->seq - new_seg->seq; + StreamTcpSegmentDataReplace(new_seg, seg, new_seg->seq, + copy_len); - if (new_seg->prev != NULL) { - new_seg->prev->next = new_seg; - } + /* copy the part after list_seg */ + copy_len = (seg->seq + seg->payload_len) - + (list_seg->seq + list_seg->payload_len); + StreamTcpSegmentDataReplace(new_seg, seg, (list_seg->seq + + list_seg->payload_len), copy_len); - /*update the stream last_seg in case of removal of list_seg*/ - if (stream->seg_list_tail == list_seg) - stream->seg_list_tail = new_seg; + if (new_seg->prev != NULL) { + new_seg->prev->next = new_seg; + } - StreamTcpSegmentReturntoPool(list_seg); - list_seg = new_seg; - return_after = TRUE; + /*update the stream last_seg in case of removal of list_seg*/ + if (stream->seg_list_tail == list_seg) + stream->seg_list_tail = new_seg; + + StreamTcpSegmentReturntoPool(list_seg); + list_seg = new_seg; + return_after = TRUE; } } @@ -1025,9 +1040,10 @@ static int HandleSegmentStartsBeforeListSegment(ThreadVars *tv, TcpReassemblyThr * \param list_seg Original Segment in the stream * \param seg Newly arrived segment * \param prev_seg Previous segment in the stream segment list - * \retval 1 done - * \retval 0 not done yet - * \retval -1 error + * + * \retval 1 success and done + * \retval 0 success, but not done yet + * \retval -1 error, will *only* happen on memory errors */ static int HandleSegmentStartsAtSameListSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, @@ -1130,6 +1146,8 @@ static int HandleSegmentStartsAtSameListSegment(ThreadVars *tv, TcpReassemblyThr TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length); if (new_seg == NULL) { SCLogDebug("egment_pool[%"PRIu16"] is empty", segment_pool_idx[packet_length]); + + StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT); return -1; } new_seg->payload_len = packet_length; @@ -1208,6 +1226,7 @@ static int HandleSegmentStartsAtSameListSegment(ThreadVars *tv, TcpReassemblyThr } /** + * \internal * \brief Function to handle the newly arrived segment, when newly arrived * starts with the sequence number higher than the original segment and * ends at different position relative to original segment. @@ -1216,9 +1235,10 @@ static int HandleSegmentStartsAtSameListSegment(ThreadVars *tv, TcpReassemblyThr * \param list_seg Original Segment in the stream * \param seg Newly arrived segment * \param prev_seg Previous segment in the stream segment list - * \retval 1 done - * \retval 0 not done yet - * \retval -1 error + + * \retval 1 success and done + * \retval 0 success, but not done yet + * \retval -1 error, will *only* happen on memory errors */ static int HandleSegmentStartsAfterListSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, @@ -1325,6 +1345,8 @@ static int HandleSegmentStartsAfterListSegment(ThreadVars *tv, TcpReassemblyThre TcpSegment *new_seg = StreamTcpGetSegment(tv, ra_ctx, packet_length); if (new_seg == NULL) { SCLogDebug("segment_pool[%"PRIu16"] is empty", segment_pool_idx[packet_length]); + + StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT); SCReturnInt(-1); } new_seg->payload_len = packet_length; @@ -1457,6 +1479,16 @@ static uint32_t StreamTcpReassembleCheckDepth(TcpStream *stream, SCReturnUInt(0); } +/** + * \brief Insert a packets TCP data into the stream reassembly engine. + * + * \retval 0 good segment, as far as we checked. + * \retval -1 badness, reason to drop in inline mode + * + * If the retval is 0 the segment is inserted correctly, or overlap is handled, + * or it wasn't added because of reassembly depth. + * + */ int StreamTcpReassembleHandleSegmentHandleData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, TcpSession *ssn, TcpStream *stream, Packet *p) { @@ -1490,6 +1522,8 @@ int StreamTcpReassembleHandleSegmentHandleData(ThreadVars *tv, TcpReassemblyThre TcpSegment *seg = StreamTcpGetSegment(tv, ra_ctx, p->payload_len); if (seg == NULL) { SCLogDebug("segment_pool[%"PRIu16"] is empty", segment_pool_idx[p->payload_len]); + + StreamTcpSetEvent(p, STREAM_REASSEMBLY_NO_SEGMENT); SCReturnInt(-1); } @@ -1704,7 +1738,9 @@ static int StreamTcpReassembleAppLayer (TcpReassemblyThreadCtx *ra_ctx, } SCLogDebug("stream->seg_list %p", stream->seg_list); +#ifdef DEBUG PrintList(stream->seg_list); +#endif if (stream->seg_list == NULL) { /* send an empty EOF msg if we have no segments but TCP state diff --git a/src/stream-tcp.h b/src/stream-tcp.h index eedee6e8ba..cbe430386f 100644 --- a/src/stream-tcp.h +++ b/src/stream-tcp.h @@ -66,6 +66,8 @@ int StreamTcpCheckMemcap(uint32_t); void StreamTcpPseudoPacketSetupHeader(Packet *, Packet *); Packet *StreamTcpPseudoSetup(Packet *, uint8_t *, uint32_t); +void StreamTcpSetEvent(Packet *p, uint8_t e); + /** ------- Inline functions: ------ */ /**