You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/src/stream-tcp.c

2446 lines
90 KiB
C

/* Copyright (c) 2008 Victor Julien <victor@inliniac.net> */
/* 2009 Gurvinder Singh <gurvindersinghdahiya@gmail.com>*/
#include "eidps-common.h"
#include "decode.h"
#include "debug.h"
#include "detect.h"
#include "flow.h"
#include "threads.h"
#include "threadvars.h"
#include "tm-modules.h"
#include "util-pool.h"
#include "util-unittest.h"
#include "util-print.h"
#include "util-debug.h"
#include "stream-tcp-private.h"
#include "stream-tcp-reassemble.h"
#include "stream-tcp.h"
#include "stream.h"
#include "stream-tcp.h"
#include "app-layer-parser.h"
//#define DEBUG
typedef struct StreamTcpThread_ {
uint64_t pkts;
uint16_t counter_tcp_sessions;
TcpReassemblyThreadCtx *ra_ctx;
} StreamTcpThread;
int StreamTcp (ThreadVars *, Packet *, void *, PacketQueue *);
int StreamTcpThreadInit(ThreadVars *, void *, void **);
int StreamTcpThreadDeinit(ThreadVars *, void *);
void StreamTcpExitPrintStats(ThreadVars *, void *);
static int ValidReset(TcpSession * , Packet *);
static int StreamTcpHandleFin(StreamTcpThread *, TcpSession *, Packet *);
void StreamTcpRegisterTests (void);
void StreamTcpReturnStreamSegments (TcpStream *);
void StreamTcpInitConfig(char);
extern void StreamTcpSegmentReturntoPool(TcpSegment *);
int StreamTcpGetFlowState(void *);
static int ValidTimestamp(TcpSession * , Packet *);
#define STREAMTCP_DEFAULT_SESSIONS 262144
#define STREAMTCP_DEFAULT_PREALLOC 32768
#define STREAMTCP_NEW_TIMEOUT 60
#define STREAMTCP_EST_TIMEOUT 3600
#define STREAMTCP_CLOSED_TIMEOUT 120
#define STREAMTCP_EMERG_NEW_TIMEOUT 10
#define STREAMTCP_EMERG_EST_TIMEOUT 300
#define STREAMTCP_EMERG_CLOSED_TIMEOUT 20
static Pool *ssn_pool = NULL;
static pthread_mutex_t ssn_pool_mutex;
#ifdef DEBUG
static uint64_t ssn_pool_cnt;
static pthread_mutex_t ssn_pool_cnt_mutex;
#endif
void TmModuleStreamTcpRegister (void) {
tmm_modules[TMM_STREAMTCP].name = "StreamTcp";
tmm_modules[TMM_STREAMTCP].ThreadInit = StreamTcpThreadInit;
tmm_modules[TMM_STREAMTCP].Func = StreamTcp;
tmm_modules[TMM_STREAMTCP].ThreadExitPrintStats = StreamTcpExitPrintStats;
tmm_modules[TMM_STREAMTCP].ThreadDeinit = StreamTcpThreadDeinit;
tmm_modules[TMM_STREAMTCP].RegisterTests = StreamTcpRegisterTests;
}
void StreamTcpReturnStreamSegments (TcpStream *stream) {
TcpSegment *seg = stream->seg_list;
TcpSegment *next_seg;
if (seg == NULL)
return;
while (seg != NULL) {
next_seg = seg->next;
StreamTcpSegmentReturntoPool(seg);
seg = next_seg;
}
stream->seg_list = NULL;
}
/** \brief Function to return the stream back to the pool. It returns the
* segments in the stream to the segment pool.
*
* \param ssn Void ptr to the ssn.
*/
void StreamTcpSessionClear(void *ssnptr) {
TcpSession *ssn = (TcpSession *)ssnptr;
if (ssn == NULL)
return;
StreamTcpReturnStreamSegments(&ssn->client);
StreamTcpReturnStreamSegments(&ssn->server);
AppLayerParserCleanupState(ssn);
memset(ssn, 0, sizeof(TcpSession));
mutex_lock(&ssn_pool_mutex);
PoolReturn(ssn_pool, ssn);
mutex_unlock(&ssn_pool_mutex);
#ifdef DEBUG
mutex_lock(&ssn_pool_cnt_mutex);
ssn_pool_cnt--;
mutex_unlock(&ssn_pool_cnt_mutex);
#endif
}
/** \brief Function to return the stream back to the pool. It returns the
* segments in the stream to the segment pool.
*
* \param p Packet used to identify the stream.
*/
static void StreamTcpSessionPktFree (Packet *p) {
TcpSession *ssn = (TcpSession *)p->flow->protoctx;
if (ssn == NULL)
return;
StreamTcpReturnStreamSegments(&ssn->client);
StreamTcpReturnStreamSegments(&ssn->server);
AppLayerParserCleanupState(ssn);
memset(ssn, 0, sizeof(TcpSession));
mutex_lock(&ssn_pool_mutex);
PoolReturn(ssn_pool, p->flow->protoctx);
mutex_unlock(&ssn_pool_mutex);
p->flow->protoctx = NULL;
#ifdef DEBUG
mutex_lock(&ssn_pool_cnt_mutex);
ssn_pool_cnt--;
mutex_unlock(&ssn_pool_cnt_mutex);
#endif
}
/** \brief Stream alloc function for the Pool
* \param null NULL ptr (value of null is ignored)
* \retval ptr void ptr to TcpSession structure with all vars set to 0/NULL
*/
void *StreamTcpSessionPoolAlloc(void *null) {
void *ptr = malloc(sizeof(TcpSession));
if (ptr == NULL)
return NULL;
memset(ptr, 0, sizeof(TcpSession));
return ptr;
}
/** \brief Pool free function
* \param s Void ptr to TcpSession memory */
void StreamTcpSessionPoolFree(void *s) {
if (s == NULL)
return;
TcpSession *ssn = (TcpSession *)s;
StreamTcpReturnStreamSegments(&ssn->client);
StreamTcpReturnStreamSegments(&ssn->server);
free(ssn);
}
/** \brief To initialize the stream global configuration data
*
* \param quiet It tells the mode of operation, if it is TRUE nothing will
* be get printed.
*/
void StreamTcpInitConfig(char quiet) {
//if (quiet == FALSE)
// printf("Initializing Stream:\n");
memset(&stream_config, 0, sizeof(stream_config));
/** set config defaults */
stream_config.max_sessions = STREAMTCP_DEFAULT_SESSIONS;
stream_config.prealloc_sessions = STREAMTCP_DEFAULT_PREALLOC;
stream_config.midstream = TRUE;
ssn_pool = PoolInit(stream_config.max_sessions, stream_config.prealloc_sessions, StreamTcpSessionPoolAlloc, NULL, StreamTcpSessionPoolFree);
if (ssn_pool == NULL) {
exit(1);
}
pthread_mutex_init(&ssn_pool_mutex, NULL);
StreamTcpReassembleInit(quiet);
/* set the default TCP timeout, free function and flow state function values. */
FlowSetProtoTimeout(IPPROTO_TCP, STREAMTCP_NEW_TIMEOUT, STREAMTCP_EST_TIMEOUT, STREAMTCP_CLOSED_TIMEOUT);
FlowSetProtoEmergencyTimeout(IPPROTO_TCP, STREAMTCP_EMERG_NEW_TIMEOUT, STREAMTCP_EMERG_EST_TIMEOUT, STREAMTCP_EMERG_CLOSED_TIMEOUT);
FlowSetProtoFreeFunc(IPPROTO_TCP, StreamTcpSessionClear);
FlowSetFlowStateFunc(IPPROTO_TCP, StreamTcpGetFlowState);
}
void StreamTcpFreeConfig(char quiet) {
StreamTcpReassembleFree(quiet);
if (ssn_pool != NULL) {
PoolFree(ssn_pool);
ssn_pool = NULL;
} else {
printf("ERROR: ssn_pool is NULL\n");
exit(1);
}
#ifdef DEBUG
SCLogDebug("ssn_pool_cnt %"PRIu64"", ssn_pool_cnt);
#endif
pthread_mutex_destroy(&ssn_pool_mutex);
}
/** \brief The function is used to to fetch a TCP session from the
* ssn_pool, when a TCP SYN is received.
*
* \param quiet Packet P, which has been recieved for the new TCP session.
*
* \retval TcpSession A new TCP session with field initilaized to 0/NULL.
*/
TcpSession *StreamTcpNewSession (Packet *p) {
TcpSession *ssn = (TcpSession *)p->flow->protoctx;
if (ssn == NULL) {
mutex_lock(&ssn_pool_mutex);
p->flow->protoctx = PoolGet(ssn_pool);
mutex_unlock(&ssn_pool_mutex);
ssn = (TcpSession *)p->flow->protoctx;
if (ssn == NULL)
return NULL;
ssn->state = TCP_NONE;
ssn->aldata = NULL;
#ifdef DEBUG
mutex_lock(&ssn_pool_cnt_mutex);
ssn_pool_cnt++;
mutex_unlock(&ssn_pool_cnt_mutex);
#endif
}
return ssn;
}
static inline void StreamTcpPacketSetState(Packet *p, TcpSession *ssn, uint8_t state) {
if (state == ssn->state)
return;
ssn->state = state;
FlowUpdateQueue(p->flow);
}
/**
* \brief Function to handle the TCP_CLOSED or NONE state. The function handles
* packets while the session state is None which means a newly
* initialized structure, or a fully closed session.
*
* \param tv Thread Variable containig input/output queue, cpu affinity etc.
* \param p Packet which has to be handled in this TCP state.
* \param stt Strean Thread module registered to handle the stream handling
*/
static int StreamTcpPacketStateNone(ThreadVars *tv, Packet *p, StreamTcpThread *stt, TcpSession *ssn) {
switch (p->tcph->th_flags) {
case TH_SYN:
{
if (ssn == NULL) {
ssn = StreamTcpNewSession(p);
if (ssn == NULL)
return -1;
PerfCounterIncr(stt->counter_tcp_sessions, tv->pca);
}
/* set the state */
StreamTcpPacketSetState(p, ssn, TCP_SYN_SENT);
SCLogDebug("ssn %p: =~ ssn state is now TCP_SYN_SENT", ssn);
/* set the sequence numbers and window */
ssn->client.isn = TCP_GET_SEQ(p);
ssn->client.ra_base_seq = ssn->client.isn;
ssn->client.next_seq = ssn->client.isn + 1;
/*Set the stream timestamp value, if packet has timestamp option enabled.*/
if (p->tcpvars.ts != NULL) {
ssn->client.last_ts = TCP_GET_TSVAL(p);
SCLogDebug("ssn %p: p->tcpvars.ts %p, %02x", ssn, p->tcpvars.ts, ssn->client.last_ts);
if (ssn->client.last_ts == 0)
ssn->client.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP;
ssn->client.last_pkt_ts = p->ts.tv_sec;
ssn->client.flags |= STREAMTCP_FLAG_TIMESTAMP;
}
ssn->server.window = TCP_GET_WINDOW(p);
if (p->tcpvars.ws != NULL) {
ssn->flags |= STREAMTCP_FLAG_SERVER_WSCALE;
ssn->server.wscale = TCP_GET_WSCALE(p);
}
SCLogDebug("ssn %p: ssn->client.isn %" PRIu32 ", ssn->client.next_seq %" PRIu32 ", ssn->client.last_ack %"PRIu32"",
ssn, ssn->client.isn, ssn->client.next_seq, ssn->client.last_ack);
break;
}
case TH_SYN|TH_ACK:
if (stream_config.midstream == FALSE)
break;
if (ssn == NULL) {
ssn = StreamTcpNewSession(p);
if (ssn == NULL)
return -1;
PerfCounterIncr(stt->counter_tcp_sessions, tv->pca);
}
/* set the state */
StreamTcpPacketSetState(p, ssn, TCP_SYN_RECV);
SCLogDebug("ssn %p: =~ midstream picked ssn state is now TCP_SYN_RECV", ssn);
ssn->flags = STREAMTCP_FLAG_MIDSTREAM;
/* sequence number & window */
ssn->server.isn = TCP_GET_SEQ(p);
ssn->server.ra_base_seq = ssn->server.isn;
ssn->server.next_seq = ssn->server.isn + 1;
ssn->server.window = TCP_GET_WINDOW(p);
SCLogDebug("ssn %p: server window %u", ssn, ssn->server.window);
ssn->client.isn = TCP_GET_ACK(p) - 1;
ssn->client.ra_base_seq = ssn->client.isn;
ssn->client.next_seq = ssn->client.isn + 1;
ssn->client.last_ack = TCP_GET_ACK(p);
/** If the client has a wscale option the server had it too,
* so set the wscale for the server to max. Otherwise none
* will have the wscale opt just like it should. */
if (p->tcpvars.ws != NULL) {
ssn->client.wscale = TCP_GET_WSCALE(p);
ssn->server.wscale = TCP_WSCALE_MAX;
}
SCLogDebug("ssn %p: ssn->client.isn %"PRIu32", ssn->client.next_seq %"PRIu32", ssn->client.last_ack %"PRIu32"",
ssn, ssn->client.isn, ssn->client.next_seq, ssn->client.last_ack);
SCLogDebug("ssn %p: ssn->server.isn %"PRIu32", ssn->server.next_seq %"PRIu32", ssn->server.last_ack %"PRIu32"",
ssn, ssn->server.isn, ssn->server.next_seq, ssn->server.last_ack);
/*Set the timestamp value for both streams, if packet has timestamp option enabled.*/
if (p->tcpvars.ts != NULL) {
ssn->client.last_ts = TCP_GET_TSVAL(p);
ssn->server.last_ts = TCP_GET_TSECR(p);
SCLogDebug("ssn %p: ssn->server.last_ts %" PRIu32" ssn->client.last_ts %" PRIu32"", ssn, ssn->server.last_ts, ssn->client.last_ts);
ssn->flags |= STREAMTCP_FLAG_TIMESTAMP;
ssn->server.last_pkt_ts = p->ts.tv_sec;
if (ssn->server.last_ts == 0)
ssn->server.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP;
if (ssn->client.last_ts == 0)
ssn->client.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP;
} else {
ssn->server.last_ts = 0;
ssn->client.last_ts = 0;
}
break;
/* Handle SYN/ACK and 3WHS shake missed together as it is almost similar. */
case TH_ACK:
case TH_ACK|TH_PUSH:
if (stream_config.midstream == FALSE)
break;
if (ssn == NULL) {
ssn = StreamTcpNewSession(p);
if (ssn == NULL)
return -1;
PerfCounterIncr(stt->counter_tcp_sessions, tv->pca);
}
/* set the state */
StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED);
SCLogDebug("ssn %p: =~ midstream picked ssn state is now TCP_ESTABLISHED", ssn);
ssn->flags = STREAMTCP_FLAG_MIDSTREAM;
ssn->flags |= STREAMTCP_FLAG_MIDSTREAM_ESTABLISHED;
/* set the sequence numbers and window */
ssn->client.isn = TCP_GET_SEQ(p) - 1;
ssn->client.ra_base_seq = ssn->client.isn;
ssn->client.next_seq = TCP_GET_SEQ(p) + p->payload_len;
ssn->client.window = TCP_GET_WINDOW(p);
ssn->client.last_ack = TCP_GET_SEQ(p);
ssn->client.next_win = ssn->client.last_ack + ssn->client.window;
SCLogDebug("ssn %p: ssn->client.isn %u, ssn->client.next_seq %u",
ssn, ssn->client.isn, ssn->client.next_seq);
ssn->server.isn = TCP_GET_ACK(p) - 1;
ssn->server.ra_base_seq = ssn->server.isn;
ssn->server.next_seq = ssn->server.isn + 1;
ssn->server.last_ack = TCP_GET_ACK(p);
ssn->server.next_win = ssn->server.last_ack;
SCLogDebug("ssn %p: ssn->client.next_win %"PRIu32", ssn->server.next_win %"PRIu32"",
ssn, ssn->client.next_win, ssn->server.next_win);
SCLogDebug("ssn %p: ssn->client.last_ack %"PRIu32", ssn->server.last_ack %"PRIu32"",
ssn, ssn->client.last_ack, ssn->server.last_ack);
/** window scaling for midstream pickups, we can't do much other
* than assume that it's set to the max value: 14 */
ssn->client.wscale = TCP_WSCALE_MAX;
ssn->server.wscale = TCP_WSCALE_MAX;
/*Set the timestamp value for both streams, if packet has timestamp option enabled.*/
if (p->tcpvars.ts != NULL) {
ssn->client.last_ts = TCP_GET_TSVAL(p);
ssn->server.last_ts = TCP_GET_TSECR(p);
SCLogDebug("ssn %p: ssn->server.last_ts %" PRIu32" ssn->client.last_ts %" PRIu32"", ssn, ssn->server.last_ts, ssn->client.last_ts);
ssn->flags |= STREAMTCP_FLAG_TIMESTAMP;
ssn->client.last_pkt_ts = p->ts.tv_sec;
if (ssn->server.last_ts == 0)
ssn->server.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP;
if (ssn->client.last_ts == 0)
ssn->client.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP;
} else {
ssn->server.last_ts = 0;
ssn->client.last_ts = 0;
}
/*If no stream reassembly/application layer protocol inspection, then simple return*/
if (! (ssn->flags & STREAMTCP_FLAG_NOCLIENT_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->client, p);
break;
case TH_RST:
case TH_RST|TH_ACK:
case TH_RST|TH_ACK|TH_PUSH:
case TH_FIN:
case TH_FIN|TH_ACK:
case TH_FIN|TH_ACK|TH_PUSH:
BUG_ON(p->flow->protoctx != NULL);
SCLogDebug("FIN or RST packet received, no session setup");
break;
default:
SCLogDebug("default case");
break;
}
return 0;
}
/**
* \brief Function to handle the TCP_SYN_SENT state. The function handles
* SYN, SYN/ACK, RSTpackets and correspondingly changes the connection
* state.
*
* \param tv Thread Variable containig input/output queue, cpu affinity etc.
* \param p Packet which has to be handled in this TCP state.
* \param stt Strean Thread module registered to handle the stream handling
*/
static int StreamTcpPacketStateSynSent(ThreadVars *tv, Packet *p, StreamTcpThread *stt, TcpSession *ssn) {
if (ssn == NULL)
return -1;
switch (p->tcph->th_flags) {
case TH_SYN:
SCLogDebug("ssn %p: SYN packet on state SYN_SENT... resent", ssn);
break;
case TH_SYN|TH_ACK:
if (PKT_IS_TOSERVER(p)) {
SCLogDebug("ssn %p: SYN/ACK received in the wrong direction", ssn);
return -1;
}
/* Check if the SYN/ACK packet ack's the earlier
* received SYN packet. */
if (!(SEQ_EQ(TCP_GET_ACK(p), ssn->client.isn + 1))) {
SCLogDebug("ssn %p: ACK mismatch, packet ACK %" PRIu32 " != %" PRIu32 " from stream",
ssn, TCP_GET_ACK(p), ssn->client.isn + 1);
return -1;
}
/* update state */
StreamTcpPacketSetState(p, ssn, TCP_SYN_RECV);
SCLogDebug("ssn %p: =~ ssn state is now TCP_SYN_RECV", ssn);
/* sequence number & window */
ssn->server.isn = TCP_GET_SEQ(p);
ssn->server.ra_base_seq = ssn->server.isn;
ssn->server.next_seq = ssn->server.isn + 1;
ssn->client.window = TCP_GET_WINDOW(p);
SCLogDebug("ssn %p: window %" PRIu32 "", ssn, ssn->server.window);
if ((p->tcpvars.ts != NULL) && (ssn->client.flags & STREAMTCP_FLAG_TIMESTAMP)) {
ssn->server.last_ts = TCP_GET_TSVAL(p);
SCLogDebug("ssn %p: ssn->server.last_ts %" PRIu32" ssn->client.last_ts %" PRIu32"", ssn, ssn->server.last_ts, ssn->client.last_ts);
ssn->client.flags &= ~STREAMTCP_FLAG_TIMESTAMP;
ssn->flags |= STREAMTCP_FLAG_TIMESTAMP;
ssn->server.last_pkt_ts = p->ts.tv_sec;
if (ssn->server.last_ts == 0)
ssn->server.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP;
} else {
ssn->client.last_ts = 0;
ssn->server.last_ts = 0;
ssn->client.flags &= ~STREAMTCP_FLAG_TIMESTAMP;
ssn->client.flags &= ~STREAMTCP_FLAG_ZERO_TIMESTAMP;
}
ssn->client.last_ack = TCP_GET_ACK(p);
ssn->server.last_ack = ssn->server.isn + 1;
/** check for the presense of the ws ptr to determine if we
* support wscale at all */
if (ssn->flags & STREAMTCP_FLAG_SERVER_WSCALE && p->tcpvars.ws != NULL) {
ssn->client.wscale = TCP_GET_WSCALE(p);
} else {
ssn->client.wscale = 0;
}
ssn->server.next_win = ssn->server.last_ack + ssn->server.window;
ssn->client.next_win = ssn->client.last_ack + ssn->client.window;
SCLogDebug("ssn %p: ssn->server.next_win %" PRIu32 "", ssn, ssn->server.next_win);
SCLogDebug("ssn %p: ssn->client.next_win %" PRIu32 "", ssn, ssn->client.next_win);
SCLogDebug("ssn %p: ssn->server.isn %" PRIu32 ", ssn->server.next_seq %" PRIu32 ", ssn->server.last_ack %" PRIu32 " (ssn->client.last_ack %" PRIu32 ")",
ssn, ssn->server.isn, ssn->server.next_seq, ssn->server.last_ack, ssn->client.last_ack);
break;
case TH_RST:
case TH_RST|TH_ACK:
if(ValidReset(ssn, p)){
if(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.isn) && SEQ_EQ(TCP_GET_WINDOW(p), 0) && SEQ_EQ(TCP_GET_ACK(p), (ssn->client.isn + 1))) {
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
}
} else
return -1;
break;
default:
SCLogDebug("ssn %p: default case", ssn);
break;
}
return 0;
}
/**
* \brief Function to handle the TCP_SYN_RECV state. The function handles
* SYN, SYN/ACK, ACK, FIN, RST packets and correspondingly changes
* the connection state.
*
* \param tv Thread Variable containig input/output queue, cpu affinity etc.
* \param p Packet which has to be handled in this TCP state.
* \param stt Strean Thread module registered to handle the stream handling
*/
static int StreamTcpPacketStateSynRecv(ThreadVars *tv, Packet *p, StreamTcpThread *stt, TcpSession *ssn) {
if (ssn == NULL)
return -1;
switch (p->tcph->th_flags) {
case TH_SYN:
SCLogDebug("ssn %p: SYN packet on state SYN_RECV... resent", ssn);
break;
case TH_SYN|TH_ACK:
SCLogDebug("ssn %p: SYN/ACK packet on state SYN_RECV... resent", ssn);
break;
case TH_ACK:
/* If the timestamp option is enabled for both the streams, then validate the received packet
timestamp value against the stream->last_ts. If the timestamp is valid then process the packet normally
otherwise the drop the packet (RFC 1323)*/
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!ValidTimestamp(ssn, p))
return -1;
}
if (PKT_IS_TOCLIENT(p)) {
SCLogDebug("ssn %p: ACK received in the wrong direction", ssn);
return -1;
}
if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.next_seq))) {
SCLogDebug("ssn %p: wrong seq nr on packet", ssn);
return -1;
}
ssn->server.last_ack = TCP_GET_ACK(p);
if (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) {
ssn->client.window = TCP_GET_WINDOW(p);
ssn->server.next_win = ssn->server.last_ack + ssn->server.window;
/** window scaling for midstream pickups, we can't do much other
* than assume that it's set to the max value: 14 */
ssn->server.wscale = TCP_WSCALE_MAX;
ssn->client.wscale = TCP_WSCALE_MAX;
}
SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ %" PRIu32 ", ACK %" PRIu32 "",
ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p));
StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED);
SCLogDebug("ssn %p: =~ ssn state is now TCP_ESTABLISHED", ssn);
ssn->client.next_seq += p->payload_len;
ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
ssn->server.next_win = ssn->server.last_ack + ssn->server.window;
SCLogDebug("ssn %p: ssn->server.next_win %" PRIu32 ", ssn->server.last_ack %"PRIu32"", ssn, ssn->server.next_win, ssn->server.last_ack);
break;
case TH_RST:
case TH_RST|TH_ACK:
if(ValidReset(ssn, p)) {
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
} else
return -1;
break;
case TH_FIN:
/*FIN is handled in the same way as in TCP_ESTABLISHED case */;
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!ValidTimestamp(ssn, p))
return -1;
}
if((StreamTcpHandleFin(stt, ssn, p)) == -1)
return -1;
break;
default:
SCLogDebug("ssn %p: default case", ssn);
break;
}
return 0;
}
/**
* \brief Function to handle the TCP_ESTABLISHED state. The function handles
* ACK, FIN, RST packets and correspondingly changes the connection
* state. The function handles the data inside packets and call
* StreamTcpReassembleHandleSegment() to handle the reassembling.
*
* \param tv Thread Variable containig input/output queue, cpu affinity etc.
* \param p Packet which has to be handled in this TCP state.
* \param stt Strean Thread module registered to handle the stream handling
*/
static int StreamTcpPacketStateEstablished(ThreadVars *tv, Packet *p, StreamTcpThread *stt, TcpSession *ssn) {
if (ssn == NULL)
return -1;
switch (p->tcph->th_flags) {
case TH_SYN:
SCLogDebug("ssn %p: SYN packet on state ESTABLISED... resent", ssn);
break;
case TH_SYN|TH_ACK:
SCLogDebug("ssn %p: SYN/ACK packet on state ESTABLISHED... resent", ssn);
break;
case TH_ACK:
case TH_ACK|TH_PUSH:
/* If the timestamp option is enabled for both the streams, then validate the received packet
timestamp value against the stream->last_ts. If the timestamp is valid then process the packet normally
otherwise the drop the packet (RFC 1323) */
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!ValidTimestamp(ssn, p))
return -1;
}
if (PKT_IS_TOSERVER(p)) {
SCLogDebug("ssn %p: =+ pkt (%" PRIu32 ") is to server: SEQ %" PRIu32 ", ACK %" PRIu32 ", WIN %"PRIu16"",
ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p), TCP_GET_WINDOW(p));
if (SEQ_EQ(ssn->client.next_seq, TCP_GET_SEQ(p))) {
ssn->client.next_seq += p->payload_len;
SCLogDebug("ssn %p: ssn->client.next_seq %" PRIu32 "", ssn, ssn->client.next_seq);
}
if (SEQ_GEQ(TCP_GET_SEQ(p), ssn->client.last_ack)) {
if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->client.next_win) ||
ssn->flags & STREAMTCP_FLAG_MIDSTREAM) {
SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->client.next_win %" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->client.next_win);
ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
SCLogDebug("ssn %p: ssn->server.window %"PRIu32"", ssn, ssn->server.window);
if (SEQ_GT(TCP_GET_ACK(p),ssn->server.last_ack))
ssn->server.last_ack = TCP_GET_ACK(p);
if (SEQ_GT((ssn->server.last_ack + ssn->server.window), ssn->server.next_win)) {
ssn->server.next_win = ssn->server.last_ack + ssn->server.window;
SCLogDebug("ssn %p: seq %"PRIu32", updated ssn->server.next_win %" PRIu32 " (win %"PRIu32")", ssn, TCP_GET_SEQ(p), ssn->server.next_win, ssn->server.window);
}
/*If no stream reassembly/application layer protocol inspection, then simple return*/
if (!(ssn->flags & STREAMTCP_FLAG_NOCLIENT_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->client, p);
} else {
SCLogDebug("ssn %p: server => SEQ out of window, packet SEQ %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "), ssn->client.last_ack %" PRIu32 ", ssn->client.next_win %" PRIu32 "(%"PRIu32") (ssn->client.ra_base_seq %"PRIu32")", ssn, TCP_GET_SEQ(p), p->payload_len, TCP_GET_SEQ(p) + p->payload_len, ssn->client.last_ack, ssn->client.next_win, TCP_GET_SEQ(p) + p->payload_len - ssn->client.next_win, ssn->client.ra_base_seq);
}
} else {
SCLogDebug("ssn %p: server => SEQ before last_ack, packet SEQ %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "), ssn->client.last_ack %" PRIu32 ", ssn->client.next_win %" PRIu32 "(%"PRIu32") (ssn->client.ra_base_seq %"PRIu32")", ssn, TCP_GET_SEQ(p), p->payload_len, TCP_GET_SEQ(p) + p->payload_len, ssn->client.last_ack, ssn->client.next_win, TCP_GET_SEQ(p) + p->payload_len - ssn->client.next_win, ssn->client.ra_base_seq);
}
SCLogDebug("ssn %p: next SEQ %" PRIu32 ", last ACK %" PRIu32 ", next win %" PRIu32 ", win %" PRIu32 "",
ssn, ssn->client.next_seq, ssn->server.last_ack, ssn->client.next_win, ssn->client.window);
} else { /* implied to client */
SCLogDebug("ssn %p: =+ pkt (%" PRIu32 ") is to client: SEQ %" PRIu32 ", ACK %" PRIu32 ", WIN %"PRIu16"",
ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p), TCP_GET_WINDOW(p));
/* To get the server window value from the servers packet, when connection
is picked up as midstream */
if ((ssn->flags & STREAMTCP_FLAG_MIDSTREAM) && (ssn->flags & STREAMTCP_FLAG_MIDSTREAM_ESTABLISHED)) {
ssn->server.window = TCP_GET_WINDOW(p);
ssn->server.next_win = ssn->server.last_ack + ssn->server.window;
ssn->flags &= ~STREAMTCP_FLAG_MIDSTREAM_ESTABLISHED;
SCLogDebug("ssn %p: adjusted midstream ssn->server.next_win to %" PRIu32 "", ssn, ssn->server.next_win);
}
if (SEQ_EQ(ssn->server.next_seq, TCP_GET_SEQ(p))) {
ssn->server.next_seq += p->payload_len;
SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", ssn, ssn->server.next_seq);
}
if (SEQ_GEQ(TCP_GET_SEQ(p), ssn->server.last_ack)) {
if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->server.next_win) ||
ssn->flags & STREAMTCP_FLAG_MIDSTREAM) {
SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->server.next_win %" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->server.next_win);
ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
SCLogDebug("ssn %p: ssn->client.window %"PRIu32"", ssn, ssn->client.window);
if (SEQ_GT(TCP_GET_ACK(p),ssn->client.last_ack))
ssn->client.last_ack = TCP_GET_ACK(p);
if (SEQ_GT((ssn->client.last_ack + ssn->client.window), ssn->client.next_win)) {
ssn->client.next_win = ssn->client.last_ack + ssn->client.window;
SCLogDebug("ssn %p: seq %"PRIu32", updated ssn->client.next_win %" PRIu32 " (win %"PRIu32")", ssn, TCP_GET_SEQ(p), ssn->client.next_win, ssn->client.window);
} else {
SCLogDebug("ssn %p: seq %"PRIu32", keeping ssn->client.next_win %" PRIu32 " the same (win %"PRIu32")", ssn, TCP_GET_SEQ(p), ssn->client.next_win, ssn->client.window);
}
/*If no stream reassembly/application layer protocol inspection, then simple return*/
if (!(ssn->flags & STREAMTCP_FLAG_NOSERVER_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->server, p);
} else {
SCLogDebug("ssn %p: client => SEQ out of window, packet SEQ %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "), ssn->server.last_ack %" PRIu32 ", ssn->server.next_win %" PRIu32 "(%"PRIu32") (ssn->server.ra_base_seq %"PRIu32")", ssn, TCP_GET_SEQ(p), p->payload_len, TCP_GET_SEQ(p) + p->payload_len, ssn->server.last_ack, ssn->server.next_win, TCP_GET_SEQ(p) + p->payload_len - ssn->server.next_win, ssn->server.ra_base_seq);
}
} else {
SCLogDebug("ssn %p: client => SEQ before last ack, packet SEQ %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "), ssn->server.last_ack %" PRIu32 ", ssn->server.next_win %" PRIu32 "(%"PRIu32") (ssn->server.ra_base_seq %"PRIu32")", ssn, TCP_GET_SEQ(p), p->payload_len, TCP_GET_SEQ(p) + p->payload_len, ssn->server.last_ack, ssn->server.next_win, TCP_GET_SEQ(p) + p->payload_len - ssn->server.next_win, ssn->server.ra_base_seq);
}
SCLogDebug("ssn %p: next SEQ %" PRIu32 ", last ACK %" PRIu32 ", next win %" PRIu32 ", win %" PRIu32 "",
ssn, ssn->server.next_seq, ssn->client.last_ack, ssn->server.next_win, ssn->server.window);
}
break;
case TH_FIN:
case TH_FIN|TH_ACK:
case TH_FIN|TH_ACK|TH_PUSH:
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!ValidTimestamp(ssn, p))
return -1;
}
SCLogDebug("StreamTcpPacketStateEstablished (%p): FIN received SEQ %" PRIu32 ", last ACK %" PRIu32 ", next win %" PRIu32 ", win %" PRIu32 "",
ssn, ssn->server.next_seq, ssn->client.last_ack, ssn->server.next_win, ssn->server.window);
if((StreamTcpHandleFin(stt, ssn, p)) == -1)
return -1;
break;
case TH_RST:
case TH_RST|TH_ACK:
if(ValidReset(ssn, p)) {
if(PKT_IS_TOSERVER(p)) {
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: Reset received and state changed to TCP_CLOSED", ssn);
ssn->client.next_seq = TCP_GET_ACK(p);
ssn->server.next_seq = TCP_GET_SEQ(p) + p->payload_len + 1;
SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", ssn, ssn->server.next_seq);
ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
if (SEQ_GT(TCP_GET_ACK(p),ssn->server.last_ack))
ssn->server.last_ack = TCP_GET_ACK(p);
if (! (ssn->flags & STREAMTCP_FLAG_NOCLIENT_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->client, p);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK %" PRIu32 "",
ssn, ssn->client.next_seq, ssn->server.last_ack);
} else {
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: Reset received and state changed to TCP_CLOSED", ssn);
ssn->server.next_seq = TCP_GET_SEQ(p) + p->payload_len + 1;
ssn->client.next_seq = TCP_GET_ACK(p);
SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", ssn, ssn->server.next_seq);
ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
if (SEQ_GT(TCP_GET_ACK(p),ssn->client.last_ack))
ssn->client.last_ack = TCP_GET_ACK(p);
if (!(ssn->flags & STREAMTCP_FLAG_NOSERVER_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->server, p);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK %" PRIu32 "",
ssn, ssn->server.next_seq, ssn->client.last_ack);
}
} else
return -1;
break;
default:
SCLogDebug("ssn %p: default case", ssn);
break;
}
return 0;
}
/**
* \brief Function to handle the FIN packets for states TCP_SYN_RECV and
* TCP_ESTABLISHED and changes to another TCP state as required.
*
* \param tv Thread Variable containig input/output queue, cpu affinity etc.
* \param p Packet which has to be handled in this TCP state.
* \param stt Strean Thread module registered to handle the stream handling
*/
static int StreamTcpHandleFin(StreamTcpThread *stt, TcpSession *ssn, Packet *p) {
if (PKT_IS_TOSERVER(p)) {
SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ %" PRIu32 ", ACK %" PRIu32 "",
ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p));
if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq) || SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window))) {
SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 " != %" PRIu32 " from stream",
ssn, TCP_GET_SEQ(p), ssn->client.next_seq);
return -1;
}
StreamTcpPacketSetState(p, ssn, TCP_CLOSE_WAIT);
SCLogDebug("ssn %p: state changed to TCP_CLOSE_WAIT", ssn);
ssn->client.next_seq = TCP_GET_ACK(p);
ssn->server.next_seq = TCP_GET_SEQ(p) + p->payload_len + 1;
SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", ssn, ssn->server.next_seq);
ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
if (SEQ_GT(TCP_GET_ACK(p),ssn->server.last_ack))
ssn->server.last_ack = TCP_GET_ACK(p);
if (! (ssn->flags & STREAMTCP_FLAG_NOCLIENT_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->client, p);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK %" PRIu32 "",
ssn, ssn->client.next_seq, ssn->server.last_ack);
} else { /* implied to client */
SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ %" PRIu32 ", ACK %" PRIu32 "",
ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p));
if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) || SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window))) {
SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 " != %" PRIu32 " from stream",
ssn, TCP_GET_SEQ(p), ssn->server.next_seq);
return -1;
}
StreamTcpPacketSetState(p, ssn, TCP_FIN_WAIT1);
SCLogDebug("ssn %p: state changed to TCP_FIN_WAIT1", ssn);
ssn->server.next_seq = TCP_GET_SEQ(p) + p->payload_len + 1;
ssn->client.next_seq = TCP_GET_ACK(p);
SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", ssn, ssn->server.next_seq);
ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
if (SEQ_GT(TCP_GET_ACK(p),ssn->client.last_ack))
ssn->client.last_ack = TCP_GET_ACK(p);
if (!(ssn->flags & STREAMTCP_FLAG_NOSERVER_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->server, p);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK %" PRIu32 "",
ssn, ssn->server.next_seq, ssn->client.last_ack);
}
return 0;
}
/**
* \brief Function to handle the TCP_FIN_WAIT1 state. The function handles
* ACK, FIN, RST packets and correspondingly changes the connection
* state.
*
* \param tv Thread Variable containig input/output queue, cpu affinity etc.
* \param p Packet which has to be handled in this TCP state.
* \param stt Strean Thread module registered to handle the stream handling
*/
static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p, StreamTcpThread *stt, TcpSession *ssn) {
if (ssn == NULL)
return -1;
switch (p->tcph->th_flags) {
case TH_ACK:
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!ValidTimestamp(ssn, p))
return -1;
}
if (PKT_IS_TOSERVER(p)) {
SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ %" PRIu32 ", ACK %" PRIu32 "",
ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p));
StreamTcpPacketSetState(p, ssn, TCP_FIN_WAIT2);
SCLogDebug("ssn %p: state changed to TCP_FIN_WAIT2", ssn);
ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
if (SEQ_GT(TCP_GET_ACK(p),ssn->server.last_ack))
ssn->server.last_ack = TCP_GET_ACK(p);
if (! (ssn->flags & STREAMTCP_FLAG_NOCLIENT_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->client, p);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK %" PRIu32 "",
ssn, ssn->client.next_seq, ssn->server.last_ack);
} else { /* implied to client */
SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ %" PRIu32 ", ACK %" PRIu32 "",
ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p));
StreamTcpPacketSetState(p, ssn, TCP_FIN_WAIT2);
SCLogDebug("ssn %p: state changed to TCP_FIN_WAIT2", ssn);
ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
if (SEQ_GT(TCP_GET_ACK(p),ssn->client.last_ack))
ssn->client.last_ack = TCP_GET_ACK(p);
if (!(ssn->flags & STREAMTCP_FLAG_NOSERVER_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->server, p);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK %" PRIu32 "",
ssn, ssn->server.next_seq, ssn->client.last_ack);
}
break;
case TH_FIN:
case TH_FIN|TH_ACK:
case TH_FIN|TH_ACK|TH_PUSH:
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!ValidTimestamp(ssn, p))
return -1;
}
if (PKT_IS_TOSERVER(p)) {
SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ %" PRIu32 ", ACK %" PRIu32 "",
ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p));
if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq || SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window)))) {
SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 " != %" PRIu32 " from stream",
ssn, TCP_GET_SEQ(p), ssn->client.next_seq);
return -1;
}
StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT);
SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn);
ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
if (SEQ_GT(TCP_GET_ACK(p),ssn->server.last_ack))
ssn->server.last_ack = TCP_GET_ACK(p);
if (! (ssn->flags & STREAMTCP_FLAG_NOCLIENT_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->client, p);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK %" PRIu32 "",
ssn, ssn->client.next_seq, ssn->server.last_ack);
} else { /* implied to client */
SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ %" PRIu32 ", ACK %" PRIu32 "",
ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p));
if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) || SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window))) {
SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 " != %" PRIu32 " from stream",
ssn, TCP_GET_SEQ(p), ssn->server.next_seq);
return -1;
}
StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT);
SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn);
ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
if (SEQ_GT(TCP_GET_ACK(p),ssn->client.last_ack))
ssn->client.last_ack = TCP_GET_ACK(p);
if (!(ssn->flags & STREAMTCP_FLAG_NOSERVER_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->server, p);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK %" PRIu32 "",
ssn, ssn->server.next_seq, ssn->client.last_ack);
}
break;
case TH_RST:
case TH_RST|TH_ACK:
if(ValidReset(ssn, p)) {
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED", ssn);
}
else
return -1;
break;
default:
SCLogDebug("ssn (%p): default case", ssn);
break;
}
return 0;
}
/**
* \brief Function to handle the TCP_FIN_WAIT2 state. The function handles
* ACK, RST, FIN packets and correspondingly changes the connection
* state.
*
* \param tv Thread Variable containig input/output queue, cpu affinity etc.
* \param p Packet which has to be handled in this TCP state.
* \param stt Strean Thread module registered to handle the stream handling
*/
static int StreamTcpPacketStateFinWait2(ThreadVars *tv, Packet *p, StreamTcpThread *stt, TcpSession *ssn) {
if (ssn == NULL)
return -1;
switch (p->tcph->th_flags) {
case TH_ACK:
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!ValidTimestamp(ssn, p))
return -1;
}
if (PKT_IS_TOSERVER(p)) {
SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ %" PRIu32 ", ACK %" PRIu32 "",
ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p));
if (TCP_GET_SEQ(p) != ssn->client.next_seq) {
SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 " != %" PRIu32 " from stream",
ssn, TCP_GET_SEQ(p), ssn->client.next_seq);
return -1;
}
StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT);
SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn);
ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
if (SEQ_GT(TCP_GET_ACK(p),ssn->server.last_ack))
ssn->server.last_ack = TCP_GET_ACK(p);
if (! (ssn->flags & STREAMTCP_FLAG_NOCLIENT_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->client, p);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK %" PRIu32 "",
ssn, ssn->client.next_seq, ssn->server.last_ack);
} else { /* implied to client */
SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ %" PRIu32 ", ACK %" PRIu32 "",
ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p));
if (TCP_GET_SEQ(p) != ssn->server.next_seq) {
SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 " != %" PRIu32 " from stream",
ssn, TCP_GET_SEQ(p), ssn->server.next_seq);
return -1;
}
StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT);
SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn);
ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
if (SEQ_GT(TCP_GET_ACK(p),ssn->client.last_ack))
ssn->client.last_ack = TCP_GET_ACK(p);
if (!(ssn->flags & STREAMTCP_FLAG_NOSERVER_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->server, p);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK %" PRIu32 "",
ssn, ssn->server.next_seq, ssn->client.last_ack);
}
break;
case TH_RST:
case TH_RST|TH_ACK:
if(ValidReset(ssn, p)) {
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED", ssn);
}
else
return -1;
break;
case TH_FIN:
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!ValidTimestamp(ssn, p))
return -1;
}
if (PKT_IS_TOSERVER(p)) {
SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ %" PRIu32 ", ACK %" PRIu32 "",
ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p));
if (SEQ_LT(TCP_GET_SEQ(p), ssn->client.next_seq || SEQ_GT(TCP_GET_SEQ(p), (ssn->client.last_ack + ssn->client.window)))) {
SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 " != %" PRIu32 " from stream",
ssn, TCP_GET_SEQ(p), ssn->client.next_seq);
return -1;
}
StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT);
SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn);
ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
if (SEQ_GT(TCP_GET_ACK(p),ssn->server.last_ack))
ssn->server.last_ack = TCP_GET_ACK(p);
if (! (ssn->flags & STREAMTCP_FLAG_NOCLIENT_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->client, p);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK %" PRIu32 "",
ssn, ssn->client.next_seq, ssn->server.last_ack);
} else { /* implied to client */
SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ %" PRIu32 ", ACK %" PRIu32 "",
ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p));
if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) || SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window))) {
SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 " != %" PRIu32 " from stream",
ssn, TCP_GET_SEQ(p), ssn->server.next_seq);
return -1;
}
StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT);
SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn);
ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
if (SEQ_GT(TCP_GET_ACK(p),ssn->client.last_ack))
ssn->client.last_ack = TCP_GET_ACK(p);
if (!(ssn->flags & STREAMTCP_FLAG_NOSERVER_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->server, p);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK %" PRIu32 "",
ssn, ssn->server.next_seq, ssn->client.last_ack);
}
break;
default:
SCLogDebug("ssn %p: default case", ssn);
break;
}
return 0;
}
/**
* \brief Function to handle the TCP_CLOSING state. Upon arrival of ACK
* the connection goes to TCP_TIME_WAIT state. The state has been
* reached as both end application has been closed.
*
* \param tv Thread Variable containig input/output queue, cpu affinity etc.
* \param p Packet which has to be handled in this TCP state.
* \param stt Strean Thread module registered to handle the stream handling
*/
static int StreamTcpPacketStateClosing(ThreadVars *tv, Packet *p, StreamTcpThread *stt, TcpSession *ssn) {
if (ssn == NULL)
return -1;
switch(p->tcph->th_flags) {
case TH_ACK:
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!ValidTimestamp(ssn, p))
return -1;
}
if (PKT_IS_TOSERVER(p)) {
SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ %" PRIu32 ", ACK %" PRIu32 "",
ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p));
if (TCP_GET_SEQ(p) != ssn->client.next_seq) {
SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 " != %" PRIu32 " from stream",
ssn, TCP_GET_SEQ(p), ssn->client.next_seq);
return -1;
}
StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT);
SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn);
ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
if (SEQ_GT(TCP_GET_ACK(p),ssn->server.last_ack))
ssn->server.last_ack = TCP_GET_ACK(p);
if (! (ssn->flags & STREAMTCP_FLAG_NOCLIENT_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->client, p);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK %" PRIu32 "",
ssn, ssn->client.next_seq, ssn->server.last_ack);
} else { /* implied to client */
SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ %" PRIu32 ", ACK %" PRIu32 "",
ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p));
if (TCP_GET_SEQ(p) != ssn->server.next_seq) {
SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 " != %" PRIu32 " from stream",
ssn, TCP_GET_SEQ(p), ssn->server.next_seq);
return -1;
}
StreamTcpPacketSetState(p, ssn, TCP_TIME_WAIT);
SCLogDebug("ssn %p: state changed to TCP_TIME_WAIT", ssn);
ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
if (SEQ_GT(TCP_GET_ACK(p),ssn->client.last_ack))
ssn->client.last_ack = TCP_GET_ACK(p);
if (!(ssn->flags & STREAMTCP_FLAG_NOSERVER_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->server, p);
SCLogDebug("StreamTcpPacketStateClosing (%p): =+ next SEQ %" PRIu32 ", last ACK %" PRIu32 "",
ssn, ssn->server.next_seq, ssn->client.last_ack);
}
break;
default:
SCLogDebug("ssn %p: default case", ssn);
break;
}
return 0;
}
/**
* \brief Function to handle the TCP_CLOSE_WAIT state. Upon arrival of FIN
* packet from server the connection goes to TCP_LAST_ACK state.
* The state is possible only for server host.
*
* \param tv Thread Variable containig input/output queue, cpu affinity etc.
* \param p Packet which has to be handled in this TCP state.
* \param stt Strean Thread module registered to handle the stream handling
*/
static int StreamTcpPacketStateCloseWait(ThreadVars *tv, Packet *p, StreamTcpThread *stt, TcpSession *ssn) {
if (ssn == NULL)
return -1;
switch(p->tcph->th_flags) {
case TH_FIN:
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!ValidTimestamp(ssn, p))
return -1;
}
if (PKT_IS_TOCLIENT(p)) {
SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ %" PRIu32 ", ACK %" PRIu32 "",
ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p));
if (SEQ_LT(TCP_GET_SEQ(p), ssn->server.next_seq) || SEQ_GT(TCP_GET_SEQ(p), (ssn->server.last_ack + ssn->server.window))) {
SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 " != %" PRIu32 " from stream",
ssn, TCP_GET_SEQ(p), ssn->server.next_seq);
return -1;
}
StreamTcpPacketSetState(p, ssn, TCP_LAST_ACK);
SCLogDebug("ssn %p: state changed to TCP_LAST_ACK", ssn);
ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
if (SEQ_GT(TCP_GET_ACK(p),ssn->client.last_ack))
ssn->client.last_ack = TCP_GET_ACK(p);
if (!(ssn->flags & STREAMTCP_FLAG_NOSERVER_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->server, p);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK %" PRIu32 "",
ssn, ssn->server.next_seq, ssn->client.last_ack);
}
break;
default:
SCLogDebug("ssn %p: default case", ssn);
break;
}
return 0;
}
/**
* \brief Function to handle the TCP_LAST_ACK state. Upon arrival of ACK
* the connection goes to TCP_CLOSED state and stream memory is
* returned back to pool. The state is possible only for server host.
*
* \param tv Thread Variable containig input/output queue, cpu affinity etc.
* \param p Packet which has to be handled in this TCP state.
* \param stt Strean Thread module registered to handle the stream handling
*/
static int StreamTcpPakcetStateLastAck(ThreadVars *tv, Packet *p, StreamTcpThread *stt, TcpSession *ssn) {
if (ssn == NULL)
return -1;
switch(p->tcph->th_flags) {
case TH_ACK:
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!ValidTimestamp(ssn, p))
return -1;
}
if (PKT_IS_TOSERVER(p)) {
SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ %" PRIu32 ", ACK %" PRIu32 "",
ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p));
if (TCP_GET_SEQ(p) != ssn->client.next_seq) {
SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 " != %" PRIu32 " from stream",
ssn, TCP_GET_SEQ(p), ssn->client.next_seq);
return -1;
}
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: state changed to TCP_CLOSED", ssn);
ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
if (SEQ_GT(TCP_GET_ACK(p),ssn->server.last_ack))
ssn->server.last_ack = TCP_GET_ACK(p);
if (! (ssn->flags & STREAMTCP_FLAG_NOCLIENT_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->client, p);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK %" PRIu32 "",
ssn, ssn->client.next_seq, ssn->server.last_ack);
}
break;
default:
SCLogDebug("ssn %p: default case", ssn);
break;
}
return 0;
}
/**
* \brief Function to handle the TCP_TIME_WAIT state. Upon arrival of ACK
* the connection goes to TCP_CLOSED state and stream memory is
* returned back to pool.
*
* \param tv Thread Variable containig input/output queue, cpu affinity etc.
* \param p Packet which has to be handled in this TCP state.
* \param stt Strean Thread module registered to handle the stream handling
*/
static int StreamTcpPacketStateTimeWait(ThreadVars *tv, Packet *p, StreamTcpThread *stt, TcpSession *ssn) {
if (ssn == NULL)
return -1;
switch(p->tcph->th_flags) {
case TH_ACK:
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!ValidTimestamp(ssn, p))
return -1;
}
if (PKT_IS_TOSERVER(p)) {
SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ %" PRIu32 ", ACK %" PRIu32 "",
ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p));
if (TCP_GET_SEQ(p) != ssn->client.next_seq) {
SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 " != %" PRIu32 " from stream",
ssn, TCP_GET_SEQ(p), ssn->client.next_seq);
return -1;
}
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: state changed to TCP_CLOSED", ssn);
ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
if (SEQ_GT(TCP_GET_ACK(p),ssn->server.last_ack))
ssn->server.last_ack = TCP_GET_ACK(p);
if (! (ssn->flags & STREAMTCP_FLAG_NOCLIENT_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->client, p);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK %" PRIu32 "",
ssn, ssn->client.next_seq, ssn->server.last_ack);
} else {
SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to client: SEQ %" PRIu32 ", ACK %" PRIu32 "",
ssn, p->payload_len, TCP_GET_SEQ(p), TCP_GET_ACK(p));
if (TCP_GET_SEQ(p) != ssn->server.next_seq) {
SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 " != %" PRIu32 " from stream",
ssn, TCP_GET_SEQ(p), ssn->server.next_seq);
return -1;
}
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: state changed to TCP_CLOSED", ssn);
ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
if (SEQ_GT(TCP_GET_ACK(p),ssn->client.last_ack))
ssn->client.last_ack = TCP_GET_ACK(p);
if (!(ssn->flags & STREAMTCP_FLAG_NOSERVER_REASSEMBLY))
StreamTcpReassembleHandleSegment(stt->ra_ctx, ssn, &ssn->server, p);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK %" PRIu32 "",
ssn, ssn->server.next_seq, ssn->client.last_ack);
}
break;
default:
SCLogDebug("ssn %p: default case", ssn);
break;
}
return 0;
}
/* flow is and stays locked */
static int StreamTcpPacket (ThreadVars *tv, Packet *p, StreamTcpThread *stt) {
TcpSession *ssn = (TcpSession *)p->flow->protoctx;
if (ssn == NULL || ssn->state == TCP_NONE) {
if (StreamTcpPacketStateNone(tv, p, stt, ssn) == -1)
return -1;
} else {
switch (ssn->state) {
case TCP_SYN_SENT:
if(StreamTcpPacketStateSynSent(tv, p, stt, ssn))
return -1;
break;
case TCP_SYN_RECV:
if(StreamTcpPacketStateSynRecv(tv, p, stt, ssn))
return -1;
break;
case TCP_ESTABLISHED:
if(StreamTcpPacketStateEstablished(tv, p, stt, ssn))
return -1;
break;
case TCP_FIN_WAIT1:
if(StreamTcpPacketStateFinWait1(tv, p, stt, ssn))
return -1;
break;
case TCP_FIN_WAIT2:
if(StreamTcpPacketStateFinWait2(tv, p, stt, ssn))
return -1;
break;
case TCP_CLOSING:
if(StreamTcpPacketStateClosing(tv, p, stt, ssn))
return -1;
break;
case TCP_CLOSE_WAIT:
if(StreamTcpPacketStateCloseWait(tv, p, stt, ssn))
return -1;
break;
case TCP_LAST_ACK:
if(StreamTcpPakcetStateLastAck(tv, p, stt, ssn))
return -1;
break;
case TCP_TIME_WAIT:
if(StreamTcpPacketStateTimeWait(tv, p, stt, ssn))
return -1;
break;
case TCP_CLOSED:
//printf("StreamTcpPacket: packet received on closed state\n");
break;
default:
//printf("StreamTcpPacket: packet received on default state\n");
break;
}
}
/* Process stream smsgs we may have in queue */
StreamTcpReassembleProcessAppLayer(stt->ra_ctx);
return 0;
}
int StreamTcp (ThreadVars *tv, Packet *p, void *data, PacketQueue *pq)
{
StreamTcpThread *stt = (StreamTcpThread *)data;
if (!(PKT_IS_TCP(p)))
return 0;
if (p->flow == NULL)
return 0;
mutex_lock(&p->flow->m);
StreamTcpPacket(tv, p, stt);
mutex_unlock(&p->flow->m);
stt->pkts++;
return 0;
}
int StreamTcpThreadInit(ThreadVars *tv, void *initdata, void **data)
{
StreamTcpThread *stt = malloc(sizeof(StreamTcpThread));
if (stt == NULL) {
return -1;
}
memset(stt, 0, sizeof(StreamTcpThread));
*data = (void *)stt;
stt->counter_tcp_sessions = PerfTVRegisterCounter("tcp.sessions", tv, TYPE_UINT64, "NULL");
tv->pca = PerfGetAllCountersArray(&tv->pctx);
PerfAddToClubbedTMTable(tv->name, &tv->pctx);
/* init reassembly ctx */
stt->ra_ctx = StreamTcpReassembleInitThreadCtx();
if (stt->ra_ctx == NULL)
return -1;
SCLogDebug("StreamTcp thread specific ctx online at %p, reassembly ctx %p", stt, stt->ra_ctx);
return 0;
}
int StreamTcpThreadDeinit(ThreadVars *tv, void *data)
{
StreamTcpThread *stt = (StreamTcpThread *)data;
if (stt == NULL) {
return 0;
}
/* XXX */
/* free reassembly ctx */
/* clear memory */
memset(stt, 0, sizeof(StreamTcpThread));
free(stt);
return 0;
}
void StreamTcpExitPrintStats(ThreadVars *tv, void *data) {
StreamTcpThread *stt = (StreamTcpThread *)data;
if (stt == NULL) {
return;
}
SCLogInfo("(%s) Packets %" PRIu64 "", tv->name, stt->pkts);
}
/**
* \brief Function to check the validity of the RST packets based on the target
* OS of the given packet.
*
* \param ssn TCP session to which the given packet belongs
* \param p Packet which has to be checked for its validity
*/
static int ValidReset(TcpSession *ssn, Packet *p) {
uint8_t os_policy;
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!ValidTimestamp(ssn, p))
return -1;
}
if (PKT_IS_TOSERVER(p))
os_policy = ssn->server.os_policy;
else
os_policy = ssn->client.os_policy;
switch (os_policy) {
case OS_POLICY_HPUX11:
if(PKT_IS_TOSERVER(p)){
if(SEQ_GEQ(TCP_GET_SEQ(p), ssn->client.next_seq)) {
SCLogDebug("reset is Valid! Packet SEQ: %" PRIu32 "", TCP_GET_SEQ(p));
return 1;
} else {
SCLogDebug("reset is not Valid! Packet SEQ: %" PRIu32 " and server SEQ: %" PRIu32 "", TCP_GET_SEQ(p), ssn->client.next_seq);
return 0;
}
} else { /* implied to client */
if(SEQ_GEQ(TCP_GET_SEQ(p), ssn->server.next_seq)) {
SCLogDebug("reset is valid! Packet SEQ: %" PRIu32 "", TCP_GET_SEQ(p));
return 1;
} else {
SCLogDebug("reset is not valid! Packet SEQ: %" PRIu32 " and client SEQ: %" PRIu32 "", TCP_GET_SEQ(p), ssn->server.next_seq);
return 0;
}
}
break;
case OS_POLICY_OLD_LINUX:
case OS_POLICY_LINUX:
case OS_POLICY_SOLARIS:
if(PKT_IS_TOSERVER(p)){
if(SEQ_GEQ((TCP_GET_SEQ(p)+p->payload_len), ssn->client.last_ack)) { /*window base is needed !!*/
if(SEQ_LT(TCP_GET_SEQ(p), (ssn->client.next_seq + ssn->client.window))) {
SCLogDebug("reset is Valid! Packet SEQ: %" PRIu32 "", TCP_GET_SEQ(p));
return 1;
}
} else {
SCLogDebug("reset is not valid! Packet SEQ: %" PRIu32 " and server SEQ: %" PRIu32 "", TCP_GET_SEQ(p), ssn->client.next_seq);
return 0;
}
} else { /* implied to client */
if(SEQ_GEQ((TCP_GET_SEQ(p) + p->payload_len), ssn->server.last_ack)) { /*window base is needed !!*/
if(SEQ_LT(TCP_GET_SEQ(p), (ssn->server.next_seq + ssn->server.window))) {
SCLogDebug("reset is Valid! Packet SEQ: %" PRIu32 "", TCP_GET_SEQ(p));
return 1;
}
} else {
SCLogDebug("reset is not valid! Packet SEQ: %" PRIu32 " and client SEQ: %" PRIu32 "", TCP_GET_SEQ(p), ssn->server.next_seq);
return 0;
}
}
break;
default:
case OS_POLICY_BSD:
case OS_POLICY_FIRST:
case OS_POLICY_HPUX10:
case OS_POLICY_IRIX:
case OS_POLICY_MACOS:
case OS_POLICY_LAST:
case OS_POLICY_WINDOWS:
case OS_POLICY_WINDOWS2K3:
case OS_POLICY_VISTA:
if(PKT_IS_TOSERVER(p)) {
if(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.next_seq)) {
SCLogDebug("reset is valid! Packet SEQ: %" PRIu32 "", TCP_GET_SEQ(p));
return 1;
} else {
SCLogDebug("reset is not valid! Packet SEQ: %" PRIu32 " and server SEQ: %" PRIu32 "", TCP_GET_SEQ(p), ssn->client.next_seq);
return 0;
}
} else { /* implied to client */
if(SEQ_EQ(TCP_GET_SEQ(p), ssn->server.next_seq)) {
SCLogDebug("reset is valid! Packet SEQ: %" PRIu32 "", TCP_GET_SEQ(p));
return 1;
} else {
SCLogDebug("reset is not valid! Packet SEQ: %" PRIu32 " and client SEQ: %" PRIu32 "", TCP_GET_SEQ(p), ssn->server.next_seq);
return 0;
}
}
break;
}
return 0;
}
/**
* \brief Function to return the FLOW state depending upon the TCP session state.
*
* \param s TCP session of which the state has to be returned
* \retval The FLOW_STATE_ depends upon the TCP sesison state, default is FLOW_STATE_CLOSED
*/
int StreamTcpGetFlowState(void *s) {
TcpSession *ssn = (TcpSession *)s;
if (ssn == NULL)
return FLOW_STATE_CLOSED;
switch(ssn->state) {
case TCP_NONE:
case TCP_SYN_SENT:
case TCP_SYN_RECV:
case TCP_LISTEN:
return FLOW_STATE_NEW;
case TCP_ESTABLISHED:
return FLOW_STATE_ESTABLISHED;
case TCP_FIN_WAIT1:
case TCP_FIN_WAIT2:
case TCP_CLOSING:
case TCP_LAST_ACK:
case TCP_TIME_WAIT:
case TCP_CLOSE_WAIT:
case TCP_CLOSED:
return FLOW_STATE_CLOSED;
}
return FLOW_STATE_CLOSED;
}
/**
* \brief Function to check the validity of the received timestamp based on the target
* OS of the given stream.
*
* \param ssn TCP session to which the given packet belongs
* \param p Packet which has to be checked for its validity
* \retval If timestamp is valid, function returns 1 otherwise 0
*/
static int ValidTimestamp (TcpSession *ssn, Packet *p) {
TcpStream *sender_stream;
TcpStream *receiver_stream;
uint8_t ret = 1;
uint8_t check_ts = 1;
if (PKT_IS_TOSERVER(p)) {
sender_stream = &ssn->client;
receiver_stream = &ssn->server;
} else {
sender_stream = &ssn->server;
receiver_stream = &ssn->client;
}
if (p->tcpvars.ts != NULL) {
uint32_t ts = TCP_GET_TSVAL(p);
if (sender_stream->flags & STREAMTCP_FLAG_ZERO_TIMESTAMP) {
/*The 3whs used the timestamp with 0 value. */
switch (receiver_stream->os_policy) {
case OS_POLICY_LINUX:
case OS_POLICY_WINDOWS2K3:
/*Linux and windows 2003 does not allow the use of 0 as timestamp
in the 3whs. */
ssn->flags &= ~STREAMTCP_FLAG_TIMESTAMP;
check_ts = 0;
break;
case OS_POLICY_OLD_LINUX:
case OS_POLICY_WINDOWS:
case OS_POLICY_VISTA:
sender_stream->flags &= ~STREAMTCP_FLAG_ZERO_TIMESTAMP;
if (SEQ_EQ(sender_stream->next_seq, TCP_GET_SEQ(p))) {
sender_stream->last_ts = ts;
check_ts = 0; /*next packet will be checked for validity
and stream TS has been updated with this one.*/
}
break;
default:
break;
}
}
if (receiver_stream->os_policy == OS_POLICY_HPUX11) {
/*HPUX11 igoners the timestamp of out of order packets*/
if (!SEQ_EQ(sender_stream->next_seq, TCP_GET_SEQ(p)))
check_ts = 0;
}
if (ts == 0) {
switch (receiver_stream->os_policy) {
case OS_POLICY_OLD_LINUX:
case OS_POLICY_WINDOWS:
case OS_POLICY_WINDOWS2K3:
case OS_POLICY_VISTA:
case OS_POLICY_SOLARIS:
/*Old Linux and windows allowed packet with 0 timestamp.*/
break;
default:
/* other OS simply drop the pakcet with 0 timestamp, when 3whs
has valid timestamp*/
return 0;
}
}
if (check_ts) {
int32_t result = 0;
if (receiver_stream->os_policy == OS_POLICY_LINUX) {
result = (int32_t) ((ts - sender_stream->last_ts) + 1); /* Linux accepts TS which are off by one.*/
} else {
result = (int32_t) (ts - sender_stream->last_ts);
}
if (sender_stream->last_pkt_ts == 0 && (ssn->flags & STREAMTCP_FLAG_MIDSTREAM))
sender_stream->last_pkt_ts = p->ts.tv_sec;
if (result < 0) {
SCLogDebug("timestamp is not valid sender_stream->last_ts %" PRIu32 " p->tcpvars->ts %" PRIu32 " result %" PRId32 "", sender_stream->last_ts, ts, result);
ret = 0;
} else if ((sender_stream->last_ts != 0) && (((uint32_t) p->ts.tv_sec) > sender_stream->last_pkt_ts + PAWS_24DAYS)) {
SCLogDebug("packet is not valid sender_stream->last_pkt_ts %" PRIu32 " p->ts.tv_sec %" PRIu32 "", sender_stream->last_pkt_ts, (uint32_t) p->ts.tv_sec);
ret = 0;
}
if (ret == 1) {
/*Update the timestamp and last seen packet time for this stream.*/
if (SEQ_EQ(sender_stream->next_seq, TCP_GET_SEQ(p)))
sender_stream->last_ts = ts;
sender_stream->last_pkt_ts = p->ts.tv_sec;
}
if (ret == 0) {
/*if the timestamp of packet is not valid then, check if the current
stream timestamp is not so old. if so then we need to accept the packet
and update the stream->last_ts (RFC 1323)*/
if ((SEQ_EQ(sender_stream->next_seq, TCP_GET_SEQ(p)))
&& (((uint32_t) p->ts.tv_sec > (sender_stream->last_pkt_ts + PAWS_24DAYS)))) {
sender_stream->last_ts = ts;
sender_stream->last_pkt_ts = p->ts.tv_sec;
ret = 1;
}
}
}
} else {
/* Solaris stops using timestamps if a packet is received
without a timestamp and timestamps were used on that stream. */
if (receiver_stream->os_policy == OS_POLICY_SOLARIS)
ssn->flags &= ~STREAMTCP_FLAG_TIMESTAMP;
}
return ret;
}
#ifdef UNITTESTS
/**
* \test Test the allocation of TCP session for a given packet from the
* ssn_pool.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest01 (void) {
Packet p;
Flow f;
memset (&p, 0, sizeof(Packet));
memset (&f, 0, sizeof(Flow));
p.flow = &f;
int ret = 0;
StreamTcpInitConfig(TRUE);
TcpSession *ssn = StreamTcpNewSession(&p);
if (ssn == NULL) {
printf("Session can not be allocated \n");
goto end;
}
f.protoctx = ssn;
if (ssn->aldata != NULL) {
printf("AppLayer field not set to NULL \n");
goto end;
}
if (ssn->state != 0) {
printf("TCP state field not set to 0 \n");
goto end;
}
StreamTcpSessionPktFree(&p);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
return ret;
}
/**
* \test Test the deallocation of TCP session for a given packet and return
* the memory back to ssn_pool and corresponding segments to segment
* pool.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest02 (void) {
Packet p;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
u_int8_t payload[4];
TCPHdr tcph;
memset (&p, 0, sizeof(Packet));
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof (StreamTcpThread));
memset(&tcph, 0, sizeof (TCPHdr));
p.flow = &f;
tcph.th_win = htons(5480);
tcph.th_flags = TH_SYN;
p.tcph = &tcph;
p.flowflags = FLOW_PKT_TOSERVER;
int ret = 0;
StreamTcpInitConfig(TRUE);
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
p.tcph->th_ack = htonl(1);
p.tcph->th_flags = TH_SYN | TH_ACK;
p.flowflags = FLOW_PKT_TOCLIENT;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
p.tcph->th_ack = htonl(1);
p.tcph->th_seq = htonl(1);
p.tcph->th_flags = TH_ACK;
p.flowflags = FLOW_PKT_TOSERVER;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
p.tcph->th_ack = htonl(1);
p.tcph->th_seq = htonl(2);
p.tcph->th_flags = TH_PUSH | TH_ACK;
p.flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x41, 3); /*AAA*/
p.payload = payload;
p.payload_len = 3;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
p.flowflags = FLOW_PKT_TOCLIENT;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
p.tcph->th_ack = htonl(1);
p.tcph->th_seq = htonl(6);
p.tcph->th_flags = TH_PUSH | TH_ACK;
p.flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x42, 3); /*BBB*/
p.payload = payload;
p.payload_len = 3;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
p.flowflags = FLOW_PKT_TOCLIENT;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
StreamTcpSessionPktFree(&p);
if (p.flow->protoctx != NULL)
goto end;
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
return ret;
}
/**
* \test Test the setting up a TCP session when we missed the intial
* SYN packet of the session. The session is setup only if midstream
* sessions are allowed to setup.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest03 (void) {
Packet p;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
memset (&p, 0, sizeof(Packet));
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof (StreamTcpThread));
memset(&tcph, 0, sizeof (TCPHdr));
p.flow = &f;
StreamTcpInitConfig(TRUE);
tcph.th_win = htons(5480);
tcph.th_seq = htonl(10);
tcph.th_ack = htonl(20);
tcph.th_flags = TH_SYN|TH_ACK;
p.tcph = &tcph;
int ret = 0;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
p.tcph->th_seq = htonl(20);
p.tcph->th_ack = htonl(11);
p.tcph->th_flags = TH_ACK;
p.flowflags = FLOW_PKT_TOSERVER;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
p.tcph->th_seq = htonl(19);
p.tcph->th_ack = htonl(11);
p.tcph->th_flags = TH_ACK|TH_PUSH;
p.flowflags = FLOW_PKT_TOSERVER;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
if (stream_config.midstream != TRUE) {
ret = 1;
goto end;
}
if (((TcpSession *)(p.flow->protoctx))->state != TCP_ESTABLISHED)
goto end;
if (((TcpSession *)(p.flow->protoctx))->client.next_seq != 20 ||
((TcpSession *)(p.flow->protoctx))->server.next_seq != 11)
goto end;
StreamTcpSessionPktFree(&p);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
return ret;
}
/**
* \test Test the setting up a TCP session when we missed the intial
* SYN/ACK packet of the session. The session is setup only if
* midstream sessions are allowed to setup.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest04 (void) {
Packet p;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
memset (&p, 0, sizeof(Packet));
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof (StreamTcpThread));
memset(&tcph, 0, sizeof (TCPHdr));
p.flow = &f;
StreamTcpInitConfig(TRUE);
tcph.th_win = htons(5480);
tcph.th_seq = htonl(10);
tcph.th_ack = htonl(20);
tcph.th_flags = TH_ACK;
p.tcph = &tcph;
int ret = 0;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
p.tcph->th_seq = htonl(9);
p.tcph->th_ack = htonl(19);
p.tcph->th_flags = TH_ACK|TH_PUSH;
p.flowflags = FLOW_PKT_TOSERVER;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
if (stream_config.midstream != TRUE) {
ret = 1;
goto end;
}
if (((TcpSession *)(p.flow->protoctx))->state != TCP_ESTABLISHED)
goto end;
if (((TcpSession *)(p.flow->protoctx))->client.next_seq != 10 ||
((TcpSession *)(p.flow->protoctx))->server.next_seq != 20)
goto end;
StreamTcpSessionPktFree(&p);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
return ret;
}
/**
* \test Test the setting up a TCP session when we missed the intial
* 3WHS packet of the session. The session is setup only if
* midstream sessions are allowed to setup.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest05 (void) {
Packet p;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
u_int8_t payload[4];
memset (&p, 0, sizeof(Packet));
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof (StreamTcpThread));
memset(&tcph, 0, sizeof (TCPHdr));
p.flow = &f;
int ret = 0;
StreamTcpInitConfig(TRUE);
/* prevent L7 from kicking in */
StreamMsgQueueSetMinInitChunkLen(FLOW_PKT_TOSERVER, 4096);
StreamMsgQueueSetMinInitChunkLen(FLOW_PKT_TOCLIENT, 4096);
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
tcph.th_win = htons(5480);
tcph.th_seq = htonl(10);
tcph.th_ack = htonl(20);
tcph.th_flags = TH_ACK|TH_PUSH;
p.tcph = &tcph;
StreamTcpCreateTestPacket(payload, 0x41, 3); /*AAA*/
p.payload = payload;
p.payload_len = 3;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
p.tcph->th_seq = htonl(20);
p.tcph->th_ack = htonl(13);
p.tcph->th_flags = TH_ACK|TH_PUSH;
p.flowflags = FLOW_PKT_TOCLIENT;
StreamTcpCreateTestPacket(payload, 0x42, 3); /*BBB*/
p.payload = payload;
p.payload_len = 3;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
p.tcph->th_seq = htonl(13);
p.tcph->th_ack = htonl(23);
p.tcph->th_flags = TH_ACK|TH_PUSH;
p.flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x43, 3); /*CCC*/
p.payload = payload;
p.payload_len = 3;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
p.tcph->th_seq = htonl(19);
p.tcph->th_ack = htonl(16);
p.tcph->th_flags = TH_ACK|TH_PUSH;
p.flowflags = FLOW_PKT_TOCLIENT;
StreamTcpCreateTestPacket(payload, 0x44, 3); /*DDD*/
p.payload = payload;
p.payload_len = 3;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
if (stream_config.midstream != TRUE) {
ret = 1;
goto end;
}
if (((TcpSession *)(p.flow->protoctx))->state != TCP_ESTABLISHED)
goto end;
if (((TcpSession *)(p.flow->protoctx))->client.next_seq != 16 ||
((TcpSession *)(p.flow->protoctx))->server.next_seq != 23)
goto end;
StreamTcpSessionPktFree(&p);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
return ret;
}
/**
* \test Test the setting up a TCP session when we have seen only the
* FIN, RST packets packet of the session. The session is setup only if
* midstream sessions are allowed to setup.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest06 (void) {
Packet p;
Flow f;
TcpSession ssn;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
memset (&p, 0, sizeof(Packet));
memset (&f, 0, sizeof(Flow));
memset(&ssn, 0, sizeof (TcpSession));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof (StreamTcpThread));
memset(&tcph, 0, sizeof (TCPHdr));
p.flow = &f;
int ret = 0;
StreamTcpInitConfig(TRUE);
tcph.th_flags = TH_FIN;
p.tcph = &tcph;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
if (((TcpSession *)(p.flow->protoctx)) != NULL)
goto end;
p.tcph->th_flags = TH_RST;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
if (((TcpSession *)(p.flow->protoctx)) != NULL)
goto end;
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
return ret;
}
/**
* \test Test the working on PAWS. The packet will be dropped by stream, as
* its timestamp is old, although the segment is in the window.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest07 (void) {
Packet p;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
u_int8_t payload[1] = {0x42};
TCPVars tcpvars;
TCPOpt ts;
uint32_t data[2];
memset (&p, 0, sizeof(Packet));
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof(StreamTcpThread));
memset(&tcph, 0, sizeof(TCPHdr));
memset(&tcpvars, 0, sizeof(TCPVars));
memset(&ts, 0, sizeof(TCPOpt));
p.flow = &f;
int ret = 0;
StreamTcpInitConfig(TRUE);
/* prevent L7 from kicking in */
StreamMsgQueueSetMinInitChunkLen(FLOW_PKT_TOSERVER, 4096);
StreamMsgQueueSetMinInitChunkLen(FLOW_PKT_TOCLIENT, 4096);
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
tcph.th_win = htons(5480);
tcph.th_seq = htonl(10);
tcph.th_ack = htonl(20);
tcph.th_flags = TH_ACK|TH_PUSH;
p.tcph = &tcph;
data[0] = htonl(10);
data[1] = htonl(11);
ts.type = TCP_OPT_TS;
ts.len = 10;
ts.data = (uint8_t *)data;
tcpvars.ts = &ts;
p.tcpvars = tcpvars;
p.payload = payload;
p.payload_len = 1;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
p.tcph->th_seq = htonl(11);
p.tcph->th_ack = htonl(23);
p.tcph->th_flags = TH_ACK|TH_PUSH;
p.flowflags = FLOW_PKT_TOSERVER;
data[0] = htonl(2);
p.tcpc.ts1 = 0;
p.tcpc.ts2 = 0;
p.tcpvars.ts->data = (uint8_t *)data;
if (StreamTcpPacket(&tv, &p, &stt) == -1) {
if (((TcpSession *) (p.flow->protoctx))->client.next_seq != 11) {
printf("the timestamp values are client %"PRIu32" server %" PRIu32 " seq %" PRIu32 "\n", TCP_GET_TSVAL(&p), TCP_GET_TSECR(&p), ((TcpSession *) (p.flow->protoctx))->client.next_seq);
goto end;
}
StreamTcpSessionPktFree(&p);
ret = 1;
}
end:
StreamTcpFreeConfig(TRUE);
return ret;
}
/**
* \test Test the working on PAWS. The packet will be accpeted by engine as
* the timestamp is valid and it is in window.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest08 (void) {
Packet p;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
u_int8_t payload[1] = {0x42};
TCPVars tcpvars;
TCPOpt ts;
uint32_t data[2];
memset (&p, 0, sizeof(Packet));
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof(StreamTcpThread));
memset(&tcph, 0, sizeof(TCPHdr));
memset(&tcpvars, 0, sizeof(TCPVars));
memset(&ts, 0, sizeof(TCPOpt));
p.flow = &f;
int ret = 0;
StreamTcpInitConfig(TRUE);
/* prevent L7 from kicking in */
StreamMsgQueueSetMinInitChunkLen(FLOW_PKT_TOSERVER, 4096);
StreamMsgQueueSetMinInitChunkLen(FLOW_PKT_TOCLIENT, 4096);
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
tcph.th_win = htons(5480);
tcph.th_seq = htonl(10);
tcph.th_ack = htonl(20);
tcph.th_flags = TH_ACK|TH_PUSH;
p.tcph = &tcph;
data[0] = htonl(10);
data[1] = htonl(11);
ts.type = TCP_OPT_TS;
ts.len = 10;
ts.data = (uint8_t *)data;
tcpvars.ts = &ts;
p.tcpvars = tcpvars;
p.payload = payload;
p.payload_len = 1;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
p.tcph->th_seq = htonl(11);
p.tcph->th_ack = htonl(23);
p.tcph->th_flags = TH_ACK|TH_PUSH;
p.flowflags = FLOW_PKT_TOSERVER;
data[0] = htonl(12);
p.tcpc.ts1 = 0;
p.tcpc.ts2 = 0;
p.tcpvars.ts->data = (uint8_t *)data;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
if (((TcpSession *) (p.flow->protoctx))->client.next_seq != 12) {
printf("the timestamp values are client %"PRIu32" server %" PRIu32 " seq %" PRIu32 "\n", TCP_GET_TSVAL(&p), TCP_GET_TSECR(&p), ((TcpSession *) (p.flow->protoctx))->client.next_seq);
goto end;
}
StreamTcpSessionPktFree(&p);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
return ret;
}
/*static int StreamTcpTest09 (void) {
Packet p;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
u_int8_t payload[1] = {0x42};
memset (&p, 0, sizeof(Packet));
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof(StreamTcpThread));
memset(&tcph, 0, sizeof(TCPHdr));
p.flow = &f;
int ret = 0;
StreamTcpInitConfig(TRUE);
//prevent L7 from kicking in
StreamMsgQueueSetMinInitChunkLen(FLOW_PKT_TOSERVER, 4096);
StreamMsgQueueSetMinInitChunkLen(FLOW_PKT_TOCLIENT, 4096);
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
tcph.th_win = htons(5480);
tcph.th_seq = htonl(10);
tcph.th_ack = htonl(20);
tcph.th_flags = TH_ACK|TH_PUSH;
p.tcph = &tcph;
p.payload = payload;
p.payload_len = 1;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
p.tcph->th_seq = htonl(12);
p.tcph->th_ack = htonl(23);
p.tcph->th_flags = TH_ACK|TH_PUSH;
p.flowflags = FLOW_PKT_TOSERVER;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
p.tcph->th_seq = htonl(11);
p.tcph->th_ack = htonl(23);
p.tcph->th_flags = TH_ACK|TH_PUSH;
p.flowflags = FLOW_PKT_TOSERVER;
if (StreamTcpPacket(&tv, &p, &stt) == -1)
goto end;
if (((TcpSession *) (p.flow->protoctx))->client.seg_list == NULL)
ret = 1;
StreamTcpSessionPktFree(&p);
end:
StreamTcpFreeConfig(TRUE);
return ret;
}*/
#endif /* UNITTESTS */
void StreamTcpRegisterTests (void) {
#ifdef UNITTESTS
UtRegisterTest("StreamTcpTest01 -- TCP session allocation", StreamTcpTest01, 1);
UtRegisterTest("StreamTcpTest02 -- TCP session deallocation", StreamTcpTest02, 1);
UtRegisterTest("StreamTcpTest03 -- SYN missed MidStream session", StreamTcpTest03, 1);
UtRegisterTest("StreamTcpTest04 -- SYN/ACK missed MidStream session", StreamTcpTest04, 1);
UtRegisterTest("StreamTcpTest05 -- 3WHS missed MidStream session", StreamTcpTest05, 1);
UtRegisterTest("StreamTcpTest06 -- FIN, RST message MidStream session", StreamTcpTest06, 1);
UtRegisterTest("StreamTcpTest07 -- PAWS invalid timestamp", StreamTcpTest07, 1);
UtRegisterTest("StreamTcpTest08 -- PAWS valid timestamp", StreamTcpTest08, 1);
//UtRegisterTest("StreamTcpTest09 -- No Client Reassembly", StreamTcpTest09, 1);
/* set up the reassembly tests as well */
StreamTcpReassembleRegisterTests();
#endif /* UNITTESTS */
}