Add a new app layer reassembly function that is for inline use, and use it when the stream engine is in inline mode.

remotes/origin/master-1.1.x
Victor Julien 14 years ago
parent 8cacd5fe50
commit abdffadc1c

@ -1554,6 +1554,21 @@ int StreamTcpReassembleHandleSegmentHandleData(ThreadVars *tv, TcpReassemblyThre
} \
}
#define STREAM_SET_INLINE_FLAGS(ssn, stream, p, flag) { \
flag = 0; \
if ((stream)->ra_app_base_seq == (stream)->isn) { \
flag |= STREAM_START; \
} \
if ((ssn)->state > TCP_ESTABLISHED) { \
flag |= STREAM_EOF; \
} \
if ((p)->flowflags & FLOW_PKT_TOSERVER) { \
flag |= STREAM_TOSERVER; \
} else { \
flag |= STREAM_TOCLIENT; \
} \
}
static void StreamTcpSetupMsg(TcpSession *ssn, TcpStream *stream, Packet *p,
StreamMsg *smsg)
{
@ -1713,6 +1728,457 @@ static void StreamTcpRemoveSegmentFromStream(TcpStream *stream, TcpSegment *seg)
stream->seg_list_tail = seg->prev;
}
/**
* \brief Update the stream reassembly upon receiving a data segment
*
* Reassembly is in the same direction of the packet.
*
* \todo this function is too long, we need to break it up. It needs it BAD
*/
static int StreamTcpReassembleInlineAppLayer (TcpReassemblyThreadCtx *ra_ctx,
TcpSession *ssn, TcpStream *stream, Packet *p)
{
SCEnter();
uint8_t flags = 0;
/* check if toserver reassembly has started before reassembling toclient. */
if (PKT_IS_TOCLIENT(p) &&
!(ssn->flags & STREAMTCP_FLAG_TOSERVER_REASSEMBLY_STARTED))
{
SCLogDebug("toserver reassembling is not done yet, so "
"skipping reassembling at the moment for to_client");
SCReturnInt(0);
}
SCLogDebug("stream->seg_list %p", stream->seg_list);
#ifdef DEBUG
PrintList(stream->seg_list);
PrintRawDataFp(stdout, p->payload, p->payload_len);
#endif
if (stream->seg_list == NULL) {
/* send an empty EOF msg if we have no segments but TCP state
* is beyond ESTABLISHED */
if (ssn->state > TCP_ESTABLISHED) {
SCLogDebug("sending empty eof message");
/* send EOF to app layer */
STREAM_SET_INLINE_FLAGS(ssn, stream, p, flags);
AppLayerHandleTCPData(&ra_ctx->dp_ctx, p->flow, ssn,
NULL, 0, flags|STREAM_EOF);
/* even if app layer detection failed, we will now move on to
* release reassembly for both directions. */
ssn->flags |= STREAMTCP_FLAG_TOSERVER_REASSEMBLY_STARTED;
} else {
SCLogDebug("no segments in the list to reassemble");
}
SCReturnInt(0);
}
/* check if reassembling has been paused for the moment or not */
if (stream->flags & STREAMTCP_STREAM_FLAG_PAUSE_REASSEMBLY) {
SCLogDebug("reassembling has been paused for this stream, so no"
" reassembling at the moment");
SCReturnInt(0);
}
if (stream->flags & STREAMTCP_STREAM_FLAG_GAP) {
SCReturnInt(0);
}
uint32_t ra_base_seq;
/* check if we have detected the app layer protocol or not. If it has been
detected then, process data normally, as we have sent one smsg from
toserver side already to the app layer */
if (ssn->state <= TCP_ESTABLISHED) {
if (!(ssn->flags & STREAMTCP_FLAG_APPPROTO_DETECTION_COMPLETED)) {
/* Do not perform reassembling of data from server, until the app layer
proto has been detected and we have sent atleast one smsg from client
data to app layer */
if (PKT_IS_TOCLIENT(p)) {
SCLogDebug("we didn't detected the app layer protocol till "
"yet, so not doing toclient reassembling");
SCReturnInt(0);
}
/* initialize the tmp_ra_base_seq for each new run */
stream->tmp_ra_app_base_seq = stream->ra_app_base_seq;
ra_base_seq = stream->tmp_ra_app_base_seq;
/* if app layer protocol has been detected, then restore the reassembled
seq. to the value till reassembling has been done and unset the queue
init flag permanently for this tcp session */
} else if (SEQ_GT(stream->tmp_ra_app_base_seq, stream->ra_app_base_seq)) {
stream->ra_app_base_seq = stream->tmp_ra_app_base_seq;
ra_base_seq = stream->ra_app_base_seq;
SCLogDebug("the app layer protocol has been detected");
} else {
ra_base_seq = stream->ra_app_base_seq;
}
/* set the ra_bas_seq to stream->ra_base_seq as now app layer protocol
has been detected */
} else {
if (SEQ_GT(stream->tmp_ra_app_base_seq, stream->ra_app_base_seq)) {
stream->ra_app_base_seq = stream->tmp_ra_app_base_seq;
ra_base_seq = stream->ra_app_base_seq;
} else {
ra_base_seq = stream->ra_app_base_seq;
}
}
SCLogDebug("ra_base_seq %u", ra_base_seq);
uint8_t data[4096];
uint32_t data_len = 0;
uint16_t payload_offset = 0;
uint16_t payload_len = 0;
uint32_t next_seq = ra_base_seq + 1;
/* loop through the segments and fill one or more msgs */
TcpSegment *seg = stream->seg_list;
SCLogDebug("pre-loop seg %p", seg);
for (; seg != NULL && SEQ_LT(seg->seq, stream->next_seq);) {
SCLogDebug("seg %p", seg);
/* if app layer protocol has been detected, then remove all the segments
which has been previously processed and reassembled */
if ((ssn->flags & STREAMTCP_FLAG_APPPROTO_DETECTION_COMPLETED) &&
(seg->flags & SEGMENTTCP_FLAG_RAW_PROCESSED) &&
(seg->flags & SEGMENTTCP_FLAG_APPLAYER_PROCESSED))
{
SCLogDebug("segment(%p) of length %"PRIu16" has been processed,"
" so return it to pool", seg, seg->payload_len);
TcpSegment *next_seg = seg->next;
StreamTcpRemoveSegmentFromStream(stream, seg);
StreamTcpSegmentReturntoPool(seg);
seg = next_seg;
continue;
}
/* If packets are fully before ra_base_seq, skip them. We do this
* because we've reassembled up to the ra_base_seq point already,
* so we won't do anything with segments before it anyway. */
SCLogDebug("checking for pre ra_base_seq %"PRIu32" seg %p seq %"PRIu32""
" len %"PRIu16", combined %"PRIu32" and stream->last_ack "
"%"PRIu32"", ra_base_seq, seg, seg->seq,
seg->payload_len, seg->seq+seg->payload_len, stream->last_ack);
/* Remove the segments which are either completely before the
ra_base_seq or if they are beyond ra_base_seq, but the segment offset
from which we need to copy in to smsg is beyond the stream->last_ack.
As we are copying until the stream->last_ack only */
/** \todo we should probably not even insert them into the seglist */
if ((SEQ_LEQ((seg->seq + seg->payload_len), (ra_base_seq+1)) ||
SEQ_LEQ(stream->last_ack, (ra_base_seq + (ra_base_seq - seg->seq)))) &&
(seg->flags & SEGMENTTCP_FLAG_RAW_PROCESSED) &&
(seg->flags & SEGMENTTCP_FLAG_APPLAYER_PROCESSED))
{
SCLogDebug("removing pre ra_base_seq %"PRIu32" seg %p seq %"PRIu32
" len %"PRIu16"", ra_base_seq, seg, seg->seq, seg->payload_len);
TcpSegment *next_seg = seg->next;
StreamTcpRemoveSegmentFromStream(stream, seg);
StreamTcpSegmentReturntoPool(seg);
seg = next_seg;
continue;
}
/* we've run into a sequence gap */
if (SEQ_GT(seg->seq, next_seq)) {
/* first, pass on data before the gap */
if (data_len > 0) {
SCLogDebug("pre GAP data");
STREAM_SET_INLINE_FLAGS(ssn, stream, p, flags);
/* if app layer protocol has not been detected till yet,
then check did we have sent message to app layer already
or not. If not then sent the message and set flag that first
message has been sent. No more data till proto has not
been detected */
if (!(ssn->flags & STREAMTCP_FLAG_APPPROTO_DETECTION_COMPLETED)) {
/* process what we have so far */
BUG_ON(data_len > sizeof(data));
AppLayerHandleTCPData(&ra_ctx->dp_ctx, p->flow, ssn,
data, data_len, flags);
stream->tmp_ra_app_base_seq = ra_base_seq;
} else {
/* process what we have so far */
AppLayerHandleTCPData(&ra_ctx->dp_ctx, p->flow, ssn,
data, data_len, flags);
stream->ra_app_base_seq = ra_base_seq;
}
}
/* don't conclude it's a gap straight away. If ra_base_seq is lower
* than last_ack - the window, we consider it a gap. */
if (SEQ_GT((stream->last_ack - stream->window), ra_base_seq) ||
ssn->state > TCP_ESTABLISHED)
{
/* see what the length of the gap is, gap length is seg->seq -
* (ra_base_seq +1) */
#ifdef DEBUG
uint32_t gap_len = seg->seq - next_seq;
SCLogDebug("expected next_seq %" PRIu32 ", got %" PRIu32 " , "
"stream->last_ack %" PRIu32 ". Seq gap %" PRIu32"",
next_seq, seg->seq, stream->last_ack, gap_len);
#endif
next_seq = seg->seq;
/* we need to update the ra_base_seq, if app layer proto has
been detected and we are setting new stream message. Otherwise
every smsg will be with flag STREAM_START set, which we
don't want :-) */
if (ssn->flags & STREAMTCP_FLAG_APPPROTO_DETECTION_COMPLETED) {
stream->ra_app_base_seq = ra_base_seq;
} else {
stream->tmp_ra_app_base_seq = ra_base_seq;
}
/* We have missed the packet and end host has ack'd it, so
* 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
* drop it anyway */
ra_base_seq = seg->seq - 1;
/* send gap signal */
STREAM_SET_INLINE_FLAGS(ssn, stream, p, flags);
AppLayerHandleTCPData(&ra_ctx->dp_ctx, p->flow, ssn,
NULL, 0, flags|STREAM_GAP);
data_len = 0;
/* set a GAP flag and make sure not bothering this stream anymore */
stream->flags |= STREAMTCP_STREAM_FLAG_GAP;
/* flag reassembly as started, so the to_client part can start */
ssn->flags |= STREAMTCP_FLAG_TOSERVER_REASSEMBLY_STARTED;
dbg_app_layer_gap++;
break;
} else {
SCLogDebug("possible GAP, but waiting to see if out of order "
"packets might solve that");
dbg_app_layer_gap_candidate++;
break;
}
}
/* if the segment ends beyond ra_base_seq we need to consider it */
if (SEQ_GT((seg->seq + seg->payload_len), ra_base_seq)) {
SCLogDebug("seg->seq %" PRIu32 ", seg->payload_len %" PRIu32 ", "
"ra_base_seq %" PRIu32 "", seg->seq,
seg->payload_len, ra_base_seq);
/* handle segments partly before ra_base_seq */
if (SEQ_GT(ra_base_seq, seg->seq)) {
payload_offset = ra_base_seq - seg->seq -1;
if (SEQ_LT(stream->next_seq, (seg->seq + seg->payload_len))) {
if (SEQ_LT(stream->next_seq, ra_base_seq)) {
payload_len = (stream->next_seq - seg->seq);
} else {
payload_len = (stream->next_seq - seg->seq) - payload_offset;
}
} else {
payload_len = seg->payload_len - payload_offset;
}
if (SCLogDebugEnabled()) {
BUG_ON(payload_offset > seg->payload_len);
BUG_ON((payload_len + payload_offset) > seg->payload_len);
}
} else {
payload_offset = 0;
if (SEQ_LT(stream->next_seq, (seg->seq + seg->payload_len))) {
payload_len = stream->next_seq - seg->seq;
} else {
payload_len = seg->payload_len;
}
}
SCLogDebug("payload_offset is %"PRIu16", payload_len is %"PRIu16""
" and stream->next_seq is %"PRIu32"", payload_offset,
payload_len, stream->next_seq);
if (payload_len == 0) {
SCLogDebug("no payload_len, so bail out");
break;
}
/* copy the data into the smsg */
uint16_t copy_size = sizeof(data) - data_len;
if (copy_size > payload_len) {
copy_size = payload_len;
}
if (SCLogDebugEnabled()) {
BUG_ON(copy_size > sizeof(data));
}
SCLogDebug("copy_size is %"PRIu16"", copy_size);
memcpy(data + data_len, seg->payload + payload_offset, copy_size);
data_len += copy_size;
ra_base_seq += copy_size;
SCLogDebug("ra_base_seq %"PRIu32", data_len %"PRIu32, ra_base_seq, data_len);
/* queue the smsg if it's full */
if (data_len == sizeof(data)) {
/* if app layer protocol has not been detected till yet,
then check did we have sent message to app layer already
or not. If not then sent the message and set flag that first
message has been sent. No more data till proto has not
been detected */
if (!(ssn->flags & STREAMTCP_FLAG_APPPROTO_DETECTION_COMPLETED)) {
/* process what we have so far */
STREAM_SET_INLINE_FLAGS(ssn, stream, p, flags);
BUG_ON(data_len > sizeof(data));
AppLayerHandleTCPData(&ra_ctx->dp_ctx, p->flow, ssn,
data, data_len, flags);
data_len = 0;
stream->tmp_ra_app_base_seq = ra_base_seq;
} else {
/* process what we have so far */
STREAM_SET_INLINE_FLAGS(ssn, stream, p, flags);
BUG_ON(data_len > sizeof(data));
AppLayerHandleTCPData(&ra_ctx->dp_ctx, p->flow, ssn,
data, data_len, flags);
data_len = 0;
stream->ra_app_base_seq = ra_base_seq;
}
}
/* if the payload len is bigger than what we copied, we handle the
* rest of the payload next... */
if (copy_size < payload_len) {
SCLogDebug("copy_size %" PRIu32 " < %" PRIu32 "", copy_size,
payload_len);
payload_offset += copy_size;
payload_len -= copy_size;
SCLogDebug("payload_offset is %"PRIu16", seg->payload_len is "
"%"PRIu16" and stream->last_ack is %"PRIu32"",
payload_offset, seg->payload_len, stream->last_ack);
if (SCLogDebugEnabled()) {
BUG_ON(payload_offset > seg->payload_len);
}
/* we need a while loop here as the packets theoretically can be
* 64k */
char segment_done = FALSE;
while (segment_done == FALSE) {
SCLogDebug("new msg at offset %" PRIu32 ", payload_len "
"%" PRIu32 "", payload_offset, payload_len);
data_len = 0;
copy_size = sizeof(data) - data_len;
if (copy_size > (seg->payload_len - payload_offset)) {
copy_size = (seg->payload_len - payload_offset);
}
if (SCLogDebugEnabled()) {
BUG_ON(copy_size > sizeof(data));
}
SCLogDebug("copy payload_offset %" PRIu32 ", data_len "
"%" PRIu32 ", copy_size %" PRIu32 "",
payload_offset, data_len, copy_size);
memcpy(data + data_len, seg->payload +
payload_offset, copy_size);
data_len += copy_size;
ra_base_seq += copy_size;
SCLogDebug("ra_base_seq %"PRIu32, ra_base_seq);
SCLogDebug("copied payload_offset %" PRIu32 ", "
"data_len %" PRIu32 ", copy_size %" PRIu32 "",
payload_offset, data_len, copy_size);
if (data_len == sizeof(data)) {
/* if app layer protocol has not been detected till yet,
then check did we have sent message to app layer already
or not. If not then sent the message and set flag that first
message has been sent. No more data till proto has not
been detected */
if (!(ssn->flags & STREAMTCP_FLAG_APPPROTO_DETECTION_COMPLETED)) {
/* process what we have so far */
STREAM_SET_INLINE_FLAGS(ssn, stream, p, flags);
BUG_ON(data_len > sizeof(data));
AppLayerHandleTCPData(&ra_ctx->dp_ctx, p->flow, ssn,
data, data_len, flags);
data_len = 0;
stream->tmp_ra_app_base_seq = ra_base_seq;
} else {
/* process what we have so far */
STREAM_SET_INLINE_FLAGS(ssn, stream, p, flags);
BUG_ON(data_len > sizeof(data));
AppLayerHandleTCPData(&ra_ctx->dp_ctx, p->flow, ssn,
data, data_len, flags);
data_len = 0;
stream->ra_app_base_seq = ra_base_seq;
}
}
/* see if we have segment payload left to process */
if ((copy_size + payload_offset) < seg->payload_len) {
payload_offset += copy_size;
payload_len -= copy_size;
if (SCLogDebugEnabled()) {
BUG_ON(payload_offset > seg->payload_len);
}
} else {
payload_offset = 0;
segment_done = TRUE;
}
}
} else {
payload_offset = 0;
}
}
/* done with this segment, return it to the pool */
TcpSegment *next_seg = seg->next;
next_seq = seg->seq + seg->payload_len;
seg->flags |= SEGMENTTCP_FLAG_APPLAYER_PROCESSED;
seg = next_seg;
}
/* put the partly filled smsg in the queue to the l7 handler */
if (data_len > 0) {
SCLogDebug("data_len > 0, %u", data_len);
if (!(ssn->flags & STREAMTCP_FLAG_APPPROTO_DETECTION_COMPLETED)) {
/* process what we have so far */
STREAM_SET_INLINE_FLAGS(ssn, stream, p, flags);
BUG_ON(data_len > sizeof(data));
AppLayerHandleTCPData(&ra_ctx->dp_ctx, p->flow, ssn,
data, data_len, flags);
stream->tmp_ra_app_base_seq = ra_base_seq;
} else {
/* process what we have so far */
STREAM_SET_INLINE_FLAGS(ssn, stream, p, flags);
BUG_ON(data_len > sizeof(data));
AppLayerHandleTCPData(&ra_ctx->dp_ctx, p->flow, ssn,
data, data_len, flags);
stream->ra_app_base_seq = ra_base_seq;
}
}
if (ssn->flags & STREAMTCP_FLAG_APPPROTO_DETECTION_COMPLETED) {
SCLogDebug("proto detection already completed");
ssn->flags |= STREAMTCP_FLAG_TOSERVER_REASSEMBLY_STARTED;
} else {
SCLogDebug("protocol detection not yet completed");
}
SCReturnInt(0);
}
/**
* \brief Update the stream reassembly upon receiving an ACK packet.
*
@ -2538,8 +3004,10 @@ int StreamTcpReassembleHandleSegmentUpdateACK (TcpReassemblyThreadCtx *ra_ctx,
SCLogDebug("stream->seg_list %p", stream->seg_list);
int r = 0;
if (StreamTcpReassembleAppLayer(ra_ctx, ssn, stream, p) < 0)
r = -1;
if (!(StreamTcpInlineMode())) {
if (StreamTcpReassembleAppLayer(ra_ctx, ssn, stream, p) < 0)
r = -1;
}
if (StreamTcpReassembleRaw(ra_ctx, ssn, stream, p) < 0)
r = -1;
@ -2620,11 +3088,23 @@ int StreamTcpReassembleHandleSegment(ThreadVars *tv, TcpReassemblyThreadCtx *ra_
/* If no stream reassembly/application layer protocol inspection, then
simple return */
if (p->payload_len > 0 && !(stream->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
SCLogDebug("calling StreamTcpReassembleHandleSegmentHandleData");
if (!(StreamTcpInlineMode())) {
SCLogDebug("calling StreamTcpReassembleHandleSegmentHandleData");
if (StreamTcpReassembleHandleSegmentHandleData(tv, ra_ctx, ssn, stream, p) != 0) {
SCLogDebug("StreamTcpReassembleHandleSegmentHandleData error");
SCReturnInt(-1);
}
} else {
SCLogDebug("calling StreamTcpReassembleHandleSegmentHandleData");
if (StreamTcpReassembleHandleSegmentHandleData(tv, ra_ctx, ssn, stream, p) != 0) {
SCLogDebug("StreamTcpReassembleHandleSegmentHandleData error");
SCReturnInt(-1);
if (StreamTcpReassembleHandleSegmentHandleData(tv, ra_ctx, ssn, stream, p) != 0) {
SCLogDebug("StreamTcpReassembleHandleSegmentHandleData error");
SCReturnInt(-1);
}
if (StreamTcpReassembleInlineAppLayer(ra_ctx, ssn, stream, p) < 0)
SCReturnInt(-1);
}
p->flags |= PKT_STREAM_ADD;

Loading…
Cancel
Save