ret = -1;
else
ret = StreamTcpReassembleAppLayer(tv, ra_ctx, ssn,
- opposing_stream, p);
+ opposing_stream, p, UPDATE_DIR_OPPOSING); // TODO see if we can simplify this
if (stream == &ssn->client) {
if (StreamTcpInlineMode()) {
p->flowflags &= ~FLOW_PKT_TOCLIENT;
-/* Copyright (C) 2007-2012 Open Information Security Foundation
+/* Copyright (C) 2007-2017 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* inspected the app layer state yet */
if (ssn->state >= TCP_ESTABLISHED && ssn->state != TCP_CLOSED)
{
- if (*client != STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY)
- *client = STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
-
- if (*server != STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY)
- *server = STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
+ *client = STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
+ *server = STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
}
/* if app layer still needs some love, push through */
if (AppLayerParserGetTransactionActive(f->proto, f->alproto,
f->alparser, STREAM_TOCLIENT) < total_txs)
{
- if (*server != STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY)
- *server = STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
+ *server = STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
}
if (AppLayerParserGetTransactionActive(f->proto, f->alproto,
f->alparser, STREAM_TOSERVER) < total_txs)
{
- if (*client != STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY)
- *client = STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
+ *client = STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
}
}
*/
int FlowForceReassemblyForFlow(Flow *f, int server, int client)
{
- Packet *p1 = NULL, *p2 = NULL, *p3 = NULL;
+ Packet *p1 = NULL, *p2 = NULL;
TcpSession *ssn;
/* looks like we have no flows in this queue */
* toclient which is now dummy since all we need it for is detection */
/* insert a pseudo packet in the toserver direction */
- if (client == STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY) {
- p1 = FlowForceReassemblyPseudoPacketGet(1, f, ssn, 0);
+ if (client == STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION) {
+ p1 = FlowForceReassemblyPseudoPacketGet(0, f, ssn, 1);
if (p1 == NULL) {
goto done;
}
PKT_SET_SRC(p1, PKT_SRC_FFR);
- if (server == STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY) {
- p2 = FlowForceReassemblyPseudoPacketGet(0, f, ssn, 0);
- if (p2 == NULL) {
- FlowDeReference(&p1->flow);
- TmqhOutputPacketpool(NULL, p1);
- goto done;
- }
- PKT_SET_SRC(p2, PKT_SRC_FFR);
-
- p3 = FlowForceReassemblyPseudoPacketGet(1, f, ssn, 1);
- if (p3 == NULL) {
- FlowDeReference(&p1->flow);
- TmqhOutputPacketpool(NULL, p1);
- FlowDeReference(&p2->flow);
- TmqhOutputPacketpool(NULL, p2);
- goto done;
- }
- PKT_SET_SRC(p3, PKT_SRC_FFR);
- } else {
- p2 = FlowForceReassemblyPseudoPacketGet(0, f, ssn, 1);
- if (p2 == NULL) {
- FlowDeReference(&p1->flow);
- TmqhOutputPacketpool(NULL, p1);
- goto done;
- }
- PKT_SET_SRC(p2, PKT_SRC_FFR);
- }
-
- } else if (client == STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION) {
- if (server == STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY) {
- p1 = FlowForceReassemblyPseudoPacketGet(0, f, ssn, 0);
- if (p1 == NULL) {
- goto done;
- }
- PKT_SET_SRC(p1, PKT_SRC_FFR);
-
+ if (server == STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION) {
p2 = FlowForceReassemblyPseudoPacketGet(1, f, ssn, 1);
if (p2 == NULL) {
FlowDeReference(&p1->flow);
goto done;
}
PKT_SET_SRC(p2, PKT_SRC_FFR);
- } else {
- p1 = FlowForceReassemblyPseudoPacketGet(0, f, ssn, 1);
- if (p1 == NULL) {
- goto done;
- }
- PKT_SET_SRC(p1, PKT_SRC_FFR);
-
- if (server == STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION) {
- p2 = FlowForceReassemblyPseudoPacketGet(1, f, ssn, 1);
- if (p2 == NULL) {
- FlowDeReference(&p1->flow);
- TmqhOutputPacketpool(NULL, p1);
- goto done;
- }
- PKT_SET_SRC(p2, PKT_SRC_FFR);
- }
}
-
} else {
- if (server == STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY) {
- p1 = FlowForceReassemblyPseudoPacketGet(0, f, ssn, 0);
- if (p1 == NULL) {
- goto done;
- }
- PKT_SET_SRC(p1, PKT_SRC_FFR);
-
- p2 = FlowForceReassemblyPseudoPacketGet(1, f, ssn, 1);
- if (p2 == NULL) {
- FlowDeReference(&p1->flow);
- TmqhOutputPacketpool(NULL, p1);
- goto done;
- }
- PKT_SET_SRC(p2, PKT_SRC_FFR);
- } else if (server == STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION) {
+ if (server == STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION) {
p1 = FlowForceReassemblyPseudoPacketGet(1, f, ssn, 1);
if (p1 == NULL) {
goto done;
/* inject the packet(s) into the appropriate thread */
int thread_id = (int)f->thread_id;
- Packet *packets[4] = { p1, p2 ? p2 : p3, p2 ? p3 : NULL, NULL }; /**< null terminated array of packets */
+ Packet *packets[3] = { p1, p2 ? p2 : NULL, NULL }; /**< null terminated array of packets */
if (unlikely(!(TmThreadsInjectPacketsById(packets, thread_id)))) {
FlowDeReference(&p1->flow);
TmqhOutputPacketpool(NULL, p1);
FlowDeReference(&p2->flow);
TmqhOutputPacketpool(NULL, p2);
}
- if (p3) {
- FlowDeReference(&p3->flow);
- TmqhOutputPacketpool(NULL, p3);
- }
}
/* done, in case of error (no packet) we still tag flow as complete
/* handle TCP and app layer */
if (PKT_IS_TCP(p)) {
- SCLogDebug("packet %"PRIu64" is TCP", p->pcap_cnt);
+ SCLogDebug("packet %"PRIu64" is TCP. Direction %s", p->pcap_cnt, PKT_IS_TOSERVER(p) ? "TOSERVER" : "TOCLIENT");
DEBUG_ASSERT_FLOW_LOCKED(p->flow);
/* if detect is disabled, we need to apply file flags to the flow
}
static uint8_t StreamGetAppLayerFlags(TcpSession *ssn, TcpStream *stream,
- Packet *p)
+ Packet *p, enum StreamUpdateDir dir)
{
uint8_t flag = 0;
flag |= STREAM_EOF;
}
- if (StreamTcpInlineMode() == 0) {
+ if (dir == UPDATE_DIR_OPPOSING) {
if (p->flowflags & FLOW_PKT_TOSERVER) {
flag |= STREAM_TOCLIENT;
} else {
}
/* some states mean we reassemble no matter how much data we have */
- if (ssn->state >= TCP_TIME_WAIT)
+ if (ssn->state > TCP_TIME_WAIT)
SCReturnInt(1);
if (p->flags & PKT_PSEUDO_STREAM_END)
if (use_raw) {
if (right_edge > STREAM_RAW_PROGRESS(stream)) {
- SCLogDebug("%s: STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY", dirstr);
- return STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY;
+ SCLogDebug("%s: STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION", dirstr);
+ return STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
}
}
if (use_app) {
if (right_edge > STREAM_APP_PROGRESS(stream)) {
- SCLogDebug("%s: STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY", dirstr);
- return STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY;
+ SCLogDebug("%s: STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION", dirstr);
+ return STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION;
}
}
} else {
static int ReassembleUpdateAppLayer (ThreadVars *tv,
TcpReassemblyThreadCtx *ra_ctx,
TcpSession *ssn, TcpStream *stream,
- Packet *p)
+ 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 */
/* update the app-layer */
int r = AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
(uint8_t *)mydata, mydata_len,
- StreamGetAppLayerFlags(ssn, stream, p));
+ StreamGetAppLayerFlags(ssn, stream, p, dir));
/* see if we can update the progress */
if (r == 0 && StreamTcpIsSetStreamFlagAppProtoDetectionCompleted(stream)) {
}
/**
- * \brief Update the stream reassembly upon receiving an ACK packet.
+ * \brief Update the stream reassembly upon receiving a packet.
*
- * Stream is in the opposite direction of the packet, as the ACK-packet
- * is ACK'ing the stream.
+ * For IDS mode, the 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)
+ Packet *p, enum StreamUpdateDir dir)
{
SCEnter();
SCLogDebug("sending GAP to app-layer");
AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
NULL, 0,
- StreamGetAppLayerFlags(ssn, stream, p)|STREAM_GAP);
+ 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 */
/* send EOF to app layer */
AppLayerHandleTCPData(tv, ra_ctx, p, p->flow, ssn, stream,
NULL, 0,
- StreamGetAppLayerFlags(ssn, stream, p));
+ StreamGetAppLayerFlags(ssn, stream, p, dir));
AppLayerProfilingStore(ra_ctx->app_tctx, p);
SCReturnInt(0);
}
/* with all that out of the way, lets update the app-layer */
- ReassembleUpdateAppLayer(tv, ra_ctx, ssn, stream, p);
-
- SCReturnInt(0);
+ return ReassembleUpdateAppLayer(tv, ra_ctx, ssn, stream, p, dir);
}
/** \internal
stream = &ssn->server;
}
- uint64_t progress = STREAM_RAW_PROGRESS(stream);
if (StreamTcpInlineMode() == FALSE) {
if (StreamTcpReassembleRawCheckLimit(ssn, stream, p) == 1) {
- uint32_t delta = stream->last_ack - stream->base_seq;
- /* get max absolute offset */
- uint64_t last_ack_abs = STREAM_BASE_OFFSET(stream) + delta;
- if (last_ack_abs > progress) {
- return true;
- }
+ return true;
}
} else {
if (p->payload_len > 0 && (p->flags & PKT_STREAM_ADD)) {
*
* \retval r 0 on success, -1 on error
*/
-int StreamTcpReassembleHandleSegmentUpdateACK (ThreadVars *tv,
+static int StreamTcpReassembleHandleSegmentUpdateACK (ThreadVars *tv,
TcpReassemblyThreadCtx *ra_ctx, TcpSession *ssn, TcpStream *stream, Packet *p)
{
SCEnter();
-
SCLogDebug("stream->seg_list %p", stream->seg_list);
int r = 0;
- if (!(StreamTcpInlineMode())) {
- if (StreamTcpReassembleAppLayer(tv, ra_ctx, ssn, stream, p) < 0)
- r = -1;
- }
+ if (StreamTcpReassembleAppLayer(tv, ra_ctx, ssn, stream, p, UPDATE_DIR_OPPOSING) < 0)
+ r = -1;
SCLogDebug("stream->seg_list %p", stream->seg_list);
SCReturnInt(r);
opposing_stream = &ssn->client;
}
+ /* default IDS: update opposing side (triggered by ACK) */
+ enum StreamUpdateDir dir = UPDATE_DIR_OPPOSING;
+ /* inline and stream end and flow timeout packets trigger same dir handling */
+ if (StreamTcpInlineMode()) {
+ dir = UPDATE_DIR_PACKET;
+ } else if (p->flags & PKT_PSEUDO_STREAM_END) {
+ dir = UPDATE_DIR_PACKET;
+ } else if (p->tcph && (p->tcph->th_flags & TH_RST)) { // accepted rst
+ dir = UPDATE_DIR_PACKET;
+ } else if (p->tcph && (p->tcph->th_flags & TH_FIN) && ssn->state > TCP_TIME_WAIT) {
+ dir = UPDATE_DIR_PACKET;
+ }
+
/* handle ack received */
- if (StreamTcpReassembleHandleSegmentUpdateACK(tv, ra_ctx, ssn, opposing_stream, p) != 0)
+ if (dir == UPDATE_DIR_OPPOSING &&
+ StreamTcpReassembleHandleSegmentUpdateACK(tv, ra_ctx, ssn, opposing_stream, p) != 0)
{
SCLogDebug("StreamTcpReassembleHandleSegmentUpdateACK error");
SCReturnInt(-1);
}
- /* If no stream reassembly/application layer protocol inspection, then
- simple return */
+ /* if this segment contains data, insert it */
if (p->payload_len > 0 && !(stream->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)) {
SCLogDebug("calling StreamTcpReassembleHandleSegmentHandleData");
/* in stream inline mode even if we have no data we call the reassembly
* functions to handle EOF */
- if (StreamTcpInlineMode()) {
- int r = 0;
- if (StreamTcpReassembleAppLayer(tv, ra_ctx, ssn, stream, p) < 0)
- r = -1;
-
- if (r < 0) {
+ if (dir == UPDATE_DIR_PACKET) {
+ SCLogDebug("inline (%s) or PKT_PSEUDO_STREAM_END (%s)",
+ StreamTcpInlineMode()?"true":"false",
+ (p->flags & PKT_PSEUDO_STREAM_END) ?"true":"false");
+ if (StreamTcpReassembleAppLayer(tv, ra_ctx, ssn, stream, p, dir) < 0) {
SCReturnInt(-1);
}
}
}
ssn.server.next_seq = 4;
- int r = StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p);
+ int r = StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET);
if (r < 0) {
printf("StreamTcpReassembleAppLayer failed: ");
goto end;
}
ssn.server.next_seq = 19;
- r = StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p);
+ r = StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET);
if (r < 0) {
printf("StreamTcpReassembleAppLayer failed: ");
goto end;
}
- if (STREAM_APP_PROGRESS(&ssn.server) != 17) {
- printf("expected ssn.server.app_progress == 17got %"PRIu64": ",
- STREAM_APP_PROGRESS(&ssn.server));
- goto end;
- }
+ FAIL_IF_NOT(STREAM_APP_PROGRESS(&ssn.server) == 17);
ret = 1;
end:
OS_POLICY_LAST
};
+enum StreamUpdateDir {
+ UPDATE_DIR_PACKET,
+ UPDATE_DIR_OPPOSING,
+};
+
typedef struct TcpReassemblyThreadCtx_ {
void *app_tctx;
void StreamTcpReassembleFreeThreadCtx(TcpReassemblyThreadCtx *);
int StreamTcpReassembleAppLayer (ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
TcpSession *ssn, TcpStream *stream,
- Packet *p);
+ Packet *p, enum StreamUpdateDir dir);
void StreamTcpCreateTestPacket(uint8_t *, uint8_t, uint8_t, uint8_t);
if (!StreamTcpValidateRst(ssn, p))
return -1;
- /* force both streams to reassemble, if necessary */
- StreamTcpPseudoPacketCreateStreamEndPacket(tv, stt, p, ssn, pq);
-
if (PKT_IS_TOSERVER(p)) {
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: Reset received and state changed to "
if (!StreamTcpValidateRst(ssn, p))
return -1;
- /* force both streams to reassemble, if necessary */
- StreamTcpPseudoPacketCreateStreamEndPacket(tv, stt, p, ssn, pq);
-
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED",
ssn);
if (!StreamTcpValidateRst(ssn, p))
return -1;
- /* force both streams to reassemble, if necessary */
- StreamTcpPseudoPacketCreateStreamEndPacket(tv, stt, p, ssn, pq);
-
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED",
ssn);
if (!StreamTcpValidateRst(ssn, p))
return -1;
- /* force both streams to reassemble, if necessary */
- StreamTcpPseudoPacketCreateStreamEndPacket(tv, stt, p, ssn, pq);
-
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED",
ssn);
if (!StreamTcpValidateRst(ssn, p))
return -1;
- /* force both streams to reassemble, if necessary */
- StreamTcpPseudoPacketCreateStreamEndPacket(tv, stt, p, ssn, pq);
-
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED",
ssn);
if (!StreamTcpValidateRst(ssn, p))
return -1;
- /* force both streams to reassemble, if necessary */
- StreamTcpPseudoPacketCreateStreamEndPacket(tv, stt, p, ssn, pq);
-
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED",
ssn);
if (!StreamTcpValidateRst(ssn, p))
return -1;
- /* force both streams to reassemble, if necessary */
- StreamTcpPseudoPacketCreateStreamEndPacket(tv, stt, p, ssn, pq);
-
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED",
ssn);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
"%" PRIu32 "", ssn, ssn->client.next_seq,
ssn->server.last_ack);
-
- StreamTcpPseudoPacketCreateStreamEndPacket(tv, stt, p, ssn, pq);
} else {
SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ "
"%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
"%" PRIu32 "", ssn, ssn->server.next_seq,
ssn->client.last_ack);
-
- StreamTcpPseudoPacketCreateStreamEndPacket(tv, stt, p, ssn, pq);
}
} else {
enum {
/* stream has no segments for forced reassembly, nor for detection */
STREAM_HAS_UNPROCESSED_SEGMENTS_NONE = 0,
- /* stream seems to have segments that need to be forced reassembled */
- STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_REASSEMBLY = 1,
/* stream has no segments for forced reassembly, but only segments that
* have been sent for detection, but are stuck in the detection queues */
- STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION = 2,
+ STREAM_HAS_UNPROCESSED_SEGMENTS_NEED_ONLY_DETECTION = 1,
};
TmEcode StreamTcp (ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);