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

9060 lines
307 KiB
C

/* Copyright (C) 2007-2011 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
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
*
* \author Victor Julien <victor@inliniac.net>
* \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
*
* TCP stream tracking and reassembly engine.
*
* \todo - 4WHS: what if after the 2nd SYN we turn out to be normal 3WHS anyway?
*/
#include "suricata-common.h"
#include "suricata.h"
#include "decode.h"
#include "debug.h"
#include "detect.h"
#include "flow.h"
#include "flow-util.h"
#include "conf.h"
#include "conf-yaml-loader.h"
#include "threads.h"
#include "threadvars.h"
#include "tm-threads.h"
#include "util-pool.h"
#include "util-checksum.h"
#include "util-unittest.h"
#include "util-print.h"
#include "util-debug.h"
#include "util-device.h"
#include "stream-tcp-private.h"
#include "stream-tcp-reassemble.h"
#include "stream-tcp.h"
#include "stream-tcp-inline.h"
#include "stream-tcp-sack.h"
#include "stream-tcp-util.h"
#include "stream.h"
#include "pkt-var.h"
#include "app-layer-parser.h"
#include "app-layer-protos.h"
#include "util-host-os-info.h"
#include "util-privs.h"
#include "util-profiling.h"
#include "util-misc.h"
//#define DEBUG
#define STREAMTCP_DEFAULT_SESSIONS 262144
#define STREAMTCP_DEFAULT_PREALLOC 32768
#define STREAMTCP_DEFAULT_MEMCAP (32 * 1024 * 1024) /* 32mb */
#define STREAMTCP_DEFAULT_REASSEMBLY_MEMCAP (64 * 1024 * 1024) /* 64mb */
#define STREAMTCP_DEFAULT_TOSERVER_CHUNK_SIZE 2560
#define STREAMTCP_DEFAULT_TOCLIENT_CHUNK_SIZE 2560
#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
TmEcode StreamTcp (ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
TmEcode StreamTcpThreadInit(ThreadVars *, void *, void **);
TmEcode StreamTcpThreadDeinit(ThreadVars *, void *);
void StreamTcpExitPrintStats(ThreadVars *, void *);
static int StreamTcpHandleFin(ThreadVars *tv, StreamTcpThread *, TcpSession *, Packet *, PacketQueue *);
void StreamTcpRegisterTests (void);
void StreamTcpReturnStreamSegments (TcpStream *);
void StreamTcpInitConfig(char);
int StreamTcpGetFlowState(void *);
void StreamTcpSetOSPolicy(TcpStream*, Packet*);
void StreamTcpPseudoPacketCreateStreamEndPacket(Packet *, TcpSession *, PacketQueue *);
static int StreamTcpValidateTimestamp(TcpSession * , Packet *);
static int StreamTcpHandleTimestamp(TcpSession * , Packet *);
static int StreamTcpValidateRst(TcpSession * , Packet *);
static inline int StreamTcpValidateAck(TcpStream *, Packet *);
static Pool *ssn_pool = NULL;
static SCMutex ssn_pool_mutex;
#ifdef DEBUG
static uint64_t ssn_pool_cnt = 0; /** counts ssns, protected by ssn_pool_mutex */
#endif
extern uint8_t engine_mode;
static SCSpinlock stream_memuse_spinlock;
static uint64_t stream_memuse = 0;
static uint64_t stream_memuse_max = 0;
/* stream engine running in "inline" mode. */
int stream_inline = 0;
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;
tmm_modules[TMM_STREAMTCP].cap_flags = 0;
}
void StreamTcpIncrMemuse(uint64_t size) {
SCSpinLock(&stream_memuse_spinlock);
stream_memuse += (uint64_t)size;
if (stream_memuse > stream_memuse_max)
stream_memuse_max = stream_memuse;
SCSpinUnlock(&stream_memuse_spinlock);
}
void StreamTcpDecrMemuse(uint64_t size) {
SCSpinLock(&stream_memuse_spinlock);
if ((uint64_t)size <= stream_memuse)
stream_memuse -= (uint64_t)size;
else
stream_memuse = 0;
SCSpinUnlock(&stream_memuse_spinlock);
}
void StreamTcpMemuseCounter(ThreadVars *tv, StreamTcpThread *stt) {
SCSpinLock(&stream_memuse_spinlock);
SCPerfCounterSetUI64(stt->counter_tcp_memuse, tv->sc_perf_pca, stream_memuse);
SCSpinUnlock(&stream_memuse_spinlock);
}
/**
* \brief Check if alloc'ing "size" would mean we're over memcap
*
* \retval 1 if in bounds
* \retval 0 if not in bounds
*/
int StreamTcpCheckMemcap(uint64_t size) {
SCEnter();
int ret = 0;
SCSpinLock(&stream_memuse_spinlock);
if (stream_config.memcap == 0 || (size + stream_memuse) <= stream_config.memcap)
ret = 1;
SCSpinUnlock(&stream_memuse_spinlock);
SCReturnInt(ret);
}
/**
* \brief Function to return the stream back to the pool. It returns the
* segments in the stream to the segment pool.
*
* This function is called when the flow is destroyed, so it should free
* *everything* related to the tcp session. So including the app layer
* data. We are guaranteed to only get here when the flow's use_cnt is 0.
*
* \param ssn Void ptr to the ssn.
*/
void StreamTcpSessionClear(void *ssnptr)
{
SCEnter();
StreamMsg *smsg = NULL;
TcpSession *ssn = (TcpSession *)ssnptr;
if (ssn == NULL)
SCReturn;
StreamTcpReturnStreamSegments(&ssn->client);
StreamTcpReturnStreamSegments(&ssn->server);
//AppLayerParserCleanupState(ssn);
StreamTcpSackFreeList(&ssn->client);
StreamTcpSackFreeList(&ssn->server);
/* if we have (a) smsg(s), return to the pool */
smsg = ssn->toserver_smsg_head;
while(smsg != NULL) {
StreamMsg *smsg_next = smsg->next;
SCLogDebug("returning smsg %p to pool", smsg);
smsg->next = NULL;
smsg->prev = NULL;
smsg->flow = NULL;
StreamMsgReturnToPool(smsg);
smsg = smsg_next;
}
ssn->toserver_smsg_head = NULL;
smsg = ssn->toclient_smsg_head;
while(smsg != NULL) {
StreamMsg *smsg_next = smsg->next;
SCLogDebug("returning smsg %p to pool", smsg);
smsg->next = NULL;
smsg->prev = NULL;
smsg->flow = NULL;
StreamMsgReturnToPool(smsg);
smsg = smsg_next;
}
ssn->toclient_smsg_head = NULL;
memset(ssn, 0, sizeof(TcpSession));
SCMutexLock(&ssn_pool_mutex);
PoolReturn(ssn_pool, ssn);
#ifdef DEBUG
ssn_pool_cnt--;
#endif
SCMutexUnlock(&ssn_pool_mutex);
SCReturn;
}
/**
* \brief Function to return the stream segments back to the pool.
*
* We don't clear out the app layer storage here as that is under protection
* of the "use_cnt" reference counter in the flow. This function is called
* when the use_cnt is always at least 1 (this pkt has incremented the flow
* use_cnt itself), so we don't bother.
*
* \param p Packet used to identify the stream.
*/
void StreamTcpSessionPktFree (Packet *p)
{
SCEnter();
TcpSession *ssn = (TcpSession *)p->flow->protoctx;
if (ssn == NULL)
SCReturn;
StreamTcpReturnStreamSegments(&ssn->client);
StreamTcpReturnStreamSegments(&ssn->server);
SCReturn;
}
/** \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)
{
if (StreamTcpCheckMemcap((uint32_t)sizeof(TcpSession)) == 0)
return NULL;
void *ptr = SCMalloc(sizeof(TcpSession));
if (ptr == NULL)
return NULL;
memset(ptr, 0, sizeof(TcpSession));
StreamTcpIncrMemuse((uint64_t)sizeof(TcpSession));
return ptr;
}
/** \brief Pool free function
* \param s Void ptr to TcpSession memory */
void StreamTcpSessionPoolFree(void *s)
{
StreamMsg *smsg = NULL;
if (s == NULL)
return;
TcpSession *ssn = (TcpSession *)s;
StreamTcpReturnStreamSegments(&ssn->client);
StreamTcpReturnStreamSegments(&ssn->server);
/* if we have (a) smsg(s), return to the pool */
smsg = ssn->toserver_smsg_head;
while(smsg != NULL) {
StreamMsg *smsg_next = smsg->next;
SCLogDebug("returning smsg %p to pool", smsg);
smsg->next = NULL;
smsg->prev = NULL;
smsg->flow = NULL;
StreamMsgReturnToPool(smsg);
smsg = smsg_next;
}
ssn->toserver_smsg_head = NULL;
smsg = ssn->toclient_smsg_head;
while(smsg != NULL) {
StreamMsg *smsg_next = smsg->next;
SCLogDebug("returning smsg %p to pool", smsg);
smsg->next = NULL;
smsg->prev = NULL;
smsg->flow = NULL;
StreamMsgReturnToPool(smsg);
smsg = smsg_next;
}
ssn->toclient_smsg_head = NULL;
SCFree(ssn);
StreamTcpDecrMemuse((uint64_t)sizeof(TcpSession));
}
/** \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)
{
intmax_t value = 0;
SCLogDebug("Initializing Stream");
memset(&stream_config, 0, sizeof(stream_config));
/** set config defaults */
if ((ConfGetInt("stream.max_sessions", &value)) == 1) {
stream_config.max_sessions = (uint32_t)value;
} else {
if (RunmodeIsUnittests())
stream_config.max_sessions = 1024;
else
stream_config.max_sessions = STREAMTCP_DEFAULT_SESSIONS;
}
if (!quiet) {
SCLogInfo("stream \"max_sessions\": %"PRIu32"", stream_config.max_sessions);
}
if ((ConfGetInt("stream.prealloc_sessions", &value)) == 1) {
stream_config.prealloc_sessions = (uint32_t)value;
} else {
if (RunmodeIsUnittests())
stream_config.prealloc_sessions = 128;
else
stream_config.prealloc_sessions = STREAMTCP_DEFAULT_PREALLOC;
}
if (!quiet) {
SCLogInfo("stream \"prealloc_sessions\": %"PRIu32"", stream_config.prealloc_sessions);
}
char *temp_stream_memcap_str;
if (ConfGet("stream.memcap", &temp_stream_memcap_str) == 1) {
if (ParseSizeStringU64(temp_stream_memcap_str, &stream_config.memcap) < 0) {
SCLogError(SC_ERR_SIZE_PARSE, "Error parsing stream.memcap "
"from conf file - %s. Killing engine",
temp_stream_memcap_str);
exit(EXIT_FAILURE);
}
} else {
stream_config.memcap = STREAMTCP_DEFAULT_MEMCAP;
}
if (!quiet) {
SCLogInfo("stream \"memcap\": %"PRIu64, stream_config.memcap);
}
ConfGetBool("stream.midstream", &stream_config.midstream);
if (!quiet) {
SCLogInfo("stream \"midstream\" session pickups: %s", stream_config.midstream ? "enabled" : "disabled");
}
ConfGetBool("stream.async_oneside", &stream_config.async_oneside);
if (!quiet) {
SCLogInfo("stream \"async_oneside\": %s", stream_config.async_oneside ? "enabled" : "disabled");
}
int csum = 0;
if ((ConfGetBool("stream.checksum_validation", &csum)) == 1) {
if (csum == 1) {
stream_config.flags |= STREAMTCP_INIT_FLAG_CHECKSUM_VALIDATION;
}
/* Default is that we validate the checksum of all the packets */
} else {
stream_config.flags |= STREAMTCP_INIT_FLAG_CHECKSUM_VALIDATION;
}
if (!quiet) {
SCLogInfo("stream \"checksum_validation\": %s",
stream_config.flags & STREAMTCP_INIT_FLAG_CHECKSUM_VALIDATION ?
"enabled" : "disabled");
}
int inl = 0;
if (ConfGetBool("stream.inline", &inl) == 1) {
stream_inline = inl;
}
if (!quiet) {
SCLogInfo("stream.\"inline\": %s", stream_inline ? "enabled" : "disabled");
}
char *temp_stream_reassembly_memcap_str;
if (ConfGet("stream.reassembly.memcap", &temp_stream_reassembly_memcap_str) == 1) {
if (ParseSizeStringU64(temp_stream_reassembly_memcap_str,
&stream_config.reassembly_memcap) < 0) {
SCLogError(SC_ERR_SIZE_PARSE, "Error parsing "
"stream.reassembly.memcap "
"from conf file - %s. Killing engine",
temp_stream_reassembly_memcap_str);
exit(EXIT_FAILURE);
}
} else {
stream_config.reassembly_memcap = STREAMTCP_DEFAULT_REASSEMBLY_MEMCAP;
}
if (!quiet) {
SCLogInfo("stream.reassembly \"memcap\": %"PRIu64"", stream_config.reassembly_memcap);
}
char *temp_stream_reassembly_depth_str;
if (ConfGet("stream.reassembly.depth", &temp_stream_reassembly_depth_str) == 1) {
if (ParseSizeStringU32(temp_stream_reassembly_depth_str,
&stream_config.reassembly_depth) < 0) {
SCLogError(SC_ERR_SIZE_PARSE, "Error parsing "
"stream.reassembly.depth "
"from conf file - %s. Killing engine",
temp_stream_reassembly_depth_str);
exit(EXIT_FAILURE);
}
} else {
stream_config.reassembly_depth = 0;
}
if (!quiet) {
SCLogInfo("stream.reassembly \"depth\": %"PRIu32"", stream_config.reassembly_depth);
}
char *temp_stream_reassembly_toserver_chunk_size_str;
if (ConfGet("stream.reassembly.toserver_chunk_size",
&temp_stream_reassembly_toserver_chunk_size_str) == 1) {
if (ParseSizeStringU16(temp_stream_reassembly_toserver_chunk_size_str,
&stream_config.reassembly_toserver_chunk_size) < 0) {
SCLogError(SC_ERR_SIZE_PARSE, "Error parsing "
"stream.reassembly.toserver_chunk_size "
"from conf file - %s. Killing engine",
temp_stream_reassembly_toserver_chunk_size_str);
exit(EXIT_FAILURE);
}
} else {
stream_config.reassembly_toserver_chunk_size =
STREAMTCP_DEFAULT_TOSERVER_CHUNK_SIZE;
}
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER,
stream_config.reassembly_toserver_chunk_size);
char *temp_stream_reassembly_toclient_chunk_size_str;
if (ConfGet("stream.reassembly.toclient_chunk_size",
&temp_stream_reassembly_toclient_chunk_size_str) == 1) {
if (ParseSizeStringU16(temp_stream_reassembly_toclient_chunk_size_str,
&stream_config.reassembly_toclient_chunk_size) < 0) {
SCLogError(SC_ERR_SIZE_PARSE, "Error parsing "
"stream.reassembly.toclient_chunk_size "
"from conf file - %s. Killing engine",
temp_stream_reassembly_toclient_chunk_size_str);
exit(EXIT_FAILURE);
}
} else {
stream_config.reassembly_toclient_chunk_size =
STREAMTCP_DEFAULT_TOCLIENT_CHUNK_SIZE;
}
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT,
stream_config.reassembly_toclient_chunk_size);
if (!quiet) {
SCLogInfo("stream.reassembly \"toserver_chunk_size\": %"PRIu16,
stream_config.reassembly_toserver_chunk_size);
SCLogInfo("stream.reassembly \"toclient_chunk_size\": %"PRIu16,
stream_config.reassembly_toclient_chunk_size);
}
/* init the memcap and it's lock */
SCSpinInit(&stream_memuse_spinlock, PTHREAD_PROCESS_PRIVATE);
SCSpinLock(&stream_memuse_spinlock);
stream_memuse = 0;
stream_memuse_max = 0;
SCSpinUnlock(&stream_memuse_spinlock);
ssn_pool = PoolInit(stream_config.max_sessions,
stream_config.prealloc_sessions,
StreamTcpSessionPoolAlloc, NULL,
StreamTcpSessionPoolFree);
if (ssn_pool == NULL) {
SCLogError(SC_ERR_POOL_INIT, "ssn_pool is not initialized");
exit(EXIT_FAILURE);
}
SCMutexInit(&ssn_pool_mutex, NULL);
StreamTcpReassembleInit(quiet);
/* set the default free function and flow state function
* values. */
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 {
SCLogError(SC_ERR_POOL_EMPTY, "ssn_pool is NULL");
exit(EXIT_FAILURE);
}
SCLogDebug("ssn_pool_cnt %"PRIu64"", ssn_pool_cnt);
if (!quiet) {
SCSpinLock(&stream_memuse_spinlock);
SCLogInfo("Max memuse of stream engine %"PRIu64" (in use %"PRIu64")",
stream_memuse_max, stream_memuse);
SCSpinUnlock(&stream_memuse_spinlock);
}
SCMutexDestroy(&ssn_pool_mutex);
SCSpinDestroy(&stream_memuse_spinlock);
}
/** \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) {
SCMutexLock(&ssn_pool_mutex);
p->flow->protoctx = PoolGet(ssn_pool);
#ifdef DEBUG
if (p->flow->protoctx != NULL)
ssn_pool_cnt++;
#endif
SCMutexUnlock(&ssn_pool_mutex);
ssn = (TcpSession *)p->flow->protoctx;
if (ssn == NULL) {
SCLogDebug("ssn_pool is empty");
return NULL;
}
ssn->state = TCP_NONE;
}
return ssn;
}
static void StreamTcpPacketSetState(Packet *p, TcpSession *ssn,
uint8_t state)
{
if (state == ssn->state || PKT_IS_PSEUDOPKT(p))
return;
ssn->state = state;
FlowUpdateQueue(p->flow);
}
/**
* \brief Function to set the OS policy for the given stream based on the
* destination of the received packet.
*
* \param stream TcpStream of which os_policy needs to set
* \param p Packet which is used to set the os policy
*/
void StreamTcpSetOSPolicy(TcpStream *stream, Packet *p)
{
int ret = 0;
if (PKT_IS_IPV4(p)) {
/* Get the OS policy based on destination IP address, as destination
OS will decide how to react on the anomalies of newly received
packets */
ret = SCHInfoGetIPv4HostOSFlavour((uint8_t *)GET_IPV4_DST_ADDR_PTR(p));
if (ret > 0)
stream->os_policy = ret;
else
stream->os_policy = OS_POLICY_DEFAULT;
} else if (PKT_IS_IPV6(p)) {
/* Get the OS policy based on destination IP address, as destination
OS will decide how to react on the anomalies of newly received
packets */
ret = SCHInfoGetIPv6HostOSFlavour((uint8_t *)GET_IPV6_DST_ADDR(p));
if (ret > 0)
stream->os_policy = ret;
else
stream->os_policy = OS_POLICY_DEFAULT;
}
if (stream->os_policy == OS_POLICY_BSD_RIGHT)
stream->os_policy = OS_POLICY_BSD;
else if (stream->os_policy == OS_POLICY_OLD_SOLARIS)
stream->os_policy = OS_POLICY_SOLARIS;
SCLogDebug("Policy is %"PRIu8"", stream->os_policy);
}
/**
* \brief macro to update last_ack only if the new value is higher
*
* \param ssn session
* \param stream stream to update
* \param ack ACK value to test and set
*/
#define StreamTcpUpdateLastAck(ssn, stream, ack) { \
if (SEQ_GT((ack), (stream)->last_ack)) { \
(stream)->last_ack = (ack); \
SCLogDebug("ssn %p: last_ack set to %"PRIu32, (ssn), (stream)->last_ack); \
StreamTcpSackPruneList((stream)); \
} \
}
/**
* \brief macro to update next_win only if the new value is higher
*
* \param ssn session
* \param stream stream to update
* \param win window value to test and set
*/
#define StreamTcpUpdateNextWin(ssn, stream, win) { \
uint32_t sacked_size__ = StreamTcpSackedSize((stream)); \
if (SEQ_GT(((win) + sacked_size__), (stream)->next_win)) { \
(stream)->next_win = ((win) + sacked_size__); \
SCLogDebug("ssn %p: next_win set to %"PRIu32, (ssn), (stream)->next_win); \
} \
}
/**
* \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
* \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, PacketQueue *pq)
{
switch (p->tcph->th_flags) {
/* The following cases will allow us to create the tcp sessions in congestion
* conditions, where the client open a connection with SYN and
* any of the following flags:
* TH_CWR -> Establish a new connection reducing window
* TH_ECN -> Echo Congestion flag
*/
case TH_SYN:
case TH_SYN | TH_ECN:
case TH_SYN | TH_CWR:
case TH_SYN | TH_PUSH:
case TH_SYN | TH_URG:
case TH_SYN | TH_CWR | TH_ECN:
case TH_SYN | TH_PUSH | TH_URG:
{
if (ssn == NULL) {
ssn = StreamTcpNewSession(p);
if (ssn == NULL) {
SCPerfCounterIncr(stt->counter_tcp_ssn_memcap, tv->sc_perf_pca);
return -1;
}
SCPerfCounterIncr(stt->counter_tcp_sessions, tv->sc_perf_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);
STREAMTCP_SET_RA_BASE_SEQ(&ssn->client, 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);
}
if (TCP_GET_SACKOK(p) == 1) {
ssn->flags |= STREAMTCP_FLAG_CLIENT_SACKOK;
SCLogDebug("ssn %p: SACK permited on SYN packet", ssn);
}
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:
case TH_SYN|TH_ACK|TH_ECN:
case TH_SYN|TH_ACK|TH_ECN|TH_CWR:
if (stream_config.midstream == FALSE &&
stream_config.async_oneside == FALSE)
break;
if (ssn == NULL) {
ssn = StreamTcpNewSession(p);
if (ssn == NULL) {
SCPerfCounterIncr(stt->counter_tcp_ssn_memcap, tv->sc_perf_pca);
return -1;
}
SCPerfCounterIncr(stt->counter_tcp_sessions, tv->sc_perf_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;
/* Flag used to change the direct in the later stage in the session */
ssn->flags |= STREAMTCP_FLAG_MIDSTREAM_SYNACK;
/* sequence number & window */
ssn->server.isn = TCP_GET_SEQ(p);
STREAMTCP_SET_RA_BASE_SEQ(&ssn->server, 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;
STREAMTCP_SET_RA_BASE_SEQ(&ssn->client, 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->server.last_ts = TCP_GET_TSVAL(p);
ssn->client.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;
}
if (TCP_GET_SACKOK(p) == 1) {
ssn->flags |= STREAMTCP_FLAG_SACKOK;
SCLogDebug("ssn %p: SYN/ACK with SACK permitted, assuming "
"SACK permitted for both sides", ssn);
}
break;
/* Handle SYN/ACK and 3WHS shake missed together as it is almost
* similar. */
case TH_ACK:
case TH_ACK| TH_URG:
case TH_ACK|TH_ECN:
case TH_ACK|TH_ECN|TH_CWR:
case TH_ACK|TH_PUSH:
case TH_ACK|TH_PUSH|TH_URG:
case TH_ACK|TH_PUSH|TH_ECN:
case TH_ACK|TH_PUSH|TH_ECN|TH_CWR:
if (stream_config.midstream == FALSE)
break;
if (ssn == NULL) {
ssn = StreamTcpNewSession(p);
if (ssn == NULL) {
SCPerfCounterIncr(stt->counter_tcp_ssn_memcap, tv->sc_perf_pca);
return -1;
}
SCPerfCounterIncr(stt->counter_tcp_sessions, tv->sc_perf_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;
STREAMTCP_SET_RA_BASE_SEQ(&ssn->client, 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;
STREAMTCP_SET_RA_BASE_SEQ(&ssn->server, 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;
}
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, &ssn->client, p, pq);
ssn->flags |= STREAMTCP_FLAG_SACKOK;
SCLogDebug("ssn %p: assuming SACK permitted for both sides", ssn);
break;
case TH_RST:
case TH_RST|TH_ACK:
case TH_RST|TH_ACK|TH_ECN:
case TH_RST|TH_ACK|TH_ECN|TH_CWR:
case TH_RST|TH_ACK|TH_PUSH:
case TH_RST|TH_ACK|TH_PUSH|TH_ECN:
case TH_RST|TH_ACK|TH_PUSH|TH_ECN|TH_CWR:
StreamTcpSetEvent(p, STREAM_RST_BUT_NO_SESSION);
SCLogDebug("RST packet received, no session setup");
break;
case TH_FIN:
case TH_FIN|TH_ACK:
case TH_FIN|TH_ACK|TH_ECN:
case TH_FIN|TH_ACK|TH_ECN|TH_CWR:
case TH_FIN|TH_ACK|TH_PUSH:
case TH_FIN|TH_ACK|TH_PUSH|TH_ECN:
case TH_FIN|TH_ACK|TH_PUSH|TH_ECN|TH_CWR:
StreamTcpSetEvent(p, STREAM_FIN_BUT_NO_SESSION);
SCLogDebug("FIN 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
* \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, PacketQueue *pq)
{
if (ssn == NULL)
return -1;
SCLogDebug("ssn %p: pkt received: %s", ssn, PKT_IS_TOCLIENT(p) ?
"toclient":"toserver");
switch (p->tcph->th_flags) {
case TH_SYN:
case TH_SYN|TH_URG:
case TH_SYN|TH_CWR:
case TH_SYN|TH_CWR|TH_ECN:
SCLogDebug("ssn %p: SYN packet on state SYN_SENT... resent", ssn);
if (ssn->flags & STREAMTCP_FLAG_4WHS) {
SCLogDebug("ssn %p: SYN packet on state SYN_SENT... resent of "
"4WHS SYN", ssn);
}
if (PKT_IS_TOCLIENT(p)) {
/** a SYN only packet in the opposite direction could be:
* http://www.breakingpointsystems.com/community/blog/tcp-
* portals-the-three-way-handshake-is-a-lie
*
* \todo improve resetting the session */
/* indicate that we're dealing with 4WHS here */
ssn->flags |= STREAMTCP_FLAG_4WHS;
SCLogDebug("ssn %p: STREAMTCP_FLAG_4WHS flag set", ssn);
/* set the sequence numbers and window for server
* We leave the ssn->client.isn in place as we will
* check the SYN/ACK pkt with that.
*/
ssn->server.isn = TCP_GET_SEQ(p);
STREAMTCP_SET_RA_BASE_SEQ(&ssn->server, ssn->server.isn);
ssn->server.next_seq = ssn->server.isn + 1;
/* Set the stream timestamp value, if packet has timestamp
* option enabled. */
if (p->tcpvars.ts != NULL) {
ssn->server.last_ts = TCP_GET_TSVAL(p);
SCLogDebug("ssn %p: p->tcpvars.ts %p, %02x", ssn,
p->tcpvars.ts, ssn->server.last_ts);
if (ssn->server.last_ts == 0)
ssn->server.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP;
ssn->server.last_pkt_ts = p->ts.tv_sec;
ssn->server.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);
} else {
ssn->flags &= ~STREAMTCP_FLAG_SERVER_WSCALE;
ssn->server.wscale = 0;
}
if (TCP_GET_SACKOK(p) == 1) {
ssn->flags |= STREAMTCP_FLAG_CLIENT_SACKOK;
} else {
ssn->flags &= ~STREAMTCP_FLAG_CLIENT_SACKOK;
}
SCLogDebug("ssn %p: 4WHS 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);
SCLogDebug("ssn %p: 4WHS 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);
}
/** \todo check if it's correct or set event */
break;
case TH_SYN|TH_ACK:
case TH_SYN|TH_ACK|TH_ECN:
case TH_SYN|TH_ACK|TH_ECN|TH_CWR:
if (ssn->flags & STREAMTCP_FLAG_4WHS && PKT_IS_TOSERVER(p)) {
SCLogDebug("ssn %p: SYN/ACK received on 4WHS session", ssn);
/* Check if the SYN/ACK packet ack's the earlier
* received SYN packet. */
if (!(SEQ_EQ(TCP_GET_ACK(p), ssn->server.isn + 1))) {
StreamTcpSetEvent(p, STREAM_4WHS_SYNACK_WITH_WRONG_ACK);
SCLogDebug("ssn %p: 4WHS ACK mismatch, packet ACK %"PRIu32""
" != %" PRIu32 " from stream", ssn,
TCP_GET_ACK(p), ssn->server.isn + 1);
return -1;
}
/* Check if the SYN/ACK packet SEQ's the *FIRST* received SYN
* packet. */
if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.isn))) {
StreamTcpSetEvent(p, STREAM_4WHS_SYNACK_WITH_WRONG_SYN);
SCLogDebug("ssn %p: 4WHS SEQ mismatch, packet SEQ %"PRIu32""
" != %" PRIu32 " from *first* SYN pkt", ssn,
TCP_GET_SEQ(p), ssn->client.isn);
return -1;
}
/* update state */
StreamTcpPacketSetState(p, ssn, TCP_SYN_RECV);
SCLogDebug("ssn %p: =~ 4WHS ssn state is now TCP_SYN_RECV", ssn);
/* sequence number & window */
ssn->client.isn = TCP_GET_SEQ(p);
STREAMTCP_SET_RA_BASE_SEQ(&ssn->client, ssn->client.isn);
ssn->client.next_seq = ssn->client.isn + 1;
ssn->server.window = TCP_GET_WINDOW(p);
SCLogDebug("ssn %p: 4WHS window %" PRIu32 "", ssn,
ssn->client.window);
/* Set the timestamp values used to validate the timestamp of
* received packets. */
if ((p->tcpvars.ts != NULL) && (ssn->server.flags &
STREAMTCP_FLAG_TIMESTAMP))
{
ssn->client.last_ts = TCP_GET_TSVAL(p);
SCLogDebug("ssn %p: 4WHS ssn->client.last_ts %" PRIu32" "
"ssn->server.last_ts %" PRIu32"", ssn,
ssn->client.last_ts, ssn->server.last_ts);
ssn->server.flags &= ~STREAMTCP_FLAG_TIMESTAMP;
ssn->flags |= STREAMTCP_FLAG_TIMESTAMP;
ssn->client.last_pkt_ts = p->ts.tv_sec;
if (ssn->client.last_ts == 0)
ssn->client.flags |= STREAMTCP_FLAG_ZERO_TIMESTAMP;
} else {
ssn->server.last_ts = 0;
ssn->client.last_ts = 0;
ssn->server.flags &= ~STREAMTCP_FLAG_TIMESTAMP;
ssn->server.flags &= ~STREAMTCP_FLAG_ZERO_TIMESTAMP;
}
ssn->server.last_ack = TCP_GET_ACK(p);
ssn->client.last_ack = ssn->client.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->server.wscale = TCP_GET_WSCALE(p);
} else {
ssn->server.wscale = 0;
}
if ((ssn->flags & STREAMTCP_FLAG_CLIENT_SACKOK) &&
TCP_GET_SACKOK(p) == 1) {
ssn->flags |= STREAMTCP_FLAG_SACKOK;
SCLogDebug("ssn %p: SACK permitted for 4WHS session", ssn);
}
ssn->client.next_win = ssn->client.last_ack + ssn->client.window;
ssn->server.next_win = ssn->server.last_ack + ssn->server.window;
SCLogDebug("ssn %p: 4WHS ssn->client.next_win %" PRIu32 "", ssn,
ssn->client.next_win);
SCLogDebug("ssn %p: 4WHS ssn->server.next_win %" PRIu32 "", ssn,
ssn->server.next_win);
SCLogDebug("ssn %p: 4WHS ssn->client.isn %" PRIu32 ", "
"ssn->client.next_seq %" PRIu32 ", "
"ssn->client.last_ack %" PRIu32 " "
"(ssn->server.last_ack %" PRIu32 ")", ssn,
ssn->client.isn, ssn->client.next_seq,
ssn->client.last_ack, ssn->server.last_ack);
/* done here */
break;
}
if (PKT_IS_TOSERVER(p)) {
StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_IN_WRONG_DIRECTION);
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))) {
StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_WITH_WRONG_ACK);
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);
STREAMTCP_SET_RA_BASE_SEQ(&ssn->server, 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);
/* Set the timestamp values used to validate the timestamp of
* received packets.*/
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;
}
if ((ssn->flags & STREAMTCP_FLAG_CLIENT_SACKOK) &&
TCP_GET_SACKOK(p) == 1) {
ssn->flags |= STREAMTCP_FLAG_SACKOK;
SCLogDebug("ssn %p: SACK permitted for session", ssn);
}
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);
/* unset the 4WHS flag as we received this SYN/ACK as part of a
* (so far) valid 3WHS */
if (ssn->flags & STREAMTCP_FLAG_4WHS)
SCLogDebug("ssn %p: STREAMTCP_FLAG_4WHS unset, normal SYN/ACK"
" so considering 3WHS", ssn);
ssn->flags &=~ STREAMTCP_FLAG_4WHS;
break;
case TH_ACK:
case TH_ACK|TH_URG:
case TH_ACK|TH_ECN:
case TH_ACK|TH_ECN|TH_CWR:
case TH_ACK|TH_PUSH:
case TH_ACK|TH_PUSH|TH_URG:
case TH_ACK|TH_PUSH|TH_ECN:
case TH_ACK|TH_PUSH|TH_ECN|TH_CWR:
/* Handle the asynchronous stream, when we receive a SYN packet
and now istead of receving a SYN/ACK we receive a ACK from the
same host, which sent the SYN, this suggests the ASNYC streams.*/
if (stream_config.async_oneside == FALSE)
break;
/* we are in AYNC (one side) mode now. */
/* one side async means we won't see a SYN/ACK, so we can
* only check the SYN. */
if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.next_seq))) {
StreamTcpSetEvent(p, STREAM_3WHS_ASYNC_WRONG_SEQ);
SCLogDebug("ssn %p: SEQ mismatch, packet SEQ %" PRIu32 " != "
"%" PRIu32 " from stream",ssn, TCP_GET_SEQ(p),
ssn->client.next_seq);
return -1;
}
ssn->flags |= STREAMTCP_FLAG_ASYNC;
StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED);
SCLogDebug("ssn %p: =~ ssn state is now TCP_ESTABLISHED", ssn);
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;
/* Set the server side parameters */
ssn->server.isn = TCP_GET_ACK(p) - 1;
STREAMTCP_SET_RA_BASE_SEQ(&ssn->server, ssn->server.isn);
ssn->server.next_seq = ssn->server.isn + 1;
ssn->server.last_ack = ssn->server.next_seq;
ssn->server.next_win = ssn->server.last_ack;
SCLogDebug("ssn %p: synsent => Asynchronous stream, packet SEQ"
" %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "), "
"ssn->client.next_seq %" PRIu32 ""
,ssn, TCP_GET_SEQ(p), p->payload_len, TCP_GET_SEQ(p)
+ p->payload_len, ssn->client.next_seq);
ssn->client.wscale = TCP_WSCALE_MAX;
ssn->server.wscale = TCP_WSCALE_MAX;
/* Set the timestamp values used to validate the timestamp of
* received packets.*/
if (p->tcpvars.ts != NULL &&
(ssn->client.flags & STREAMTCP_FLAG_TIMESTAMP))
{
ssn->flags |= STREAMTCP_FLAG_TIMESTAMP;
ssn->client.flags &= ~STREAMTCP_FLAG_TIMESTAMP;
ssn->client.last_pkt_ts = p->ts.tv_sec;
} else {
ssn->client.last_ts = 0;
ssn->client.flags &= ~STREAMTCP_FLAG_TIMESTAMP;
ssn->client.flags &= ~STREAMTCP_FLAG_ZERO_TIMESTAMP;
}
if (ssn->flags & STREAMTCP_FLAG_CLIENT_SACKOK) {
ssn->flags |= STREAMTCP_FLAG_SACKOK;
}
break;
case TH_RST:
case TH_RST|TH_ACK:
case TH_RST|TH_ACK|TH_ECN:
case TH_RST|TH_ACK|TH_ECN|TH_CWR:
if (StreamTcpValidateRst(ssn, p)) {
if (PKT_IS_TOSERVER(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);
SCLogDebug("ssn %p: Reset received and state changed to "
"TCP_CLOSED", ssn);
}
} else {
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: Reset received and 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_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
* \param p Packet which has to be handled in this TCP state.
* \param stt Strean Thread module registered to handle the stream handling
*
* \retval 0 ok
* \retval -1 error
*/
static int StreamTcpPacketStateSynRecv(ThreadVars *tv, Packet *p,
StreamTcpThread *stt, TcpSession *ssn, PacketQueue *pq)
{
if (ssn == NULL)
return -1;
switch (p->tcph->th_flags) {
case TH_SYN:
case TH_SYN|TH_URG:
case TH_SYN|TH_CWR:
case TH_SYN|TH_CWR|TH_ECN:
SCLogDebug("ssn %p: SYN packet on state SYN_RECV... resent", ssn);
if (PKT_IS_TOCLIENT(p)) {
SCLogDebug("ssn %p: SYN-pkt to client in SYN_RECV state", ssn);
StreamTcpSetEvent(p, STREAM_3WHS_SYN_TOCLIENT_ON_SYN_RECV);
return -1;
}
if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.isn))) {
SCLogDebug("ssn %p: SYN with different SEQ on SYN_RECV state", ssn);
StreamTcpSetEvent(p, STREAM_3WHS_SYN_RESEND_DIFF_SEQ_ON_SYN_RECV);
return -1;
}
break;
case TH_SYN|TH_ACK:
case TH_SYN|TH_ACK|TH_ECN:
case TH_SYN|TH_ACK|TH_ECN|TH_CWR:
SCLogDebug("ssn %p: SYN/ACK packet on state SYN_RECV. resent", ssn);
if (PKT_IS_TOSERVER(p)) {
SCLogDebug("ssn %p: SYN/ACK-pkt to server in SYN_RECV state", ssn);
StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_TOSERVER_ON_SYN_RECV);
return -1;
}
/* Check if the SYN/ACK packets ACK matches the earlier
* received SYN/ACK packet. */
if (!(SEQ_EQ(TCP_GET_ACK(p), ssn->client.last_ack))) {
SCLogDebug("ssn %p: ACK mismatch, packet ACK %" PRIu32 " != "
"%" PRIu32 " from stream", ssn, TCP_GET_ACK(p),
ssn->client.isn + 1);
StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_RESEND_WITH_DIFFERENT_ACK);
return -1;
}
/* Check if the SYN/ACK packet SEQ the earlier
* received SYN packet. */
if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->server.isn))) {
SCLogDebug("ssn %p: SEQ mismatch, packet SEQ %" PRIu32 " != "
"%" PRIu32 " from stream", ssn, TCP_GET_ACK(p),
ssn->client.isn + 1);
StreamTcpSetEvent(p, STREAM_3WHS_SYNACK_RESEND_WITH_DIFF_SEQ);
return -1;
}
break;
case TH_ACK:
case TH_ACK|TH_URG:
case TH_ACK|TH_ECN:
case TH_ACK|TH_ECN|TH_CWR:
case TH_ACK|TH_PUSH:
case TH_ACK|TH_PUSH|TH_URG:
case TH_ACK|TH_PUSH|TH_ECN:
case TH_ACK|TH_PUSH|TH_ECN|TH_CWR:
/* 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 (!(StreamTcpValidateTimestamp(ssn, p))) {
return -1;
}
}
if (ssn->flags & STREAMTCP_FLAG_4WHS && PKT_IS_TOCLIENT(p)) {
SCLogDebug("ssn %p: ACK received on 4WHS session",ssn);
if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->server.next_seq))) {
SCLogDebug("ssn %p: 4WHS wrong seq nr on packet", ssn);
StreamTcpSetEvent(p, STREAM_4WHS_WRONG_SEQ);
return -1;
}
if (StreamTcpValidateAck(&ssn->client, p) == -1) {
SCLogDebug("ssn %p: 4WHS invalid ack nr on packet", ssn);
StreamTcpSetEvent(p, STREAM_4WHS_INVALID_ACK);
return -1;
}
SCLogDebug("4WHS normal pkt");
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 (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
ssn->server.next_seq += p->payload_len;
ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
ssn->client.next_win = ssn->client.last_ack + ssn->client.window;
StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED);
SCLogDebug("ssn %p: =~ ssn state is now TCP_ESTABLISHED", ssn);
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->server, p, pq);
SCLogDebug("ssn %p: ssn->client.next_win %" PRIu32 ", "
"ssn->client.last_ack %"PRIu32"", ssn,
ssn->client.next_win, ssn->client.last_ack);
break;
}
/* Check if the ACK received is in right direction. But when we have
* picked up a mid stream session after missing the initial SYN pkt,
* in this case the ACK packet can arrive from either client (normal
* case) or from server itself (asynchronous streams). Therefore
* the check has been avoided in this case */
if (PKT_IS_TOCLIENT(p)) {
/* special case, handle 4WHS, so SYN/ACK in the opposite
* direction */
if (ssn->flags & STREAMTCP_FLAG_MIDSTREAM_SYNACK) {
SCLogDebug("ssn %p: ACK received on midstream SYN/ACK "
"pickup session",ssn);
/* fall through */
} else {
SCLogDebug("ssn %p: ACK received in the wrong direction",
ssn);
StreamTcpSetEvent(p, STREAM_3WHS_ACK_IN_WRONG_DIR);
return -1;
}
}
SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ %" PRIu32 ""
", ACK %" PRIu32 "", ssn, p->payload_len, TCP_GET_SEQ(p),
TCP_GET_ACK(p));
/* Check both seq and ack number before accepting the packet and
changing to ESTABLISHED state */
if ((SEQ_EQ(TCP_GET_SEQ(p), ssn->client.next_seq)) &&
SEQ_EQ(TCP_GET_ACK(p), ssn->server.next_seq)) {
SCLogDebug("normal pkt");
/* process the packet normal, No Async streams :) */
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
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;
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;
ssn->flags |= STREAMTCP_FLAG_SACKOK;
}
StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED);
SCLogDebug("ssn %p: =~ ssn state is now TCP_ESTABLISHED", ssn);
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->client, p, pq);
/* If asynchronous stream handling is allowed then set the session,
if packet's seq number is equal the expected seq no.*/
} else if (stream_config.async_oneside == TRUE &&
(SEQ_EQ(TCP_GET_SEQ(p), ssn->server.next_seq)))
{
/*set the ASYNC flag used to indicate the session as async stream
and helps in relaxing the windows checks.*/
ssn->flags |= STREAMTCP_FLAG_ASYNC;
ssn->server.next_seq += p->payload_len;
ssn->server.last_ack = TCP_GET_SEQ(p);
ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
ssn->client.last_ack = TCP_GET_ACK(p);
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
if (ssn->flags & STREAMTCP_FLAG_MIDSTREAM) {
ssn->server.window = TCP_GET_WINDOW(p);
ssn->client.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;
ssn->flags |= STREAMTCP_FLAG_SACKOK;
}
SCLogDebug("ssn %p: synrecv => Asynchronous stream, packet SEQ"
" %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "), "
"ssn->server.next_seq %" PRIu32 "\n"
, ssn, TCP_GET_SEQ(p), p->payload_len, TCP_GET_SEQ(p)
+ p->payload_len, ssn->server.next_seq);
StreamTcpPacketSetState(p, ssn, TCP_ESTABLISHED);
SCLogDebug("ssn %p: =~ ssn state is now TCP_ESTABLISHED", ssn);
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->server, p, pq);
/* Upon receiving the packet with correct seq number and wrong
ACK number, it causes the other end to send RST. But some target
system (Linux & solaris) does not RST the connection, so it is
likely to avoid the detection */
} else if (SEQ_EQ(TCP_GET_SEQ(p), ssn->client.next_seq)){
ssn->flags |= STREAMTCP_FLAG_DETECTION_EVASION_ATTEMPT;
SCLogDebug("ssn %p: wrong ack nr on packet, possible evasion!!",
ssn);
StreamTcpSetEvent(p, STREAM_3WHS_RIGHT_SEQ_WRONG_ACK_EVASION);
return -1;
} else {
SCLogDebug("ssn %p: wrong seq nr on packet", ssn);
StreamTcpSetEvent(p, STREAM_3WHS_WRONG_SEQ_WRONG_ACK);
return -1;
}
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:
case TH_RST|TH_ACK|TH_ECN:
case TH_RST|TH_ACK|TH_ECN|TH_CWR:
if(StreamTcpValidateRst(ssn, p)) {
uint8_t reset = TRUE;
/* After receiveing the RST in SYN_RECV state and if detection
evasion flags has been set, then the following operating
systems will not closed the connection. As they consider the
packet as stray packet and not belonging to the current
session, for more information check
http://www.packetstan.com/2010/06/recently-ive-been-on-campaign-to-make.html */
if (ssn->flags & STREAMTCP_FLAG_DETECTION_EVASION_ATTEMPT) {
if (PKT_IS_TOSERVER(p)) {
if ((ssn->server.os_policy == OS_POLICY_LINUX) ||
(ssn->server.os_policy == OS_POLICY_OLD_LINUX) ||
(ssn->server.os_policy == OS_POLICY_SOLARIS))
{
reset = FALSE;
SCLogDebug("Detection evasion has been attempted, so"
" not resetting the connection !!");
}
} else {
if ((ssn->client.os_policy == OS_POLICY_LINUX) |
(ssn->client.os_policy == OS_POLICY_OLD_LINUX) ||
(ssn->client.os_policy == OS_POLICY_SOLARIS))
{
reset = FALSE;
SCLogDebug("Detection evasion has been attempted, so"
" not resetting the connection !!");
}
}
}
if (reset == TRUE) {
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: Reset received and state changed to "
"TCP_CLOSED", ssn);
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
}
} 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 (!StreamTcpValidateTimestamp(ssn, p))
return -1;
}
if((StreamTcpHandleFin(tv, stt, ssn, p, pq)) == -1)
return -1;
break;
default:
SCLogDebug("ssn %p: default case", ssn);
break;
}
return 0;
}
/**
* \brief Function to handle the TCP_ESTABLISHED state packets, which are
* sent by the client to server. The function handles
* ACK packets and call StreamTcpReassembleHandleSegment() to handle
* the reassembly.
*
* Timestamp has already been checked at this point.
*
* \param tv Thread Variable containig input/output queue, cpu affinity etc.
* \param ssn Pointer to the current TCP session
* \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 HandleEstablishedPacketToServer(ThreadVars *tv, TcpSession *ssn, Packet *p,
StreamTcpThread *stt, PacketQueue *pq)
{
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 (StreamTcpValidateAck(&ssn->server, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_EST_INVALID_ACK);
return -1;
}
if (!(SEQ_GEQ((TCP_GET_SEQ(p)+p->payload_len), ssn->client.last_ack))) {
if (ssn->flags & STREAMTCP_FLAG_ASYNC) {
SCLogDebug("ssn %p: server => Asynchrouns stream, packet SEQ"
" %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "),"
" ssn->client.last_ack %" PRIu32 ", ssn->client.next_win"
"%" PRIu32"(%"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);
/* update the last_ack to current seq number as the session is
* async and other stream is not updating it anymore :( */
StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_SEQ(p));
} else if (SEQ_EQ(ssn->client.next_seq, TCP_GET_SEQ(p)) &&
(stream_config.async_oneside == TRUE) &&
(ssn->flags & STREAMTCP_FLAG_MIDSTREAM)) {
SCLogDebug("ssn %p: server => Asynchronous stream, packet SEQ."
" %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "), "
"ssn->client.last_ack %" PRIu32 ", ssn->client.next_win "
"%" PRIu32 "(%"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);
/* it seems we missed SYN and SYN/ACK packets of this session.
* Update the last_ack to current seq number as the session
* is async and other stream is not updating it anymore :( */
StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_SEQ(p));
ssn->flags |= STREAMTCP_FLAG_ASYNC;
} else if (SEQ_EQ(ssn->client.last_ack, (ssn->client.isn + 1)) &&
(stream_config.async_oneside == TRUE) &&
(ssn->flags & STREAMTCP_FLAG_MIDSTREAM)) {
SCLogDebug("ssn %p: server => Asynchronous stream, packet SEQ"
" %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "), "
"ssn->client.last_ack %" PRIu32 ", ssn->client.next_win "
"%" PRIu32 "(%"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);
/* it seems we missed SYN and SYN/ACK packets of this session.
* Update the last_ack to current seq number as the session
* is async and other stream is not updating it anymore :(*/
StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_SEQ(p));
ssn->flags |= STREAMTCP_FLAG_ASYNC;
} 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, 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);
StreamTcpSetEvent(p, STREAM_EST_PKT_BEFORE_LAST_ACK);
return -1;
}
}
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);
}
/* in window check */
if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->client.next_win) ||
(ssn->flags & STREAMTCP_FLAG_MIDSTREAM) ||
ssn->flags & STREAMTCP_FLAG_ASYNC)
{
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);
/* Check if the ACK value is sane and inside the window limit */
StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
/* Update the next_seq, in case if we have missed the server packet
and client has already received and acked it */
if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
ssn->server.next_seq = TCP_GET_ACK(p);
StreamTcpSackUpdatePacket(&ssn->server, p);
/* update next_win */
StreamTcpUpdateNextWin(ssn, &ssn->server, (ssn->server.last_ack + ssn->server.window));
/* handle data (if any) */
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, &ssn->client, p, pq);
} else {
SCLogDebug("ssn %p: toserver => SEQ out of window, packet SEQ "
"%" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "),"
"ssn->client.last_ack %" PRIu32 ", ssn->client.next_win "
"%" PRIu32 "(%"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);
SCLogDebug("ssn %p: window %u sacked %u", ssn, ssn->client.window,
StreamTcpSackedSize(&ssn->client));
StreamTcpSetEvent(p, STREAM_EST_PACKET_OUT_OF_WINDOW);
return -1;
}
return 0;
}
/**
* \brief Function to handle the TCP_ESTABLISHED state packets, which are
* sent by the server to client. The function handles
* ACK packets and call StreamTcpReassembleHandleSegment() to handle
* the reassembly
*
* Timestamp has already been checked at this point.
*
* \param tv Thread Variable containig input/output queue, cpu affinity etc.
* \param ssn Pointer to the current TCP session
* \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 HandleEstablishedPacketToClient(ThreadVars *tv, TcpSession *ssn, Packet *p,
StreamTcpThread *stt, PacketQueue *pq)
{
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));
if (StreamTcpValidateAck(&ssn->client, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_EST_INVALID_ACK);
return -1;
}
/* 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_GEQ((TCP_GET_SEQ(p)+p->payload_len), ssn->server.last_ack))) {
if (ssn->flags & STREAMTCP_FLAG_ASYNC) {
SCLogDebug("ssn %p: client => Asynchrouns stream, packet SEQ"
" %" PRIu32 ", payload size %" PRIu32 " (%" PRIu32 "),"
" ssn->client.last_ack %" PRIu32 ", ssn->client.next_win"
" %"PRIu32"(%"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.last_ack = TCP_GET_SEQ(p);
} else {
SCLogDebug("ssn %p: PKT SEQ %"PRIu32" payload_len %"PRIu16
" before last_ack %"PRIu32,
ssn, TCP_GET_SEQ(p), p->payload_len, ssn->server.last_ack);
StreamTcpSetEvent(p, STREAM_EST_PKT_BEFORE_LAST_ACK);
return -1;
}
}
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_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->server.next_win) ||
(ssn->flags & STREAMTCP_FLAG_MIDSTREAM) ||
(ssn->flags & STREAMTCP_FLAG_ASYNC)) {
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);
StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
/* Update the next_seq, in case if we have missed the client packet
and server has already received and acked it */
if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p)))
ssn->client.next_seq = TCP_GET_ACK(p);
StreamTcpSackUpdatePacket(&ssn->client, p);
StreamTcpUpdateNextWin(ssn, &ssn->client, (ssn->client.last_ack + ssn->client.window));
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, &ssn->server, p, pq);
} 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, 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);
StreamTcpSetEvent(p, STREAM_EST_PACKET_OUT_OF_WINDOW);
return -1;
}
return 0;
}
/**
* \internal
*
* \brief Find the highest sequence number needed to consider all segments as ACK'd
*
* Used to treat all segments as ACK'd upon receiving a valid RST.
*
* \param stream stream to inspect the segments from
* \param seq sequence number to check against
*
* \retval ack highest ack we need to set
*/
static inline uint32_t StreamTcpResetGetMaxAck(TcpStream *stream, uint32_t seq) {
uint32_t ack = seq;
if (stream->seg_list_tail != NULL) {
if (SEQ_GT((stream->seg_list_tail->seq + stream->seg_list_tail->payload_len), ack))
{
ack = stream->seg_list_tail->seq + stream->seg_list_tail->payload_len;
}
}
SCReturnUInt(ack);
}
/**
* \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(tv, ) 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, PacketQueue *pq)
{
if (ssn == NULL)
return -1;
switch (p->tcph->th_flags) {
case TH_SYN:
case TH_SYN|TH_URG:
case TH_SYN|TH_CWR:
case TH_SYN|TH_CWR|TH_ECN:
SCLogDebug("ssn %p: SYN packet on state ESTABLISED... resent", ssn);
if (PKT_IS_TOCLIENT(p)) {
SCLogDebug("ssn %p: SYN-pkt to client in EST state", ssn);
StreamTcpSetEvent(p, STREAM_EST_SYN_TOCLIENT);
return -1;
}
if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->client.isn))) {
SCLogDebug("ssn %p: SYN with different SEQ on SYN_RECV state", ssn);
StreamTcpSetEvent(p, STREAM_EST_SYN_RESEND_DIFF_SEQ);
return -1;
}
/* a resend of a SYN while we are established already -- fishy */
StreamTcpSetEvent(p, STREAM_EST_SYN_RESEND);
return -1;
break;
case TH_SYN|TH_ACK:
case TH_SYN|TH_ACK|TH_ECN:
case TH_SYN|TH_ACK|TH_ECN|TH_CWR:
SCLogDebug("ssn %p: SYN/ACK packet on state ESTABLISHED... resent",
ssn);
if (PKT_IS_TOSERVER(p)) {
SCLogDebug("ssn %p: SYN/ACK-pkt to server in ESTABLISHED state", ssn);
StreamTcpSetEvent(p, STREAM_EST_SYNACK_TOSERVER);
return -1;
}
/* Check if the SYN/ACK packets ACK matches the earlier
* received SYN/ACK packet. */
if (!(SEQ_EQ(TCP_GET_ACK(p), ssn->client.last_ack))) {
SCLogDebug("ssn %p: ACK mismatch, packet ACK %" PRIu32 " != "
"%" PRIu32 " from stream", ssn, TCP_GET_ACK(p),
ssn->client.isn + 1);
StreamTcpSetEvent(p, STREAM_EST_SYNACK_RESEND_WITH_DIFFERENT_ACK);
return -1;
}
/* Check if the SYN/ACK packet SEQ the earlier
* received SYN packet. */
if (!(SEQ_EQ(TCP_GET_SEQ(p), ssn->server.isn))) {
SCLogDebug("ssn %p: SEQ mismatch, packet SEQ %" PRIu32 " != "
"%" PRIu32 " from stream", ssn, TCP_GET_ACK(p),
ssn->client.isn + 1);
StreamTcpSetEvent(p, STREAM_EST_SYNACK_RESEND_WITH_DIFF_SEQ);
return -1;
}
/* a resend of a SYN while we are established already -- fishy */
StreamTcpSetEvent(p, STREAM_EST_SYNACK_RESEND);
return -1;
break;
case TH_ACK|TH_URG:
case TH_ACK:
case TH_ACK|TH_CWR:
case TH_ACK|TH_ECN:
case TH_ACK|TH_PUSH:
case TH_ACK|TH_PUSH|TH_ECN:
case TH_ACK|TH_PUSH|TH_ECN|TH_CWR:
case TH_ACK|TH_PUSH|TH_URG:
/* Urgent pointer size can be more than the payload size, as it tells
* the future coming data from the sender will be handled urgently
* until data of size equal to urgent offset has been processed
* (RFC 2147) */
/* 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 (!StreamTcpValidateTimestamp(ssn, p))
return -1;
}
if (PKT_IS_TOSERVER(p)) {
/* Process the received packet to server */
HandleEstablishedPacketToServer(tv, ssn, p, stt, pq);
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 */
/* Process the received packet to client */
HandleEstablishedPacketToClient(tv, ssn, p, stt, pq);
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_ECN:
case TH_FIN|TH_ACK|TH_ECN|TH_CWR:
case TH_FIN|TH_ACK|TH_PUSH:
case TH_FIN|TH_ACK|TH_PUSH|TH_ECN:
case TH_FIN|TH_ACK|TH_PUSH|TH_ECN|TH_CWR:
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!StreamTcpValidateTimestamp(ssn, p))
return -1;
}
SCLogDebug("ssn (%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(tv, stt, ssn, p, pq)) == -1)
return -1;
break;
case TH_RST:
case TH_RST|TH_ACK:
case TH_RST|TH_ACK|TH_ECN:
case TH_RST|TH_ACK|TH_ECN|TH_CWR:
if (StreamTcpValidateRst(ssn, p)) {
/* force both streams to reassemble, if necessary */
StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq);
SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca);
if(PKT_IS_TOSERVER(p)) {
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: Reset received and state changed to "
"TCP_CLOSED", ssn);
ssn->server.next_seq = TCP_GET_ACK(p);
ssn->client.next_seq = TCP_GET_SEQ(p) + p->payload_len;
SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", ssn,
ssn->server.next_seq);
ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
StreamTcpUpdateLastAck(ssn, &ssn->server,
StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p)));
StreamTcpUpdateLastAck(ssn, &ssn->client,
StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p)));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->client, p, pq);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
"%" PRIu32 "", ssn, ssn->client.next_seq,
ssn->server.last_ack);
/* don't return packets to pools here just yet, the pseudo
* packet will take care, otherwise the normal session
* cleanup. */
} 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;
StreamTcpUpdateLastAck(ssn, &ssn->client,
StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p)));
StreamTcpUpdateLastAck(ssn, &ssn->server,
StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p)));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->server, p, pq);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
"%" PRIu32 "", ssn, ssn->server.next_seq,
ssn->client.last_ack);
/* don't return packets to pools here just yet, the pseudo
* packet will take care, otherwise the normal session
* cleanup. */
}
} else {
/* invalid RST, error is given in StreamTcpValidateRst() */
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
* \param p Packet which has to be handled in this TCP state.
* \param stt Strean Thread module registered to handle the stream handling
*
* \retval 0 success
* \retval -1 something wrong with the packet
*/
static int StreamTcpHandleFin(ThreadVars *tv, StreamTcpThread *stt,
TcpSession *ssn, Packet *p, PacketQueue *pq)
{
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 (StreamTcpValidateAck(&ssn->server, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_FIN_INVALID_ACK);
return -1;
}
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);
StreamTcpSetEvent(p, STREAM_FIN_OUT_OF_WINDOW);
return -1;
}
StreamTcpPacketSetState(p, ssn, TCP_CLOSE_WAIT);
SCLogDebug("ssn %p: state changed to TCP_CLOSE_WAIT", ssn);
if (SEQ_EQ(TCP_GET_SEQ(p), ssn->client.next_seq))
ssn->client.next_seq = TCP_GET_SEQ(p) + p->payload_len;
SCLogDebug("ssn %p: ssn->client.next_seq %" PRIu32 "", ssn,
ssn->client.next_seq);
ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
/* Update the next_seq, in case if we have missed the client packet
and server has already received and acked it */
if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
ssn->server.next_seq = TCP_GET_ACK(p);
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, &ssn->client, p, pq);
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 (StreamTcpValidateAck(&ssn->client, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_FIN_INVALID_ACK);
return -1;
}
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);
StreamTcpSetEvent(p, STREAM_FIN_OUT_OF_WINDOW);
return -1;
}
StreamTcpPacketSetState(p, ssn, TCP_FIN_WAIT1);
SCLogDebug("ssn %p: state changed to TCP_FIN_WAIT1", ssn);
if (SEQ_EQ(TCP_GET_SEQ(p), ssn->server.next_seq))
ssn->server.next_seq = TCP_GET_SEQ(p) + p->payload_len;
SCLogDebug("ssn %p: ssn->server.next_seq %" PRIu32 "", ssn,
ssn->server.next_seq);
ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
/* Update the next_seq, in case if we have missed the client packet
and server has already received and acked it */
if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p)))
ssn->client.next_seq = TCP_GET_ACK(p);
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn, &ssn->server, p, pq);
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
* \param p Packet which has to be handled in this TCP state.
* \param stt Strean Thread module registered to handle the stream handling
*
* \retval 0 success
* \retval -1 something wrong with the packet
*/
static int StreamTcpPacketStateFinWait1(ThreadVars *tv, Packet *p,
StreamTcpThread *stt, TcpSession *ssn, PacketQueue *pq)
{
if (ssn == NULL)
return -1;
switch (p->tcph->th_flags) {
case TH_ACK:
case TH_ACK|TH_PUSH:
case TH_ACK|TH_URG:
case TH_ACK|TH_ECN:
case TH_ACK|TH_ECN|TH_CWR:
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!StreamTcpValidateTimestamp(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 (StreamTcpValidateAck(&ssn->server, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK);
return -1;
}
if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->client.next_win) ||
(ssn->flags & STREAMTCP_FLAG_MIDSTREAM) ||
ssn->flags & STREAMTCP_FLAG_ASYNC)
{
SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->client.next_win "
"%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->client.next_win);
if (TCP_GET_SEQ(p) == ssn->client.next_seq) {
StreamTcpPacketSetState(p, ssn, TCP_FIN_WAIT2);
SCLogDebug("ssn %p: state changed to TCP_FIN_WAIT2", ssn);
}
} else {
SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
" != %" PRIu32 " from stream", ssn,
TCP_GET_SEQ(p), ssn->client.next_seq);
StreamTcpSetEvent(p, STREAM_FIN1_ACK_WRONG_SEQ);
return -1;
}
ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
/* Update the next_seq, in case if we have missed the client
packet and server has already received and acked it */
if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
ssn->server.next_seq = TCP_GET_ACK(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);
}
StreamTcpSackUpdatePacket(&ssn->server, p);
/* update next_win */
StreamTcpUpdateNextWin(ssn, &ssn->server, (ssn->server.last_ack + ssn->server.window));
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->client, p, pq);
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 (StreamTcpValidateAck(&ssn->client, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK);
return -1;
}
if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->server.next_win) ||
(ssn->flags & STREAMTCP_FLAG_MIDSTREAM) ||
ssn->flags & STREAMTCP_FLAG_ASYNC)
{
SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->server.next_win "
"%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->server.next_win);
if (TCP_GET_SEQ(p) == ssn->server.next_seq) {
StreamTcpPacketSetState(p, ssn, TCP_FIN_WAIT2);
SCLogDebug("ssn %p: state changed to TCP_FIN_WAIT2", ssn);
}
} else {
SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
" != %" PRIu32 " from stream", ssn,
TCP_GET_SEQ(p), ssn->server.next_seq);
StreamTcpSetEvent(p, STREAM_FIN1_ACK_WRONG_SEQ);
return -1;
}
ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
/* Update the next_seq, in case if we have missed the client
packet and server has already received and acked it */
if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p)))
ssn->client.next_seq = TCP_GET_ACK(p);
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);
}
StreamTcpSackUpdatePacket(&ssn->client, p);
/* update next_win */
StreamTcpUpdateNextWin(ssn, &ssn->client, (ssn->client.last_ack + ssn->client.window));
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->server, p, pq);
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_ECN:
case TH_FIN|TH_ACK|TH_ECN|TH_CWR:
case TH_FIN|TH_ACK|TH_PUSH:
case TH_FIN|TH_ACK|TH_PUSH|TH_ECN:
case TH_FIN|TH_ACK|TH_PUSH|TH_ECN|TH_CWR:
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!StreamTcpValidateTimestamp(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);
StreamTcpSetEvent(p, STREAM_FIN1_FIN_WRONG_SEQ);
return -1;
}
if (StreamTcpValidateAck(&ssn->server, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK);
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;
StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
/* Update the next_seq, in case if we have missed the client
packet and server has already received and acked it */
if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
ssn->server.next_seq = TCP_GET_ACK(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);
}
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->client, p, pq);
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);
StreamTcpSetEvent(p, STREAM_FIN1_FIN_WRONG_SEQ);
return -1;
}
if (StreamTcpValidateAck(&ssn->client, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_FIN1_INVALID_ACK);
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;
StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
/* Update the next_seq, in case if we have missed the client
packet and server has already received and acked it */
if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p)))
ssn->client.next_seq = TCP_GET_ACK(p);
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);
}
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->server, p, pq);
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:
case TH_RST|TH_ACK|TH_ECN:
case TH_RST|TH_ACK|TH_ECN|TH_CWR:
if (StreamTcpValidateRst(ssn, p)) {
/* force both streams to reassemble, if necessary */
StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq);
SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca);
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED",
ssn);
if (PKT_IS_TOSERVER(p)) {
StreamTcpUpdateLastAck(ssn, &ssn->server,
StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p)));
StreamTcpUpdateLastAck(ssn, &ssn->client,
StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p)));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->client, p, pq);
} else {
StreamTcpUpdateLastAck(ssn, &ssn->client,
StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p)));
StreamTcpUpdateLastAck(ssn, &ssn->server,
StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p)));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->server, p, pq);
}
}
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
* \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, PacketQueue *pq)
{
if (ssn == NULL)
return -1;
switch (p->tcph->th_flags) {
case TH_ACK:
case TH_ACK|TH_PUSH:
case TH_ACK|TH_URG:
case TH_ACK|TH_ECN:
case TH_ACK|TH_ECN|TH_CWR:
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!StreamTcpValidateTimestamp(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 (StreamTcpValidateAck(&ssn->server, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_FIN2_INVALID_ACK);
return -1;
}
if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->client.next_win) ||
(ssn->flags & STREAMTCP_FLAG_MIDSTREAM) ||
ssn->flags & STREAMTCP_FLAG_ASYNC)
{
SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->client.next_win "
"%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->client.next_win);
} else {
SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
" != %" PRIu32 " from stream", ssn,
TCP_GET_SEQ(p), ssn->client.next_seq);
StreamTcpSetEvent(p, STREAM_FIN2_ACK_WRONG_SEQ);
return -1;
}
ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, 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);
}
StreamTcpSackUpdatePacket(&ssn->server, p);
/* update next_win */
StreamTcpUpdateNextWin(ssn, &ssn->server, (ssn->server.last_ack + ssn->server.window));
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->client, p, pq);
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 (StreamTcpValidateAck(&ssn->client, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_FIN2_INVALID_ACK);
return -1;
}
if (SEQ_LEQ(TCP_GET_SEQ(p) + p->payload_len, ssn->server.next_win) ||
(ssn->flags & STREAMTCP_FLAG_MIDSTREAM) ||
ssn->flags & STREAMTCP_FLAG_ASYNC)
{
SCLogDebug("ssn %p: seq %"PRIu32" in window, ssn->server.next_win "
"%" PRIu32 "", ssn, TCP_GET_SEQ(p), ssn->server.next_win);
} else {
SCLogDebug("ssn %p: -> SEQ mismatch, packet SEQ %" PRIu32 ""
" != %" PRIu32 " from stream", ssn,
TCP_GET_SEQ(p), ssn->server.next_seq);
StreamTcpSetEvent(p, STREAM_FIN2_ACK_WRONG_SEQ);
return -1;
}
ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
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);
}
StreamTcpSackUpdatePacket(&ssn->client, p);
/* update next_win */
StreamTcpUpdateNextWin(ssn, &ssn->client, (ssn->client.last_ack + ssn->client.window));
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->server, p, pq);
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:
case TH_RST|TH_ACK|TH_ECN:
case TH_RST|TH_ACK|TH_ECN|TH_CWR:
if (StreamTcpValidateRst(ssn, p)) {
/* force both streams to reassemble, if necessary */
StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq);
SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca);
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED",
ssn);
if (PKT_IS_TOSERVER(p)) {
StreamTcpUpdateLastAck(ssn, &ssn->server,
StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p)));
StreamTcpUpdateLastAck(ssn, &ssn->client,
StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p)));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->client, p, pq);
} else {
StreamTcpUpdateLastAck(ssn, &ssn->client,
StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p)));
StreamTcpUpdateLastAck(ssn, &ssn->server,
StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p)));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->server, p, pq);
}
}
else
return -1;
break;
case TH_FIN:
case TH_FIN|TH_ACK:
case TH_FIN|TH_ACK|TH_ECN:
case TH_FIN|TH_ACK|TH_ECN|TH_CWR:
case TH_FIN|TH_ACK|TH_PUSH:
case TH_FIN|TH_ACK|TH_PUSH|TH_ECN:
case TH_FIN|TH_ACK|TH_PUSH|TH_ECN|TH_CWR:
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!StreamTcpValidateTimestamp(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);
StreamTcpSetEvent(p, STREAM_FIN2_FIN_WRONG_SEQ);
return -1;
}
if (StreamTcpValidateAck(&ssn->server, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_FIN2_INVALID_ACK);
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;
StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
/* Update the next_seq, in case if we have missed the client
packet and server has already received and acked it */
if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
ssn->server.next_seq = TCP_GET_ACK(p);
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->client, p, pq);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
"%" PRIu32 "", ssn, ssn->client.next_seq,
ssn->server.last_ack);
StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq);
} 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);
StreamTcpSetEvent(p, STREAM_FIN2_FIN_WRONG_SEQ);
return -1;
}
if (StreamTcpValidateAck(&ssn->client, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_FIN2_INVALID_ACK);
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;
StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
/* Update the next_seq, in case if we have missed the client
packet and server has already received and acked it */
if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p)))
ssn->client.next_seq = TCP_GET_ACK(p);
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->server, p, pq);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
"%" PRIu32 "", ssn, ssn->server.next_seq,
ssn->client.last_ack);
StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq);
}
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
* \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, PacketQueue *pq)
{
if (ssn == NULL)
return -1;
switch(p->tcph->th_flags) {
case TH_ACK:
case TH_ACK|TH_PUSH:
case TH_ACK|TH_ECN:
case TH_ACK|TH_ECN|TH_CWR:
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!StreamTcpValidateTimestamp(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);
StreamTcpSetEvent(p, STREAM_CLOSING_ACK_WRONG_SEQ);
return -1;
}
if (StreamTcpValidateAck(&ssn->server, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_CLOSING_INVALID_ACK);
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;
StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
/* Update the next_seq, in case if we have missed the client
packet and server has already received and acked it */
if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
ssn->server.next_seq = TCP_GET_ACK(p);
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->client, p, pq);
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);
StreamTcpSetEvent(p, STREAM_CLOSING_ACK_WRONG_SEQ);
return -1;
}
if (StreamTcpValidateAck(&ssn->client, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_CLOSING_INVALID_ACK);
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;
StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
/* Update the next_seq, in case if we have missed the client
packet and server has already received and acked it */
if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p)))
ssn->client.next_seq = TCP_GET_ACK(p);
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->server, p, pq);
SCLogDebug("StreamTcpPacketStateClosing (%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:
case TH_RST|TH_ACK|TH_ECN:
case TH_RST|TH_ACK|TH_ECN|TH_CWR:
if (StreamTcpValidateRst(ssn, p)) {
/* force both streams to reassemble, if necessary */
StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq);
SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca);
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED",
ssn);
if (PKT_IS_TOSERVER(p)) {
StreamTcpUpdateLastAck(ssn, &ssn->server,
StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p)));
StreamTcpUpdateLastAck(ssn, &ssn->client,
StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p)));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->client, p, pq);
} else {
StreamTcpUpdateLastAck(ssn, &ssn->client,
StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p)));
StreamTcpUpdateLastAck(ssn, &ssn->server,
StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p)));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->server, p, pq);
}
}
else
return -1;
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
* \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, PacketQueue *pq)
{
SCEnter();
if (ssn == NULL) {
SCReturnInt(-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));
} else {
SCLogDebug("ssn %p: pkt (%" PRIu32 ") is to server: SEQ "
"%" PRIu32 ", ACK %" PRIu32 "", ssn, p->payload_len,
TCP_GET_SEQ(p), TCP_GET_ACK(p));
}
switch(p->tcph->th_flags) {
case TH_FIN:
case TH_FIN|TH_ACK:
case TH_FIN|TH_ACK|TH_ECN:
case TH_FIN|TH_ACK|TH_ECN|TH_CWR:
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!StreamTcpValidateTimestamp(ssn, p))
SCReturnInt(-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);
StreamTcpSetEvent(p, STREAM_CLOSEWAIT_FIN_OUT_OF_WINDOW);
SCReturnInt(-1);
}
if (StreamTcpValidateAck(&ssn->server, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_CLOSEWAIT_INVALID_ACK);
SCReturnInt(-1);
}
StreamTcpPacketSetState(p, ssn, TCP_LAST_ACK);
SCLogDebug("ssn %p: state changed to TCP_LAST_ACK", ssn);
ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
/* Update the next_seq, in case if we have missed the client
packet and server has already received and acked it */
if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
ssn->server.next_seq = TCP_GET_ACK(p);
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->client, p, pq);
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 (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);
StreamTcpSetEvent(p, STREAM_CLOSEWAIT_FIN_OUT_OF_WINDOW);
SCReturnInt(-1);
}
if (StreamTcpValidateAck(&ssn->client, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_CLOSEWAIT_INVALID_ACK);
SCReturnInt(-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;
StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
/* Update the next_seq, in case if we have missed the client
packet and server has already received and acked it */
if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p)))
ssn->client.next_seq = TCP_GET_ACK(p);
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->server, p, pq);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
"%" PRIu32 "", ssn, ssn->server.next_seq,
ssn->client.last_ack);
}
break;
case TH_ACK:
case TH_ACK|TH_PUSH:
case TH_ACK|TH_ECN:
case TH_ACK|TH_ECN|TH_CWR:
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!StreamTcpValidateTimestamp(ssn, p))
SCReturnInt(-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);
SCReturnInt(-1);
StreamTcpSetEvent(p, STREAM_CLOSEWAIT_ACK_OUT_OF_WINDOW);
}
if (StreamTcpValidateAck(&ssn->server, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_CLOSEWAIT_INVALID_ACK);
SCReturnInt(-1);
}
ssn->server.window = TCP_GET_WINDOW(p) << ssn->server.wscale;
StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
/* Update the next_seq, in case if we have missed the client
packet and server has already received and acked it */
if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
ssn->server.next_seq = TCP_GET_ACK(p);
if (SEQ_EQ(TCP_GET_SEQ(p),ssn->client.next_seq))
ssn->client.next_seq += p->payload_len;
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->client, p, pq);
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 (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);
StreamTcpSetEvent(p, STREAM_CLOSEWAIT_ACK_OUT_OF_WINDOW);
SCReturnInt(-1);
}
if (StreamTcpValidateAck(&ssn->client, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_CLOSEWAIT_INVALID_ACK);
SCReturnInt(-1);
}
ssn->client.window = TCP_GET_WINDOW(p) << ssn->client.wscale;
StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
/* Update the next_seq, in case if we have missed the client
packet and server has already received and acked it */
if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p)))
ssn->client.next_seq = TCP_GET_ACK(p);
if (SEQ_EQ(TCP_GET_SEQ(p),ssn->server.next_seq))
ssn->server.next_seq += p->payload_len;
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->server, p, pq);
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:
case TH_RST|TH_ACK|TH_ECN:
case TH_RST|TH_ACK|TH_ECN|TH_CWR:
if (StreamTcpValidateRst(ssn, p)) {
/* force both streams to reassemble, if necessary */
StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq);
SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca);
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED",
ssn);
if (PKT_IS_TOSERVER(p)) {
StreamTcpUpdateLastAck(ssn, &ssn->server,
StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p)));
StreamTcpUpdateLastAck(ssn, &ssn->client,
StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p)));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->client, p, pq);
} else {
StreamTcpUpdateLastAck(ssn, &ssn->client,
StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p)));
StreamTcpUpdateLastAck(ssn, &ssn->server,
StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p)));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->server, p, pq);
}
}
else
return -1;
break;
default:
SCLogDebug("ssn %p: default case", ssn);
break;
}
SCReturnInt(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
* \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, PacketQueue *pq)
{
if (ssn == NULL)
return -1;
switch(p->tcph->th_flags) {
case TH_ACK:
case TH_ACK|TH_PUSH:
case TH_ACK|TH_ECN:
case TH_ACK|TH_ECN|TH_CWR:
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!StreamTcpValidateTimestamp(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);
StreamTcpSetEvent(p, STREAM_LASTACK_ACK_WRONG_SEQ);
return -1;
}
if (StreamTcpValidateAck(&ssn->server, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_LASTACK_INVALID_ACK);
SCReturnInt(-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;
StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
/* Update the next_seq, in case if we have missed the client
packet and server has already received and acked it */
if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
ssn->server.next_seq = TCP_GET_ACK(p);
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->client, p, pq);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
"%" PRIu32 "", ssn, ssn->client.next_seq,
ssn->server.last_ack);
}
break;
case TH_RST:
case TH_RST|TH_ACK:
case TH_RST|TH_ACK|TH_ECN:
case TH_RST|TH_ACK|TH_ECN|TH_CWR:
if (StreamTcpValidateRst(ssn, p)) {
/* force both streams to reassemble, if necessary */
StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq);
SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca);
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED",
ssn);
if (PKT_IS_TOSERVER(p)) {
StreamTcpUpdateLastAck(ssn, &ssn->server,
StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p)));
StreamTcpUpdateLastAck(ssn, &ssn->client,
StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p)));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->client, p, pq);
} else {
StreamTcpUpdateLastAck(ssn, &ssn->client,
StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p)));
StreamTcpUpdateLastAck(ssn, &ssn->server,
StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p)));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->server, p, pq);
}
}
else
return -1;
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
* \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, PacketQueue *pq)
{
if (ssn == NULL)
return -1;
switch(p->tcph->th_flags) {
case TH_ACK:
case TH_ACK|TH_PUSH:
case TH_ACK|TH_ECN:
case TH_ACK|TH_ECN|TH_CWR:
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!StreamTcpValidateTimestamp(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);
StreamTcpSetEvent(p, STREAM_TIMEWAIT_ACK_WRONG_SEQ);
return -1;
}
if (StreamTcpValidateAck(&ssn->server, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_TIMEWAIT_INVALID_ACK);
SCReturnInt(-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;
StreamTcpUpdateLastAck(ssn, &ssn->server, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
/* Update the next_seq, in case if we have missed the client
packet and server has already received and acked it */
if (SEQ_LT(ssn->server.next_seq, TCP_GET_ACK(p)))
ssn->server.next_seq = TCP_GET_ACK(p);
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->client, p, pq);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
"%" PRIu32 "", ssn, ssn->client.next_seq,
ssn->server.last_ack);
StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq);
} 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);
StreamTcpSetEvent(p, STREAM_TIMEWAIT_ACK_WRONG_SEQ);
return -1;
}
if (StreamTcpValidateAck(&ssn->client, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_TIMEWAIT_INVALID_ACK);
SCReturnInt(-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;
StreamTcpUpdateLastAck(ssn, &ssn->client, TCP_GET_ACK(p));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
/* Update the next_seq, in case if we have missed the client
packet and server has already received and acked it */
if (SEQ_LT(ssn->client.next_seq, TCP_GET_ACK(p)))
ssn->client.next_seq = TCP_GET_ACK(p);
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->server, p, pq);
SCLogDebug("ssn %p: =+ next SEQ %" PRIu32 ", last ACK "
"%" PRIu32 "", ssn, ssn->server.next_seq,
ssn->client.last_ack);
StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq);
}
break;
case TH_RST:
case TH_RST|TH_ACK:
case TH_RST|TH_ACK|TH_ECN:
case TH_RST|TH_ACK|TH_ECN|TH_CWR:
if (StreamTcpValidateRst(ssn, p)) {
/* force both streams to reassemble, if necessary */
StreamTcpPseudoPacketCreateStreamEndPacket(p, ssn, pq);
SCPerfCounterIncr(stt->counter_tcp_pseudo, tv->sc_perf_pca);
StreamTcpPacketSetState(p, ssn, TCP_CLOSED);
SCLogDebug("ssn %p: Reset received state changed to TCP_CLOSED",
ssn);
if (PKT_IS_TOSERVER(p)) {
StreamTcpUpdateLastAck(ssn, &ssn->server,
StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_ACK(p)));
StreamTcpUpdateLastAck(ssn, &ssn->client,
StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_SEQ(p)));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->client, p, pq);
} else {
StreamTcpUpdateLastAck(ssn, &ssn->client,
StreamTcpResetGetMaxAck(&ssn->client, TCP_GET_ACK(p)));
StreamTcpUpdateLastAck(ssn, &ssn->server,
StreamTcpResetGetMaxAck(&ssn->server, TCP_GET_SEQ(p)));
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
StreamTcpHandleTimestamp(ssn, p);
}
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->server, p, pq);
}
}
else
return -1;
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,
PacketQueue *pq)
{
SCEnter();
SCLogDebug("p->pcap_cnt %"PRIu64, p->pcap_cnt);
TcpSession *ssn = (TcpSession *)p->flow->protoctx;
/* update counters */
if ((p->tcph->th_flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK)) {
SCPerfCounterIncr(stt->counter_tcp_synack, tv->sc_perf_pca);
} else if (p->tcph->th_flags & (TH_SYN)) {
SCPerfCounterIncr(stt->counter_tcp_syn, tv->sc_perf_pca);
}
if (p->tcph->th_flags & (TH_RST)) {
SCPerfCounterIncr(stt->counter_tcp_rst, tv->sc_perf_pca);
}
/* If we are on IPS mode, and got a drop action triggered from
* the IP only module, or from a reassembled msg and/or from an
* applayer detection, then drop the rest of the packets of the
* same stream and avoid inspecting it any further */
if (StreamTcpCheckFlowDrops(p) == 1) {
SCLogDebug("This flow/stream triggered a drop rule");
FlowSetNoPacketInspectionFlag(p->flow);
DecodeSetNoPacketInspectionFlag(p);
FlowSetSessionNoApplayerInspectionFlag(p->flow);
p->action |= ACTION_DROP;
/* return the segments to the pool */
StreamTcpSessionPktFree(p);
SCReturnInt(0);
}
if (ssn == NULL || ssn->state == TCP_NONE) {
if (StreamTcpPacketStateNone(tv, p, stt, ssn, &stt->pseudo_queue) == -1) {
goto error;
}
if (ssn != NULL)
SCLogDebug("ssn->alproto %"PRIu16"", p->flow->alproto);
} else {
/* check if the packet is in right direction, when we missed the
SYN packet and picked up midstream session. */
if (ssn->flags & STREAMTCP_FLAG_MIDSTREAM_SYNACK)
StreamTcpPacketSwitchDir(ssn, p);
switch (ssn->state) {
case TCP_SYN_SENT:
if(StreamTcpPacketStateSynSent(tv, p, stt, ssn, &stt->pseudo_queue)) {
goto error;
}
break;
case TCP_SYN_RECV:
if(StreamTcpPacketStateSynRecv(tv, p, stt, ssn, &stt->pseudo_queue)) {
goto error;
}
break;
case TCP_ESTABLISHED:
if(StreamTcpPacketStateEstablished(tv, p, stt, ssn, &stt->pseudo_queue)) {
goto error;
}
break;
case TCP_FIN_WAIT1:
if(StreamTcpPacketStateFinWait1(tv, p, stt, ssn, &stt->pseudo_queue)) {
goto error;
}
break;
case TCP_FIN_WAIT2:
if(StreamTcpPacketStateFinWait2(tv, p, stt, ssn, &stt->pseudo_queue)) {
goto error;
}
break;
case TCP_CLOSING:
if(StreamTcpPacketStateClosing(tv, p, stt, ssn, &stt->pseudo_queue)) {
goto error;
}
break;
case TCP_CLOSE_WAIT:
if(StreamTcpPacketStateCloseWait(tv, p, stt, ssn, &stt->pseudo_queue)) {
goto error;
}
break;
case TCP_LAST_ACK:
if(StreamTcpPakcetStateLastAck(tv, p, stt, ssn, &stt->pseudo_queue)) {
goto error;
}
break;
case TCP_TIME_WAIT:
if(StreamTcpPacketStateTimeWait(tv, p, stt, ssn, &stt->pseudo_queue)) {
goto error;
}
break;
case TCP_CLOSED:
/* TCP session memory is not returned to pool until timeout.
* If in the mean time we receive any other session from
* the same client reusing same port then we switch back to
* tcp state none, but only on a valid SYN that is not a
* resend from our previous session.
*
* We also check it's not a SYN/ACK, all other SYN pkt
* validation is done at StreamTcpPacketStateNone();
*/
if (PKT_IS_TOSERVER(p) && (p->tcph->th_flags & TH_SYN) &&
!(p->tcph->th_flags & TH_ACK) &&
!(SEQ_EQ(ssn->client.isn, TCP_GET_SEQ(p))))
{
SCLogDebug("reusing closed TCP session");
/* return segments */
StreamTcpReturnStreamSegments(&ssn->client);
StreamTcpReturnStreamSegments(&ssn->server);
/* free SACK list */
StreamTcpSackFreeList(&ssn->client);
StreamTcpSackFreeList(&ssn->server);
/* reset the app layer state */
AppLayerParserCleanupState(p->flow);
ssn->state = 0;
ssn->flags = 0;
ssn->client.flags = 0;
ssn->server.flags = 0;
/* set state the NONE, also pulls flow out of closed queue */
StreamTcpPacketSetState(p, ssn, TCP_NONE);
p->flow->alproto = ALPROTO_UNKNOWN;
p->flow->flags &= ~FLOW_TS_PM_PP_ALPROTO_DETECT_DONE;
p->flow->flags &= ~FLOW_TS_PM_PP_ALPROTO_DETECT_DONE;
if (StreamTcpPacketStateNone(tv,p,stt,ssn, &stt->pseudo_queue)) {
goto error;
}
SCPerfCounterIncr(stt->counter_tcp_reused_ssn, tv->sc_perf_pca);
} else {
SCLogDebug("packet received on closed state");
}
break;
default:
SCLogDebug("packet received on default state");
break;
}
if (ssn->state >= TCP_ESTABLISHED) {
p->flags |= PKT_STREAM_EST;
}
if (ssn->state > TCP_ESTABLISHED) {
p->flags |= PKT_STREAM_EOF;
}
}
/* deal with a pseudo packet that is created upon receiving a RST
* segment. To be sure we process both sides of the connection, we
* inject a fake packet into the system, forcing reassembly of the
* opposing direction.
* There should be only one, but to be sure we do a while loop. */
while (stt->pseudo_queue.len > 0) {
SCLogDebug("processing pseudo packet / stream end");
Packet *np = PacketDequeue(&stt->pseudo_queue);
if (np != NULL) {
/* process the opposing direction of the original packet */
if (PKT_IS_TOSERVER(np)) {
SCLogDebug("pseudo packet is to server");
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->client, np, NULL);
} else {
SCLogDebug("pseudo packet is to client");
StreamTcpReassembleHandleSegment(tv, stt->ra_ctx, ssn,
&ssn->server, np, NULL);
}
/* enqueue this packet so we inspect it in detect etc */
PacketEnqueue(pq, np);
}
SCLogDebug("processing pseudo packet / stream end done");
}
/* Process stream smsgs we may have in queue */
if (StreamTcpReassembleProcessAppLayer(stt->ra_ctx) < 0) {
goto error;
}
/* recalc the csum on the packet if it was modified */
if (p->flags & PKT_STREAM_MODIFIED) {
ReCalculateChecksum(p);
}
/* check for conditions that may make us not want to log this packet */
if (ssn != NULL) {
/* streams that hit depth */
if ((ssn->client.flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED ||
ssn->server.flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED))
{
p->flags |= PKT_STREAM_NOPCAPLOG;
}
/* encrypted packets */
if ((PKT_IS_TOSERVER(p) && ssn->client.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) ||
(PKT_IS_TOCLIENT(p) && ssn->server.flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY))
{
p->flags |= PKT_STREAM_NOPCAPLOG;
}
}
StreamTcpMemuseCounter(tv, stt);
SCReturnInt(0);
error:
/* make sure we don't leave packets in our pseudo queue */
while (stt->pseudo_queue.len > 0) {
Packet *np = PacketDequeue(&stt->pseudo_queue);
if (np != NULL) {
PacketEnqueue(pq, np);
}
}
/* recalc the csum on the packet if it was modified */
if (p->flags & PKT_STREAM_MODIFIED) {
ReCalculateChecksum(p);
}
if (StreamTcpInlineMode()) {
p->action |= ACTION_DROP;
}
SCReturnInt(-1);
}
/**
* \brief Function to validate the checksum of the received packet. If the
* checksum is invalid, packet will be dropped, as the end system will
* also drop the packet.
*
* \param p Packet of which checksum has to be validated
* \retval 1 if the checksum is valid, otherwise 0
*/
static inline int StreamTcpValidateChecksum(Packet *p)
{
int ret = 1;
if (p->flags & PKT_IGNORE_CHECKSUM)
return ret;
if (p->tcpvars.comp_csum == -1) {
if (PKT_IS_IPV4(p)) {
p->tcpvars.comp_csum = TCPCalculateChecksum((uint16_t *)&(p->ip4h->ip_src),
(uint16_t *)p->tcph,
(p->payload_len +
TCP_GET_HLEN(p)));
} else if (PKT_IS_IPV6(p)) {
p->tcpvars.comp_csum = TCPV6CalculateChecksum((uint16_t *)&(p->ip6h->ip6_src),
(uint16_t *)p->tcph,
(p->payload_len +
TCP_GET_HLEN(p)));
}
}
if (p->tcpvars.comp_csum != p->tcph->th_sum) {
ret = 0;
SCLogDebug("Checksum of received packet %p is invalid",p);
if (p->livedev) {
SC_ATOMIC_ADD(p->livedev->invalid_checksums, 1);
}
}
return ret;
}
TmEcode StreamTcp (ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
{
StreamTcpThread *stt = (StreamTcpThread *)data;
TmEcode ret = TM_ECODE_OK;
if (!(PKT_IS_TCP(p)))
return TM_ECODE_OK;
if (p->flow == NULL) {
SCPerfCounterIncr(stt->counter_tcp_no_flow, tv->sc_perf_pca);
return TM_ECODE_OK;
}
if ((stream_config.flags & STREAMTCP_INIT_FLAG_CHECKSUM_VALIDATION) &&
(StreamTcpValidateChecksum(p) == 0))
{
SCPerfCounterIncr(stt->counter_tcp_invalid_checksum, tv->sc_perf_pca);
return TM_ECODE_OK;
}
PACKET_PROFILING_APP_RESET(&stt->ra_ctx->dp_ctx);
SCMutexLock(&p->flow->m);
ret = StreamTcpPacket(tv, p, stt, pq);
SCMutexUnlock(&p->flow->m);
//if (ret)
// return TM_ECODE_FAILED;
stt->pkts++;
return ret;
}
TmEcode StreamTcpThreadInit(ThreadVars *tv, void *initdata, void **data)
{
SCEnter();
StreamTcpThread *stt = SCMalloc(sizeof(StreamTcpThread));
if (stt == NULL)
SCReturnInt(TM_ECODE_FAILED);
memset(stt, 0, sizeof(StreamTcpThread));
*data = (void *)stt;
stt->counter_tcp_sessions = SCPerfTVRegisterCounter("tcp.sessions", tv,
SC_PERF_TYPE_UINT64,
"NULL");
stt->counter_tcp_ssn_memcap = SCPerfTVRegisterCounter("tcp.ssn_memcap_drop", tv,
SC_PERF_TYPE_UINT64,
"NULL");
stt->counter_tcp_pseudo = SCPerfTVRegisterCounter("tcp.pseudo", tv,
SC_PERF_TYPE_UINT64,
"NULL");
stt->counter_tcp_invalid_checksum = SCPerfTVRegisterCounter("tcp.invalid_checksum", tv,
SC_PERF_TYPE_UINT64,
"NULL");
stt->counter_tcp_no_flow = SCPerfTVRegisterCounter("tcp.no_flow", tv,
SC_PERF_TYPE_UINT64,
"NULL");
stt->counter_tcp_reused_ssn = SCPerfTVRegisterCounter("tcp.reused_ssn", tv,
SC_PERF_TYPE_UINT64,
"NULL");
stt->counter_tcp_memuse = SCPerfTVRegisterCounter("tcp.memuse", tv,
SC_PERF_TYPE_Q_NORMAL,
"NULL");
stt->counter_tcp_syn = SCPerfTVRegisterCounter("tcp.syn", tv,
SC_PERF_TYPE_UINT64,
"NULL");
stt->counter_tcp_synack = SCPerfTVRegisterCounter("tcp.synack", tv,
SC_PERF_TYPE_UINT64,
"NULL");
stt->counter_tcp_rst = SCPerfTVRegisterCounter("tcp.rst", tv,
SC_PERF_TYPE_UINT64,
"NULL");
/* init reassembly ctx */
stt->ra_ctx = StreamTcpReassembleInitThreadCtx();
if (stt->ra_ctx == NULL)
SCReturnInt(TM_ECODE_FAILED);
stt->ra_ctx->counter_tcp_segment_memcap = SCPerfTVRegisterCounter("tcp.segment_memcap_drop", tv,
SC_PERF_TYPE_UINT64,
"NULL");
stt->ra_ctx->counter_tcp_stream_depth = SCPerfTVRegisterCounter("tcp.stream_depth_reached", tv,
SC_PERF_TYPE_UINT64,
"NULL");
stt->ra_ctx->counter_tcp_reass_memuse = SCPerfTVRegisterCounter("tcp.reassembly_memuse", tv,
SC_PERF_TYPE_Q_NORMAL,
"NULL");
stt->ra_ctx->counter_tcp_reass_gap = SCPerfTVRegisterCounter("tcp.reassembly_gap", tv,
SC_PERF_TYPE_UINT64,
"NULL");
tv->sc_perf_pca = SCPerfGetAllCountersArray(&tv->sc_perf_pctx);
SCPerfAddToClubbedTMTable(tv->name, &tv->sc_perf_pctx);
SCLogDebug("StreamTcp thread specific ctx online at %p, reassembly ctx %p",
stt, stt->ra_ctx);
SCReturnInt(TM_ECODE_OK);
}
TmEcode StreamTcpThreadDeinit(ThreadVars *tv, void *data)
{
SCEnter();
StreamTcpThread *stt = (StreamTcpThread *)data;
if (stt == NULL) {
return TM_ECODE_OK;
}
/* XXX */
/* free reassembly ctx */
StreamTcpReassembleFreeThreadCtx(stt->ra_ctx);
/* clear memory */
memset(stt, 0, sizeof(StreamTcpThread));
SCFree(stt);
SCReturnInt(TM_ECODE_OK);
}
void StreamTcpExitPrintStats(ThreadVars *tv, void *data)
{
StreamTcpThread *stt = (StreamTcpThread *)data;
if (stt == NULL) {
return;
}
SCLogInfo("Stream TCP processed %" PRIu64 " TCP packets", 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
*
* \retval 0 unacceptable RST
* \retval 1 acceptable RST
*
* WebSense sends RST packets that are:
* - RST flag, win 0, ack 0, seq = nextseq
*
*/
static int StreamTcpValidateRst(TcpSession *ssn, Packet *p)
{
uint8_t os_policy;
if (ssn->flags & STREAMTCP_FLAG_TIMESTAMP) {
if (!StreamTcpValidateTimestamp(ssn, p)) {
SCReturnInt(0);
}
}
/* Set up the os_policy to be used in validating the RST packets based on
target system */
if (PKT_IS_TOSERVER(p)) {
if (ssn->server.os_policy == 0)
StreamTcpSetOSPolicy(&ssn->server, p);
os_policy = ssn->server.os_policy;
if (TCP_GET_ACK(p) && StreamTcpValidateAck(&ssn->server, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_RST_INVALID_ACK);
SCReturnInt(0);
}
} else {
if (ssn->client.os_policy == 0)
StreamTcpSetOSPolicy(&ssn->client, p);
os_policy = ssn->client.os_policy;
if (TCP_GET_ACK(p) && StreamTcpValidateAck(&ssn->client, p) == -1) {
SCLogDebug("ssn %p: rejecting because of invalid ack value", ssn);
StreamTcpSetEvent(p, STREAM_RST_INVALID_ACK);
SCReturnInt(0);
}
}
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 state The FLOW_STATE_ depends upon the TCP sesison state, default is
* FLOW_STATE_CLOSED
*/
int StreamTcpGetFlowState(void *s)
{
SCEnter();
TcpSession *ssn = (TcpSession *)s;
if (ssn == NULL) {
SCReturnInt(FLOW_STATE_CLOSED);
}
switch(ssn->state) {
case TCP_NONE:
case TCP_SYN_SENT:
case TCP_SYN_RECV:
case TCP_LISTEN:
SCReturnInt(FLOW_STATE_NEW);
case TCP_ESTABLISHED:
case TCP_FIN_WAIT1:
case TCP_FIN_WAIT2:
case TCP_CLOSING:
case TCP_CLOSE_WAIT:
SCReturnInt(FLOW_STATE_ESTABLISHED);
case TCP_LAST_ACK:
case TCP_TIME_WAIT:
case TCP_CLOSED:
SCReturnInt(FLOW_STATE_CLOSED);
}
SCReturnInt(FLOW_STATE_CLOSED);
}
/**
* \brief Function to check the validity of the received timestamp based on
* the target OS of the given stream.
*
* It's passive except for:
* 1. it sets the os policy on the stream if necessary
* 2. it sets an event in the packet if necessary
*
* \param ssn TCP session to which the given packet belongs
* \param p Packet which has to be checked for its validity
*
* \retval 1 if the timestamp is valid
* \retval 0 if the timestamp is invalid
*/
static int StreamTcpValidateTimestamp (TcpSession *ssn, Packet *p)
{
SCEnter();
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;
}
/* Set up the os_policy to be used in validating the timestamps based on
the target system */
if (receiver_stream->os_policy == 0) {
StreamTcpSetOSPolicy(receiver_stream, p);
}
if (p->tcpvars.ts != NULL) {
uint32_t ts = TCP_GET_TSVAL(p);
uint32_t last_pkt_ts = sender_stream->last_pkt_ts;
uint32_t last_ts = sender_stream->last_ts;
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. */
check_ts = 0;
break;
case OS_POLICY_OLD_LINUX:
case OS_POLICY_WINDOWS:
case OS_POLICY_VISTA:
if (SEQ_EQ(sender_stream->next_seq, TCP_GET_SEQ(p))) {
last_ts = ts;
check_ts = 0; /*next packet will be checked for validity
and stream TS has been updated with this
one.*/
}
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*/
goto invalid;
}
}
if (check_ts) {
int32_t result = 0;
SCLogDebug("ts %"PRIu32", last_ts %"PRIu32"", ts, last_ts);
if (receiver_stream->os_policy == OS_POLICY_LINUX) {
/* Linux accepts TS which are off by one.*/
result = (int32_t) ((ts - last_ts) + 1);
} else {
result = (int32_t) (ts - last_ts);
}
SCLogDebug("result %"PRIi32", p->ts.tv_sec %"PRIuMAX"", result, (uintmax_t)p->ts.tv_sec);
if (last_pkt_ts == 0 &&
(ssn->flags & STREAMTCP_FLAG_MIDSTREAM))
{
last_pkt_ts = p->ts.tv_sec;
}
if (result < 0) {
SCLogDebug("timestamp is not valid last_ts "
"%" PRIu32 " p->tcpvars->ts %" PRIu32 " result "
"%" PRId32 "", last_ts, ts, result);
/* candidate for rejection */
ret = 0;
} else if ((sender_stream->last_ts != 0) &&
(((uint32_t) p->ts.tv_sec) >
last_pkt_ts + PAWS_24DAYS))
{
SCLogDebug("packet is not valid last_pkt_ts "
"%" PRIu32 " p->ts.tv_sec %" PRIu32 "",
last_pkt_ts, (uint32_t) p->ts.tv_sec);
/* candidate for rejection */
ret = 0;
}
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 > (last_pkt_ts + PAWS_24DAYS))))
{
SCLogDebug("timestamp considered valid anyway");
} else {
goto invalid;
}
}
}
}
SCReturnInt(1);
invalid:
StreamTcpSetEvent(p, STREAM_PKT_INVALID_TIMESTAMP);
SCReturnInt(0);
}
/**
* \brief Function to check the validity of the received timestamp based on
* the target OS of the given stream and update the session.
*
* \param ssn TCP session to which the given packet belongs
* \param p Packet which has to be checked for its validity
*
* \retval 1 if the timestamp is valid
* \retval 0 if the timestamp is invalid
*/
static int StreamTcpHandleTimestamp (TcpSession *ssn, Packet *p)
{
SCEnter();
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;
}
/* Set up the os_policy to be used in validating the timestamps based on
the target system */
if (receiver_stream->os_policy == 0) {
StreamTcpSetOSPolicy(receiver_stream, p);
}
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*/
goto invalid;
}
}
if (check_ts) {
int32_t result = 0;
SCLogDebug("ts %"PRIu32", last_ts %"PRIu32"", ts, sender_stream->last_ts);
if (receiver_stream->os_policy == OS_POLICY_LINUX) {
/* Linux accepts TS which are off by one.*/
result = (int32_t) ((ts - sender_stream->last_ts) + 1);
} else {
result = (int32_t) (ts - sender_stream->last_ts);
}
SCLogDebug("result %"PRIi32", p->ts.tv_sec %"PRIuMAX"", result, (uintmax_t)p->ts.tv_sec);
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);
/* candidate for rejection */
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);
/* candidate for rejection */
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;
} else 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;
SCLogDebug("timestamp considered valid anyway");
} else {
goto invalid;
}
}
}
} 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;
}
SCReturnInt(1);
invalid:
StreamTcpSetEvent(p, STREAM_PKT_INVALID_TIMESTAMP);
SCReturnInt(0);
}
/**
* \brief Function to test the received ACK values against the stream window
* and previous ack value. ACK values should be higher than previous
* ACK value and less than the next_win value.
*
* \param stream TcpStream of which last_ack needs to be tested
* \param p Packet which is used to test the last_ack
*
* \retval 0 ACK is valid, last_ack is updated if ACK was higher
* \retval -1 ACK is invalid
*/
static inline int StreamTcpValidateAck(TcpStream *stream, Packet *p)
{
SCEnter();
uint32_t ack = TCP_GET_ACK(p);
/* fast track */
if (SEQ_GT(ack, stream->last_ack) && SEQ_LEQ(ack, stream->next_win))
{
SCLogDebug("ACK in bounds");
SCReturnInt(0);
}
/* fast track */
else if (SEQ_EQ(ack, stream->last_ack)) {
SCLogDebug("pkt ACK %"PRIu32" == stream last ACK %"PRIu32, TCP_GET_ACK(p), stream->last_ack);
SCReturnInt(0);
}
/* exception handling */
if (SEQ_LT(ack, stream->last_ack)) {
SCLogDebug("pkt ACK %"PRIu32" < stream last ACK %"PRIu32, TCP_GET_ACK(p), stream->last_ack);
/* This is an attempt to get a 'left edge' value that we can check against.
* It doesn't work when the window is 0, need to think of a better way. */
if (stream->window != 0 && SEQ_LT(ack, (stream->last_ack - stream->window))) {
SCLogDebug("ACK %"PRIu32" is before last_ack %"PRIu32" - window "
"%"PRIu32" = %"PRIu32, ack, stream->last_ack,
stream->window, stream->last_ack - stream->window);
goto invalid;
}
SCReturnInt(0);
}
if (SEQ_GT(ack, stream->next_win)) {
SCLogDebug("ACK %"PRIu32" is after next_win %"PRIu32, ack, stream->next_win);
goto invalid;
}
SCLogDebug("default path leading to invalid: ACK %"PRIu32", last_ack %"PRIu32
" next_win %"PRIu32, ack, stream->last_ack, stream->next_win);
invalid:
StreamTcpSetEvent(p, STREAM_PKT_INVALID_ACK);
SCReturnInt(-1);
}
/** \brief Set the No reassembly flag for the given direction in given TCP
* session.
*
* \param ssn TCP Session to set the flag in
* \param direction direction to set the flag in: 0 toserver, 1 toclient
*/
void StreamTcpSetSessionNoReassemblyFlag (TcpSession *ssn, char direction)
{
direction ? (ssn->server.flags |= STREAMTCP_STREAM_FLAG_NOREASSEMBLY) :
(ssn->client.flags |= STREAMTCP_STREAM_FLAG_NOREASSEMBLY);
}
#define PSEUDO_PKT_SET_IPV4HDR(nipv4h,ipv4h) do { \
IPV4_SET_RAW_VER(nipv4h, IPV4_GET_RAW_VER(ipv4h)); \
IPV4_SET_RAW_HLEN(nipv4h, IPV4_GET_RAW_HLEN(ipv4h)); \
IPV4_SET_RAW_IPLEN(nipv4h, IPV4_GET_RAW_IPLEN(ipv4h)); \
IPV4_SET_RAW_IPTOS(nipv4h, IPV4_GET_RAW_IPTOS(ipv4h)); \
IPV4_SET_RAW_IPPROTO(nipv4h, IPV4_GET_RAW_IPPROTO(ipv4h)); \
(nipv4h)->ip_src = IPV4_GET_RAW_IPDST(ipv4h); \
(nipv4h)->ip_dst = IPV4_GET_RAW_IPSRC(ipv4h); \
} while (0)
#define PSEUDO_PKT_SET_IPV6HDR(nipv6h,ipv6h) do { \
(nipv6h)->ip6_src[0] = (ipv6h)->ip6_dst[0]; \
(nipv6h)->ip6_src[1] = (ipv6h)->ip6_dst[1]; \
(nipv6h)->ip6_src[2] = (ipv6h)->ip6_dst[2]; \
(nipv6h)->ip6_src[3] = (ipv6h)->ip6_dst[3]; \
(nipv6h)->ip6_dst[0] = (ipv6h)->ip6_src[0]; \
(nipv6h)->ip6_dst[1] = (ipv6h)->ip6_src[1]; \
(nipv6h)->ip6_dst[2] = (ipv6h)->ip6_src[2]; \
(nipv6h)->ip6_dst[3] = (ipv6h)->ip6_src[3]; \
IPV6_SET_RAW_NH(nipv6h, IPV6_GET_RAW_NH(ipv6h)); \
} while (0)
#define PSEUDO_PKT_SET_TCPHDR(ntcph,tcph) do { \
COPY_PORT((tcph)->th_dport, (ntcph)->th_sport); \
COPY_PORT((tcph)->th_sport, (ntcph)->th_dport); \
(ntcph)->th_seq = (tcph)->th_ack; \
(ntcph)->th_ack = (tcph)->th_seq; \
} while (0)
/**
* \brief Function to fetch a packet from the packet allocation queue for
* creation of the pseudo packet from the reassembled stream.
*
* @param parent Pointer to the parent of the pseudo packet
* @param pkt pointer to the raw packet of the parent
* @param len length of the packet
* @return upon success returns the pointer to the new pseudo packet
* otherwise NULL
*/
Packet *StreamTcpPseudoSetup(Packet *parent, uint8_t *pkt, uint32_t len)
{
SCEnter();
Packet *p = PacketGetFromQueueOrAlloc();
if (p == NULL || len == 0) {
SCReturnPtr(NULL, "Packet");
}
/* set the root ptr to the lowest layer */
if (parent->root != NULL)
p->root = parent->root;
else
p->root = parent;
/* copy packet and set lenght, proto */
p->proto = parent->proto;
p->datalink = parent->datalink;
PacketCopyData(p, pkt, len);
p->recursion_level = parent->recursion_level + 1;
p->ts.tv_sec = parent->ts.tv_sec;
p->ts.tv_usec = parent->ts.tv_usec;
p->flow = parent->flow;
FlowIncrUsecnt(p->flow);
/* set tunnel flags */
/* tell new packet it's part of a tunnel */
SET_TUNNEL_PKT(p);
/* tell parent packet it's part of a tunnel */
SET_TUNNEL_PKT(parent);
/* increment tunnel packet refcnt in the root packet */
TUNNEL_INCR_PKT_TPR(p);
return p;
}
/**
* \brief Function to setup the IP and TCP header of the pseudo packet from
* the newly copied raw packet contents of the parent.
*
* @param np pointer to the pseudo packet
* @param p pointer to the original packet
*/
void StreamTcpPseudoPacketSetupHeader(Packet *np, Packet *p)
{
/* Setup the IP header */
if (PKT_IS_IPV4(p)) {
np->ip4h = (IPV4Hdr *)((uint8_t *)GET_PKT_DATA(np) + (GET_PKT_LEN(np) - IPV4_GET_IPLEN(p)));
PSEUDO_PKT_SET_IPV4HDR(np->ip4h, p->ip4h);
/* Similarly setup the TCP header with ports in opposite direction */
np->tcph = (TCPHdr *)((uint8_t *)np->ip4h + IPV4_GET_HLEN(np));
PSEUDO_PKT_SET_TCPHDR(np->tcph, p->tcph);
/* Setup the adress and port details */
SET_IPV4_SRC_ADDR(p, &np->dst);
SET_IPV4_DST_ADDR(p, &np->src);
SET_TCP_SRC_PORT(p, &np->dp);
SET_TCP_DST_PORT(p, &np->sp);
} else if (PKT_IS_IPV6(p)) {
np->ip6h = (IPV6Hdr *)((uint8_t *)GET_PKT_DATA(np) + (GET_PKT_LEN(np) - IPV6_GET_PLEN(p) - IPV6_HEADER_LEN));
PSEUDO_PKT_SET_IPV6HDR(np->ip6h, p->ip6h);
/* Similarly setup the TCP header with ports in opposite direction */
np->tcph = (TCPHdr *)((uint8_t *)np->ip6h + IPV6_HEADER_LEN);
PSEUDO_PKT_SET_TCPHDR(np->tcph, p->tcph);
/* Setup the adress and port details */
SET_IPV6_SRC_ADDR(p, &np->src);
SET_IPV6_DST_ADDR(p, &np->dst);
SET_TCP_SRC_PORT(p, &np->sp);
SET_TCP_DST_PORT(p, &np->dp);
}
/* we don't need a payload (if any) */
np->payload = NULL;
np->payload_len = 0;
}
/** \brief Create a pseudo packet injected into the engine to signal the
* opposing direction of this stream to wrap up stream reassembly.
*
* \param p real packet
* \param pq packet queue to store the new pseudo packet in
*/
void StreamTcpPseudoPacketCreateStreamEndPacket(Packet *p, TcpSession *ssn, PacketQueue *pq)
{
SCEnter();
if (p->flags & PKT_PSEUDO_STREAM_END) {
SCReturn;
}
/* no need for a pseudo packet if there is nothing left to reassemble */
if (ssn->server.seg_list == NULL && ssn->client.seg_list == NULL) {
SCReturn;
}
Packet *np = StreamTcpPseudoSetup(p, GET_PKT_DATA(p), GET_PKT_LEN(p));
if (np == NULL) {
SCLogDebug("The packet received from packet allocation is NULL");
SCReturn;
}
/* Setup the IP and TCP headers */
StreamTcpPseudoPacketSetupHeader(np,p);
np->flowflags = p->flowflags;
np->flags |= PKT_STREAM_EST;
np->flags |= PKT_STREAM_EOF;
np->flags |= PKT_HAS_FLOW;
np->flags |= PKT_PSEUDO_STREAM_END;
if (PKT_IS_TOSERVER(p)) {
SCLogDebug("original is to_server, so pseudo is to_client");
np->flowflags &= ~FLOW_PKT_TOSERVER;
np->flowflags |= FLOW_PKT_TOCLIENT;
#ifdef DEBUG
BUG_ON(!(PKT_IS_TOCLIENT(np)));
BUG_ON((PKT_IS_TOSERVER(np)));
#endif
} else if (PKT_IS_TOCLIENT(p)) {
SCLogDebug("original is to_client, so pseudo is to_server");
np->flowflags &= ~FLOW_PKT_TOCLIENT;
np->flowflags |= FLOW_PKT_TOSERVER;
#ifdef DEBUG
BUG_ON(!(PKT_IS_TOSERVER(np)));
BUG_ON((PKT_IS_TOCLIENT(np)));
#endif
}
PacketEnqueue(pq, np);
SCReturn;
}
/**
* \brief Run callback function on each TCP segment
*
* This function is used by StreamMsgForEach() which
* should be used directly.
*
* \return -1 in case of error, the number of segment in case of success
*
*/
int StreamTcpSegmentForEach(Packet *p, uint8_t flag, StreamSegmentCallback CallbackFunc, void *data)
{
TcpSession *ssn = NULL;
TcpStream *stream = NULL;
int ret = 0;
int cnt = 0;
if (p->flow == NULL)
return 0;
SCMutexLock(&p->flow->m);
ssn = (TcpSession *)p->flow->protoctx;
if (ssn == NULL) {
SCMutexUnlock(&p->flow->m);
return 0;
}
if (flag & FLOW_PKT_TOSERVER) {
stream = &(ssn->server);
} else {
stream = &(ssn->client);
}
TcpSegment *seg = stream->seg_list;
for (; seg != NULL && SEQ_LT(seg->seq, stream->last_ack);) {
ret = CallbackFunc(p, data, seg->payload, seg->payload_len);
if (ret != 1) {
SCLogInfo("Callback function has failed");
SCMutexUnlock(&p->flow->m);
return -1;
}
seg = seg->next;
cnt++;
}
SCMutexUnlock(&p->flow->m);
return cnt;
}
#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 = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
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: ");
goto end;
}
f.protoctx = ssn;
if (f.alparser != NULL) {
printf("AppLayer field not set to NULL: ");
goto end;
}
if (ssn->state != 0) {
printf("TCP state field not set to 0: ");
goto end;
}
StreamTcpSessionClear(p->flow->protoctx);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
SCFree(p);
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 = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
uint8_t payload[4];
TCPHdr tcph;
TcpReassemblyThreadCtx ra_ctx;
StreamMsgQueue stream_q;
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
memset(&stream_q, 0, sizeof(StreamMsgQueue));
memset(&ra_ctx, 0, sizeof(TcpReassemblyThreadCtx));
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
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;
ra_ctx.stream_q = &stream_q;
stt.ra_ctx = &ra_ctx;
StreamTcpInitConfig(TRUE);
if (StreamTcpPacket(&tv, p, &stt, &pq) == -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, &pq) == -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, &pq) == -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, 4); /*AAA*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->flowflags = FLOW_PKT_TOCLIENT;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -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, 4); /*BBB*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->flowflags = FLOW_PKT_TOCLIENT;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
StreamTcpSessionClear(p->flow->protoctx);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
SCFree(p);
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 = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
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, &pq) == -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, &pq) == -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, &pq) == -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;
StreamTcpSessionClear(p->flow->protoctx);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
SCFree(p);
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 = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
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, &pq) == -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, &pq) == -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;
StreamTcpSessionClear(p->flow->protoctx);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
SCFree(p);
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 = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
uint8_t payload[4];
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
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 */
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, 4); /*AAA*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -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, 4); /*BBB*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -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, 4); /*CCC*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -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, 4); /*DDD*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -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;
StreamTcpSessionClear(p->flow->protoctx);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
SCFree(p);
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 = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
TcpSession ssn;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
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, &pq) == -1)
goto end;
if (((TcpSession *)(p->flow->protoctx)) != NULL)
goto end;
p->tcph->th_flags = TH_RST;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
if (((TcpSession *)(p->flow->protoctx)) != NULL)
goto end;
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
SCFree(p);
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 = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
uint8_t payload[1] = {0x42};
TCPVars tcpvars;
TCPOpt ts;
uint32_t data[2];
PacketQueue pq;
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
memset(&pq,0,sizeof(PacketQueue));
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);
stream_config.midstream = TRUE;
/* prevent L7 from kicking in */
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, &pq) == -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->tcpvars.ts->data = (uint8_t *)data;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -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;
}
StreamTcpSessionClear(p->flow->protoctx);
ret = 1;
}
end:
StreamTcpFreeConfig(TRUE);
SCFree(p);
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 = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
uint8_t payload[1] = {0x42};
TCPVars tcpvars;
TCPOpt ts;
uint32_t data[2];
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
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);
stream_config.midstream = TRUE;
/* prevent L7 from kicking in */
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, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(11);
p->tcph->th_ack = htonl(20);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOSERVER;
data[0] = htonl(12);
p->tcpvars.ts->data = (uint8_t *)data;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -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;
}
StreamTcpSessionClear(p->flow->protoctx);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
SCFree(p);
return ret;
}
/**
* \test Test the working of No stream reassembly flag. The stream will not
* reassemble the segment if the flag is set.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest09 (void) {
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
uint8_t payload[1] = {0x42};
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
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);
stream_config.midstream = TRUE;
//prevent L7 from kicking in
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, &pq) == -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;
StreamTcpSetSessionNoReassemblyFlag(((TcpSession *)(p->flow->protoctx)), 0);
if (StreamTcpPacket(&tv, p, &stt, &pq) == -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, &pq) == -1)
goto end;
if (((TcpSession *) (p->flow->protoctx))->client.seg_list->next == NULL)
ret = 1;
StreamTcpSessionClear(p->flow->protoctx);
end:
StreamTcpFreeConfig(TRUE);
SCFree(p);
return ret;
}
/**
* \test Test the setting up a TCP session when we are seeing asynchronous
* stream, while we see all the packets in that stream from start.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest10 (void) {
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
uint8_t payload[4];
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
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(11);
tcph.th_flags = TH_SYN;
p->tcph = &tcph;
int ret = 0;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(11);
p->tcph->th_ack = htonl(11);
p->tcph->th_flags = TH_ACK;
p->flowflags = FLOW_PKT_TOSERVER;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(11);
p->tcph->th_ack = htonl(11);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(6);
p->tcph->th_ack = htonl(11);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
if (stream_config.async_oneside != TRUE) {
ret = 1;
goto end;
}
if (((TcpSession *)(p->flow->protoctx))->state != TCP_ESTABLISHED) {
printf("failed in setting state\n");
goto end;
}
if (! (((TcpSession *)(p->flow->protoctx))->flags & STREAMTCP_FLAG_ASYNC)) {
printf("failed in setting asynchronous session\n");
goto end;
}
if (((TcpSession *)(p->flow->protoctx))->client.last_ack != 6 &&
((TcpSession *)(p->flow->protoctx))->server.next_seq != 11) {
printf("failed in seq %"PRIu32" match\n",
((TcpSession *)(p->flow->protoctx))->client.last_ack);
goto end;
}
StreamTcpSessionClear(p->flow->protoctx);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
SCFree(p);
return ret;
}
/**
* \test Test the setting up a TCP session when we are seeing asynchronous
* stream, while we missed the SYN packet of that stream.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest11 (void) {
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
uint8_t payload[4];
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
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(1);
tcph.th_flags = TH_SYN|TH_ACK;
p->tcph = &tcph;
int ret = 0;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(11);
p->tcph->th_ack = htonl(1);
p->tcph->th_flags = TH_ACK;
p->flowflags = FLOW_PKT_TOSERVER;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(11);
p->tcph->th_ack = htonl(1);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(2);
p->tcph->th_ack = htonl(1);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
if (stream_config.async_oneside != TRUE) {
ret = 1;
goto end;
}
if (! (((TcpSession *)(p->flow->protoctx))->flags & STREAMTCP_FLAG_ASYNC)) {
printf("failed in setting asynchronous session\n");
goto end;
}
if (((TcpSession *)(p->flow->protoctx))->state != TCP_ESTABLISHED) {
printf("failed in setting state\n");
goto end;
}
if (((TcpSession *)(p->flow->protoctx))->server.last_ack != 2 &&
((TcpSession *)(p->flow->protoctx))->client.next_seq != 1) {
printf("failed in seq %"PRIu32" match\n",
((TcpSession *)(p->flow->protoctx))->server.last_ack);
goto end;
}
StreamTcpSessionClear(p->flow->protoctx);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
SCFree(p);
return ret;
}
/**
* \test Test the setting up a TCP session when we are seeing asynchronous
* stream, while we missed the SYN and SYN/ACK packets in that stream.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest12 (void) {
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
uint8_t payload[4];
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
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(11);
tcph.th_flags = TH_ACK;
p->tcph = &tcph;
int ret = 0;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(10);
p->tcph->th_ack = htonl(11);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(6);
p->tcph->th_ack = htonl(11);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
if (stream_config.async_oneside != TRUE) {
ret = 1;
goto end;
}
if (! (((TcpSession *)(p->flow->protoctx))->flags & STREAMTCP_FLAG_ASYNC)) {
printf("failed in setting asynchronous session\n");
goto end;
}
if (((TcpSession *)(p->flow->protoctx))->state != TCP_ESTABLISHED) {
printf("failed in setting state\n");
goto end;
}
if (((TcpSession *)(p->flow->protoctx))->client.last_ack != 6 &&
((TcpSession *)(p->flow->protoctx))->server.next_seq != 11) {
printf("failed in seq %"PRIu32" match\n",
((TcpSession *)(p->flow->protoctx))->client.last_ack);
goto end;
}
StreamTcpSessionClear(p->flow->protoctx);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
SCFree(p);
return ret;
}
/**
* \test Test the setting up a TCP session when we are seeing asynchronous
* stream, while we missed the SYN and SYN/ACK packets in that stream.
* Later, we start to receive the packet from other end stream too.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest13 (void) {
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
uint8_t payload[4];
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
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(11);
tcph.th_flags = TH_ACK;
p->tcph = &tcph;
int ret = 0;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(10);
p->tcph->th_ack = htonl(11);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(6);
p->tcph->th_ack = htonl(11);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
if (stream_config.async_oneside != TRUE) {
ret = 1;
goto end;
}
if (! (((TcpSession *)(p->flow->protoctx))->flags & STREAMTCP_FLAG_ASYNC)) {
printf("failed in setting asynchronous session\n");
goto end;
}
if (((TcpSession *)(p->flow->protoctx))->state != TCP_ESTABLISHED) {
printf("failed in setting state\n");
goto end;
}
p->tcph->th_seq = htonl(11);
p->tcph->th_ack = htonl(9);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOCLIENT;
StreamTcpCreateTestPacket(payload, 0x42, 3, 4); /*BBB*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
if (((TcpSession *)(p->flow->protoctx))->client.last_ack != 9 &&
((TcpSession *)(p->flow->protoctx))->server.next_seq != 14) {
printf("failed in seq %"PRIu32" match\n",
((TcpSession *)(p->flow->protoctx))->client.last_ack);
goto end;
}
StreamTcpSessionPktFree(p);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
SCFree(p);
return ret;
}
/* Dummy conf string to setup the OS policy for unit testing */
static const char *dummy_conf_string =
"%YAML 1.1\n"
"---\n"
"\n"
"default-log-dir: /var/log/eidps\n"
"\n"
"logging:\n"
"\n"
" default-log-level: debug\n"
"\n"
" default-format: \"<%t> - <%l>\"\n"
"\n"
" default-startup-message: Your IDS has started.\n"
"\n"
" default-output-filter:\n"
"\n"
"host-os-policy:\n"
"\n"
" windows: 192.168.0.1\n"
"\n"
" linux: 192.168.0.2\n"
"\n";
/* Dummy conf string to setup the OS policy for unit testing */
static const char *dummy_conf_string1 =
"%YAML 1.1\n"
"---\n"
"\n"
"default-log-dir: /var/log/eidps\n"
"\n"
"logging:\n"
"\n"
" default-log-level: debug\n"
"\n"
" default-format: \"<%t> - <%l>\"\n"
"\n"
" default-startup-message: Your IDS has started.\n"
"\n"
" default-output-filter:\n"
"\n"
"host-os-policy:\n"
"\n"
" windows: 192.168.0.0/24," "192.168.1.1\n"
"\n"
" linux: 192.168.1.0/24," "192.168.0.1\n"
"\n";
/**
* \brief Function to parse the dummy conf string and get the value of IP
* address for the corresponding OS policy type.
*
* \param conf_val_name Name of the OS policy type
* \retval returns IP address as string on success and NULL on failure
*/
char *StreamTcpParseOSPolicy (char *conf_var_name)
{
SCEnter();
char conf_var_type_name[15] = "host-os-policy";
char *conf_var_full_name = NULL;
char *conf_var_value = NULL;
if (conf_var_name == NULL)
goto end;
/* the + 2 is for the '.' and the string termination character '\0' */
conf_var_full_name = (char *)SCMalloc(strlen(conf_var_type_name) +
strlen(conf_var_name) + 2);
if (conf_var_full_name == NULL)
goto end;
if (snprintf(conf_var_full_name,
strlen(conf_var_type_name) + strlen(conf_var_name) + 2, "%s.%s",
conf_var_type_name, conf_var_name) < 0) {
SCLogError(SC_LOG_ERROR, "Error in making the conf full name");
goto end;
}
if (ConfGet(conf_var_full_name, &conf_var_value) != 1) {
SCLogError(SC_LOG_ERROR, "Error in getting conf value for conf name %s",
conf_var_full_name);
goto end;
}
SCLogDebug("Value obtained from the yaml conf file, for the var "
"\"%s\" is \"%s\"", conf_var_name, conf_var_value);
end:
if (conf_var_full_name != NULL)
SCFree(conf_var_full_name);
SCReturnCharPtr(conf_var_value);
}
/**
* \test Test the setting up a OS policy. Te OS policy values are defined in
* the config string "dummy_conf_string"
*
* \retval On success it returns 1 and on failure 0
*/
static int StreamTcpTest14 (void) {
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
uint8_t payload[4];
struct in_addr addr;
IPV4Hdr ipv4h;
char os_policy_name[10] = "windows";
char *ip_addr;
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof (StreamTcpThread));
memset(&tcph, 0, sizeof (TCPHdr));
memset(&addr, 0, sizeof(addr));
memset(&ipv4h, 0, sizeof(ipv4h));
p->flow = &f;
int ret = 0;
StreamTcpInitConfig(TRUE);
/* Load the config string in to parser */
ConfCreateContextBackup();
ConfInit();
ConfYamlLoadString(dummy_conf_string, strlen(dummy_conf_string));
/* Get the IP address as string and add it to Host info tree for lookups */
ip_addr = StreamTcpParseOSPolicy(os_policy_name);
SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
strlcpy(os_policy_name, "linux\0", sizeof(os_policy_name));
ip_addr = StreamTcpParseOSPolicy(os_policy_name);
SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
/* prevent L7 from kicking in */
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
addr.s_addr = inet_addr("192.168.0.1");
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->dst.family = AF_INET;
p->dst.address.address_un_data32[0] = addr.s_addr;
p->ip4h = &ipv4h;
StreamTcpCreateTestPacket(payload, 0x41, 3, sizeof(payload)); /*AAA*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -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, sizeof(payload)); /*BBB*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(15);
p->tcph->th_ack = htonl(23);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x43, 3, sizeof(payload)); /*CCC*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(14);
p->tcph->th_ack = htonl(23);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x43, 3, sizeof(payload)); /*CCC*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
addr.s_addr = inet_addr("192.168.0.2");
p->tcph->th_seq = htonl(25);
p->tcph->th_ack = htonl(13);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOCLIENT;
p->dst.address.address_un_data32[0] = addr.s_addr;
StreamTcpCreateTestPacket(payload, 0x44, 3, sizeof(payload)); /*DDD*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(24);
p->tcph->th_ack = htonl(13);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOCLIENT;
StreamTcpCreateTestPacket(payload, 0x44, 3, sizeof(payload)); /*DDD*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -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 != 13 &&
((TcpSession *)(p->flow->protoctx))->server.next_seq != 23) {
printf("failed in next_seq match client.next_seq %"PRIu32""
" server.next_seq %"PRIu32"\n",
((TcpSession *)(p->flow->protoctx))->client.next_seq,
((TcpSession *)(p->flow->protoctx))->server.next_seq);
goto end;
}
if (((TcpSession *)(p->flow->protoctx))->client.os_policy !=
OS_POLICY_WINDOWS && ((TcpSession *)
(p->flow->protoctx))->server.os_policy != OS_POLICY_LINUX)
{
printf("failed in setting up OS policy, client.os_policy: %"PRIu8""
" should be %"PRIu8" and server.os_policy: %"PRIu8""
" should be %"PRIu8"\n", ((TcpSession *)
(p->flow->protoctx))->client.os_policy, OS_POLICY_WINDOWS,
((TcpSession *)(p->flow->protoctx))->server.os_policy,
OS_POLICY_LINUX);
goto end;
}
StreamTcpSessionClear(p->flow->protoctx);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
ConfDeInit();
ConfRestoreContextBackup();
SCFree(p);
return ret;
}
/**
* \test Test the setting up a TCP session using the 4WHS:
* SYN, SYN, SYN/ACK, ACK
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcp4WHSTest01 (void) {
int ret = 0;
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
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 = 0;
tcph.th_flags = TH_SYN;
p->tcph = &tcph;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(20);
p->tcph->th_ack = 0;
p->tcph->th_flags = TH_SYN;
p->flowflags = FLOW_PKT_TOCLIENT;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
if ((!(((TcpSession *)(p->flow->protoctx))->flags & STREAMTCP_FLAG_4WHS))) {
printf("STREAMTCP_FLAG_4WHS flag not set: ");
goto end;
}
p->tcph->th_seq = htonl(10);
p->tcph->th_ack = htonl(21); /* the SYN/ACK uses the SEQ from the first SYN pkt */
p->tcph->th_flags = TH_SYN|TH_ACK;
p->flowflags = FLOW_PKT_TOSERVER;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(21);
p->tcph->th_ack = htonl(10);
p->tcph->th_flags = TH_ACK;
p->flowflags = FLOW_PKT_TOCLIENT;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
if (((TcpSession *)(p->flow->protoctx))->state != TCP_ESTABLISHED) {
printf("state is not ESTABLISHED: ");
goto end;
}
ret = 1;
end:
StreamTcpSessionClear(p->flow->protoctx);
StreamTcpFreeConfig(TRUE);
SCFree(p);
return ret;
}
/**
* \test set up a TCP session using the 4WHS:
* SYN, SYN, SYN/ACK, ACK, but the SYN/ACK does
* not have the right SEQ
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcp4WHSTest02 (void) {
int ret = 0;
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
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 = 0;
tcph.th_flags = TH_SYN;
p->tcph = &tcph;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(20);
p->tcph->th_ack = 0;
p->tcph->th_flags = TH_SYN;
p->flowflags = FLOW_PKT_TOCLIENT;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
if ((!(((TcpSession *)(p->flow->protoctx))->flags & STREAMTCP_FLAG_4WHS))) {
printf("STREAMTCP_FLAG_4WHS flag not set: ");
goto end;
}
p->tcph->th_seq = htonl(30);
p->tcph->th_ack = htonl(21); /* the SYN/ACK uses the SEQ from the first SYN pkt */
p->tcph->th_flags = TH_SYN|TH_ACK;
p->flowflags = FLOW_PKT_TOSERVER;
if (StreamTcpPacket(&tv, p, &stt, &pq) != -1) {
printf("SYN/ACK pkt not rejected but it should have: ");
goto end;
}
ret = 1;
end:
StreamTcpSessionClear(p->flow->protoctx);
StreamTcpFreeConfig(TRUE);
SCFree(p);
return ret;
}
/**
* \test set up a TCP session using the 4WHS:
* SYN, SYN, SYN/ACK, ACK: however the SYN/ACK and ACK
* are part of a normal 3WHS
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcp4WHSTest03 (void) {
int ret = 0;
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
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 = 0;
tcph.th_flags = TH_SYN;
p->tcph = &tcph;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(20);
p->tcph->th_ack = 0;
p->tcph->th_flags = TH_SYN;
p->flowflags = FLOW_PKT_TOCLIENT;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
if ((!(((TcpSession *)(p->flow->protoctx))->flags & STREAMTCP_FLAG_4WHS))) {
printf("STREAMTCP_FLAG_4WHS flag not set: ");
goto end;
}
p->tcph->th_seq = htonl(30);
p->tcph->th_ack = htonl(11);
p->tcph->th_flags = TH_SYN|TH_ACK;
p->flowflags = FLOW_PKT_TOCLIENT;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(11);
p->tcph->th_ack = htonl(31);
p->tcph->th_flags = TH_ACK;
p->flowflags = FLOW_PKT_TOSERVER;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
if (((TcpSession *)(p->flow->protoctx))->state != TCP_ESTABLISHED) {
printf("state is not ESTABLISHED: ");
goto end;
}
ret = 1;
end:
StreamTcpSessionClear(p->flow->protoctx);
StreamTcpFreeConfig(TRUE);
SCFree(p);
return ret;
}
/**
* \test Test the setting up a OS policy. Te OS policy values are defined in
* the config string "dummy_conf_string1"
*
* \retval On success it returns 1 and on failure 0
*/
static int StreamTcpTest15 (void) {
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
uint8_t payload[4];
struct in_addr addr;
IPV4Hdr ipv4h;
char os_policy_name[10] = "windows";
char *ip_addr;
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof (StreamTcpThread));
memset(&tcph, 0, sizeof (TCPHdr));
memset(&addr, 0, sizeof(addr));
memset(&ipv4h, 0, sizeof(ipv4h));
p->flow = &f;
int ret = 0;
StreamTcpInitConfig(TRUE);
/* Load the config string in to parser */
ConfCreateContextBackup();
ConfInit();
ConfYamlLoadString(dummy_conf_string1, strlen(dummy_conf_string1));
/* Get the IP address as string and add it to Host info tree for lookups */
ip_addr = StreamTcpParseOSPolicy(os_policy_name);
SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
strlcpy(os_policy_name, "linux\0", sizeof(os_policy_name));
ip_addr = StreamTcpParseOSPolicy(os_policy_name);
SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
/* prevent L7 from kicking in */
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
addr.s_addr = inet_addr("192.168.0.20");
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->dst.family = AF_INET;
p->dst.address.address_un_data32[0] = addr.s_addr;
p->ip4h = &ipv4h;
StreamTcpCreateTestPacket(payload, 0x41, 3, sizeof(payload)); /*AAA*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -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, sizeof(payload)); /*BBB*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(15);
p->tcph->th_ack = htonl(23);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x43, 3, sizeof(payload)); /*CCC*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(14);
p->tcph->th_ack = htonl(23);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x43, 3, sizeof(payload)); /*CCC*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
addr.s_addr = inet_addr("192.168.1.20");
p->tcph->th_seq = htonl(25);
p->tcph->th_ack = htonl(13);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOCLIENT;
p->dst.address.address_un_data32[0] = addr.s_addr;
StreamTcpCreateTestPacket(payload, 0x44, 3, sizeof(payload)); /*DDD*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(24);
p->tcph->th_ack = htonl(13);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOCLIENT;
StreamTcpCreateTestPacket(payload, 0x44, 3, sizeof(payload)); /*DDD*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -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 != 13 &&
((TcpSession *)(p->flow->protoctx))->server.next_seq != 23) {
printf("failed in next_seq match client.next_seq %"PRIu32""
" server.next_seq %"PRIu32"\n",
((TcpSession *)(p->flow->protoctx))->client.next_seq,
((TcpSession *)(p->flow->protoctx))->server.next_seq);
goto end;
}
if (((TcpSession *)(p->flow->protoctx))->client.os_policy !=
OS_POLICY_WINDOWS && ((TcpSession *)
(p->flow->protoctx))->server.os_policy != OS_POLICY_LINUX)
{
printf("failed in setting up OS policy, client.os_policy: %"PRIu8""
" should be %"PRIu8" and server.os_policy: %"PRIu8""
" should be %"PRIu8"\n", ((TcpSession *)
(p->flow->protoctx))->client.os_policy, OS_POLICY_WINDOWS,
((TcpSession *)(p->flow->protoctx))->server.os_policy,
OS_POLICY_LINUX);
goto end;
}
StreamTcpSessionPktFree(p);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
ConfDeInit();
ConfRestoreContextBackup();
SCFree(p);
return ret;
}
/**
* \test Test the setting up a OS policy. Te OS policy values are defined in
* the config string "dummy_conf_string1"
*
* \retval On success it returns 1 and on failure 0
*/
static int StreamTcpTest16 (void) {
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
uint8_t payload[4];
struct in_addr addr;
IPV4Hdr ipv4h;
char os_policy_name[10] = "windows";
char *ip_addr;
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof (StreamTcpThread));
memset(&tcph, 0, sizeof (TCPHdr));
memset(&addr, 0, sizeof(addr));
memset(&ipv4h, 0, sizeof(ipv4h));
p->flow = &f;
int ret = 0;
StreamTcpInitConfig(TRUE);
/* Load the config string in to parser */
ConfCreateContextBackup();
ConfInit();
ConfYamlLoadString(dummy_conf_string1, strlen(dummy_conf_string1));
/* Get the IP address as string and add it to Host info tree for lookups */
ip_addr = StreamTcpParseOSPolicy(os_policy_name);
SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
strlcpy(os_policy_name, "linux\0", sizeof(os_policy_name));
ip_addr = StreamTcpParseOSPolicy(os_policy_name);
SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
/* prevent L7 from kicking in */
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
addr.s_addr = inet_addr("192.168.0.1");
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->dst.family = AF_INET;
p->dst.address.address_un_data32[0] = addr.s_addr;
p->ip4h = &ipv4h;
StreamTcpCreateTestPacket(payload, 0x41, 3, sizeof(payload)); /*AAA*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -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, sizeof(payload)); /*BBB*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(15);
p->tcph->th_ack = htonl(23);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x43, 3, sizeof(payload)); /*CCC*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(14);
p->tcph->th_ack = htonl(23);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x43, 3, sizeof(payload)); /*CCC*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
addr.s_addr = inet_addr("192.168.1.1");
p->tcph->th_seq = htonl(25);
p->tcph->th_ack = htonl(13);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOCLIENT;
p->dst.address.address_un_data32[0] = addr.s_addr;
StreamTcpCreateTestPacket(payload, 0x44, 3, sizeof(payload)); /*DDD*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(24);
p->tcph->th_ack = htonl(13);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOCLIENT;
StreamTcpCreateTestPacket(payload, 0x44, 3, sizeof(payload)); /*DDD*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -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 != 13 &&
((TcpSession *)(p->flow->protoctx))->server.next_seq != 23) {
printf("failed in next_seq match client.next_seq %"PRIu32""
" server.next_seq %"PRIu32"\n",
((TcpSession *)(p->flow->protoctx))->client.next_seq,
((TcpSession *)(p->flow->protoctx))->server.next_seq);
goto end;
}
if (((TcpSession *)(p->flow->protoctx))->client.os_policy !=
OS_POLICY_LINUX && ((TcpSession *)
(p->flow->protoctx))->server.os_policy != OS_POLICY_WINDOWS)
{
printf("failed in setting up OS policy, client.os_policy: %"PRIu8""
" should be %"PRIu8" and server.os_policy: %"PRIu8""
" should be %"PRIu8"\n", ((TcpSession *)
(p->flow->protoctx))->client.os_policy, OS_POLICY_LINUX,
((TcpSession *)(p->flow->protoctx))->server.os_policy,
OS_POLICY_WINDOWS);
goto end;
}
StreamTcpSessionPktFree(p);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
ConfDeInit();
ConfRestoreContextBackup();
SCFree(p);
return ret;
}
/**
* \test Test the setting up a OS policy. Te OS policy values are defined in
* the config string "dummy_conf_string1". To check the setting of
* Default os policy
*
* \retval On success it returns 1 and on failure 0
*/
static int StreamTcpTest17 (void) {
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
uint8_t payload[4];
struct in_addr addr;
IPV4Hdr ipv4h;
char os_policy_name[10] = "windows";
char *ip_addr;
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof (StreamTcpThread));
memset(&tcph, 0, sizeof (TCPHdr));
memset(&addr, 0, sizeof(addr));
memset(&ipv4h, 0, sizeof(ipv4h));
p->flow = &f;
int ret = 0;
StreamTcpInitConfig(TRUE);
/* Load the config string in to parser */
ConfCreateContextBackup();
ConfInit();
ConfYamlLoadString(dummy_conf_string1, strlen(dummy_conf_string1));
/* Get the IP address as string and add it to Host info tree for lookups */
ip_addr = StreamTcpParseOSPolicy(os_policy_name);
SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
strlcpy(os_policy_name, "linux\0", sizeof(os_policy_name));
ip_addr = StreamTcpParseOSPolicy(os_policy_name);
SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
/* prevent L7 from kicking in */
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
addr.s_addr = inet_addr("192.168.0.1");
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->dst.family = AF_INET;
p->dst.address.address_un_data32[0] = addr.s_addr;
p->ip4h = &ipv4h;
StreamTcpCreateTestPacket(payload, 0x41, 3, sizeof(payload)); /*AAA*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -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, sizeof(payload)); /*BBB*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(15);
p->tcph->th_ack = htonl(23);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x43, 3, sizeof(payload)); /*CCC*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(14);
p->tcph->th_ack = htonl(23);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x43, 3, sizeof(payload)); /*CCC*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
addr.s_addr = inet_addr("10.1.1.1");
p->tcph->th_seq = htonl(25);
p->tcph->th_ack = htonl(13);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOCLIENT;
p->dst.address.address_un_data32[0] = addr.s_addr;
StreamTcpCreateTestPacket(payload, 0x44, 3, sizeof(payload)); /*DDD*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1)
goto end;
p->tcph->th_seq = htonl(24);
p->tcph->th_ack = htonl(13);
p->tcph->th_flags = TH_ACK|TH_PUSH;
p->flowflags = FLOW_PKT_TOCLIENT;
StreamTcpCreateTestPacket(payload, 0x44, 3, sizeof(payload)); /*DDD*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -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 != 13 &&
((TcpSession *)(p->flow->protoctx))->server.next_seq != 23) {
printf("failed in next_seq match client.next_seq %"PRIu32""
" server.next_seq %"PRIu32"\n",
((TcpSession *)(p->flow->protoctx))->client.next_seq,
((TcpSession *)(p->flow->protoctx))->server.next_seq);
goto end;
}
if (((TcpSession *)(p->flow->protoctx))->client.os_policy !=
OS_POLICY_LINUX && ((TcpSession *)
(p->flow->protoctx))->server.os_policy != OS_POLICY_DEFAULT)
{
printf("failed in setting up OS policy, client.os_policy: %"PRIu8""
" should be %"PRIu8" and server.os_policy: %"PRIu8""
" should be %"PRIu8"\n", ((TcpSession *)
(p->flow->protoctx))->client.os_policy, OS_POLICY_LINUX,
((TcpSession *)(p->flow->protoctx))->server.os_policy,
OS_POLICY_DEFAULT);
goto end;
}
StreamTcpSessionPktFree(p);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
ConfDeInit();
ConfRestoreContextBackup();
SCFree(p);
return ret;
}
/** \test Test the various OS policies based on different IP addresses from
confuguration defined in 'dummy_conf_string1' */
static int StreamTcpTest18 (void) {
struct in_addr addr;
char os_policy_name[10] = "windows";
char *ip_addr;
TcpStream stream;
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
IPV4Hdr ipv4h;
int ret = 0;
memset(&addr, 0, sizeof(addr));
memset(&stream, 0, sizeof(stream));
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
memset(&ipv4h, 0, sizeof(ipv4h));
StreamTcpInitConfig(TRUE);
SCHInfoCleanResources();
/* Load the config string in to parser */
ConfCreateContextBackup();
ConfInit();
ConfYamlLoadString(dummy_conf_string1, strlen(dummy_conf_string1));
/* Get the IP address as string and add it to Host info tree for lookups */
ip_addr = StreamTcpParseOSPolicy(os_policy_name);
SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
p->dst.family = AF_INET;
p->ip4h = &ipv4h;
addr.s_addr = inet_addr("192.168.1.1");
p->dst.address.address_un_data32[0] = addr.s_addr;
StreamTcpSetOSPolicy(&stream, p);
if (stream.os_policy != OS_POLICY_WINDOWS)
goto end;
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
ConfDeInit();
ConfRestoreContextBackup();
SCFree(p);
return ret;
}
/** \test Test the various OS policies based on different IP addresses from
confuguration defined in 'dummy_conf_string1' */
static int StreamTcpTest19 (void) {
struct in_addr addr;
char os_policy_name[10] = "windows";
char *ip_addr;
TcpStream stream;
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
IPV4Hdr ipv4h;
int ret = 0;
memset(&addr, 0, sizeof(addr));
memset(&stream, 0, sizeof(stream));
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
memset(&ipv4h, 0, sizeof(ipv4h));
StreamTcpInitConfig(TRUE);
SCHInfoCleanResources();
/* Load the config string in to parser */
ConfCreateContextBackup();
ConfInit();
ConfYamlLoadString(dummy_conf_string1, strlen(dummy_conf_string1));
/* Get the IP address as string and add it to Host info tree for lookups */
ip_addr = StreamTcpParseOSPolicy(os_policy_name);
SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
p->dst.family = AF_INET;
p->ip4h = &ipv4h;
addr.s_addr = inet_addr("192.168.0.30");
p->dst.address.address_un_data32[0] = addr.s_addr;
StreamTcpSetOSPolicy(&stream, p);
if (stream.os_policy != OS_POLICY_WINDOWS) {
printf("expected os_policy: %"PRIu8" but received %"PRIu8": ",
OS_POLICY_WINDOWS, stream.os_policy);
goto end;
}
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
ConfDeInit();
ConfRestoreContextBackup();
SCFree(p);
return ret;
}
/** \test Test the various OS policies based on different IP addresses from
confuguration defined in 'dummy_conf_string1' */
static int StreamTcpTest20 (void) {
struct in_addr addr;
char os_policy_name[10] = "linux";
char *ip_addr;
TcpStream stream;
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
IPV4Hdr ipv4h;
int ret = 0;
memset(&addr, 0, sizeof(addr));
memset(&stream, 0, sizeof(stream));
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
memset(&ipv4h, 0, sizeof(ipv4h));
StreamTcpInitConfig(TRUE);
SCHInfoCleanResources();
/* Load the config string in to parser */
ConfCreateContextBackup();
ConfInit();
ConfYamlLoadString(dummy_conf_string1, strlen(dummy_conf_string1));
/* Get the IP address as string and add it to Host info tree for lookups */
ip_addr = StreamTcpParseOSPolicy(os_policy_name);
SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
p->dst.family = AF_INET;
p->ip4h = &ipv4h;
addr.s_addr = inet_addr("192.168.0.1");
p->dst.address.address_un_data32[0] = addr.s_addr;
StreamTcpSetOSPolicy(&stream, p);
if (stream.os_policy != OS_POLICY_LINUX) {
printf("expected os_policy: %"PRIu8" but received %"PRIu8"\n",
OS_POLICY_LINUX, stream.os_policy);
goto end;
}
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
ConfDeInit();
ConfRestoreContextBackup();
SCFree(p);
return ret;
}
/** \test Test the various OS policies based on different IP addresses from
confuguration defined in 'dummy_conf_string1' */
static int StreamTcpTest21 (void) {
struct in_addr addr;
char os_policy_name[10] = "linux";
char *ip_addr;
TcpStream stream;
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
IPV4Hdr ipv4h;
int ret = 0;
memset(&addr, 0, sizeof(addr));
memset(&stream, 0, sizeof(stream));
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
memset(&ipv4h, 0, sizeof(ipv4h));
StreamTcpInitConfig(TRUE);
SCHInfoCleanResources();
/* Load the config string in to parser */
ConfCreateContextBackup();
ConfInit();
ConfYamlLoadString(dummy_conf_string1, strlen(dummy_conf_string1));
/* Get the IP address as string and add it to Host info tree for lookups */
ip_addr = StreamTcpParseOSPolicy(os_policy_name);
SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
p->dst.family = AF_INET;
p->ip4h = &ipv4h;
addr.s_addr = inet_addr("192.168.1.30");
p->dst.address.address_un_data32[0] = addr.s_addr;
StreamTcpSetOSPolicy(&stream, p);
if (stream.os_policy != OS_POLICY_LINUX) {
printf("expected os_policy: %"PRIu8" but received %"PRIu8"\n",
OS_POLICY_LINUX, stream.os_policy);
goto end;
}
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
ConfDeInit();
ConfRestoreContextBackup();
SCFree(p);
return ret;
}
/** \test Test the various OS policies based on different IP addresses from
confuguration defined in 'dummy_conf_string1' */
static int StreamTcpTest22 (void) {
struct in_addr addr;
char os_policy_name[10] = "windows";
char *ip_addr;
TcpStream stream;
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
IPV4Hdr ipv4h;
int ret = 0;
memset(&addr, 0, sizeof(addr));
memset(&stream, 0, sizeof(stream));
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
memset(&ipv4h, 0, sizeof(ipv4h));
StreamTcpInitConfig(TRUE);
SCHInfoCleanResources();
/* Load the config string in to parser */
ConfCreateContextBackup();
ConfInit();
ConfYamlLoadString(dummy_conf_string1, strlen(dummy_conf_string1));
/* Get the IP address as string and add it to Host info tree for lookups */
ip_addr = StreamTcpParseOSPolicy(os_policy_name);
SCHInfoAddHostOSInfo(os_policy_name, ip_addr, -1);
p->dst.family = AF_INET;
p->ip4h = &ipv4h;
addr.s_addr = inet_addr("123.231.2.1");
p->dst.address.address_un_data32[0] = addr.s_addr;
StreamTcpSetOSPolicy(&stream, p);
if (stream.os_policy != OS_POLICY_DEFAULT) {
printf("expected os_policy: %"PRIu8" but received %"PRIu8"\n",
OS_POLICY_DEFAULT, stream.os_policy);
goto end;
}
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
ConfDeInit();
ConfRestoreContextBackup();
SCFree(p);
return ret;
}
/** \test Test the stream mem leaks conditions. */
static int StreamTcpTest23(void)
{
TcpSession ssn;
Flow f;
TCPHdr tcph;
TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx();
uint8_t packet[1460] = "";
ThreadVars tv;
int result = 1;
PacketQueue pq;
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
memset(&pq,0,sizeof(PacketQueue));
memset(&ssn, 0, sizeof (TcpSession));
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
memset(&f, 0, sizeof (Flow));
memset(&tcph, 0, sizeof (TCPHdr));
memset(&tv, 0, sizeof (ThreadVars));
StreamTcpInitConfig(TRUE);
/* prevent L7 from kicking in */
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
ssn.client.os_policy = OS_POLICY_BSD;
f.protoctx = &ssn;
p->src.family = AF_INET;
p->dst.family = AF_INET;
p->proto = IPPROTO_TCP;
p->flow = &f;
tcph.th_win = 5480;
tcph.th_flags = TH_PUSH | TH_ACK;
p->tcph = &tcph;
p->flowflags = FLOW_PKT_TOSERVER;
p->payload = packet;
ssn.client.ra_app_base_seq = ssn.client.ra_raw_base_seq = ssn.client.last_ack = 3184324453UL;
p->tcph->th_seq = htonl(3184324453UL);
p->tcph->th_ack = htonl(3373419609UL);
p->payload_len = 2;
if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &ssn.client, p, &pq) == -1) {
printf("failed in segment reassmebling: ");
result &= 0;
goto end;
}
p->tcph->th_seq = htonl(3184324455UL);
p->tcph->th_ack = htonl(3373419621UL);
p->payload_len = 2;
if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &ssn.client, p, &pq) == -1) {
printf("failed in segment reassmebling: ");
result &= 0;
goto end;
}
p->tcph->th_seq = htonl(3184324453UL);
p->tcph->th_ack = htonl(3373419621UL);
p->payload_len = 6;
if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &ssn.client, p, &pq) == -1) {
printf("failed in segment reassmebling: ");
result &= 0;
// goto end;
}
if(ssn.client.seg_list_tail->payload_len != 4) {
printf("failed in segment reassmebling: ");
result &= 0;
}
end:
StreamTcpReturnStreamSegments(&ssn.client);
StreamTcpFreeConfig(TRUE);
if (stream_memuse == 0) {
result &= 1;
} else {
printf("stream_memuse %"PRIu64"\n", stream_memuse);
}
SCFree(p);
return result;
}
/** \test Test the stream mem leaks conditions. */
static int StreamTcpTest24(void)
{
TcpSession ssn;
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
TCPHdr tcph;
TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx();
uint8_t packet[1460] = "";
ThreadVars tv;
int result = 1;
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
StreamTcpInitConfig(TRUE);
/* prevent L7 from kicking in */
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
memset(&ssn, 0, sizeof (TcpSession));
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
memset(&f, 0, sizeof (Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&tcph, 0, sizeof (TCPHdr));
ssn.client.os_policy = OS_POLICY_BSD;
f.protoctx = &ssn;
p->src.family = AF_INET;
p->dst.family = AF_INET;
p->proto = IPPROTO_TCP;
p->flow = &f;
tcph.th_win = 5480;
tcph.th_flags = TH_PUSH | TH_ACK;
p->tcph = &tcph;
p->flowflags = FLOW_PKT_TOSERVER;
p->payload = packet;
ssn.client.ra_app_base_seq = ssn.client.ra_raw_base_seq = ssn.client.last_ack = 3184324453UL;
p->tcph->th_seq = htonl(3184324455UL);
p->tcph->th_ack = htonl(3373419621UL);
p->payload_len = 4;
if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &ssn.client, p, &pq) == -1) {
printf("failed in segment reassmebling\n");
result &= 0;
goto end;
}
p->tcph->th_seq = htonl(3184324459UL);
p->tcph->th_ack = htonl(3373419633UL);
p->payload_len = 2;
if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &ssn.client, p, &pq) == -1) {
printf("failed in segment reassmebling\n");
result &= 0;
goto end;
}
p->tcph->th_seq = htonl(3184324459UL);
p->tcph->th_ack = htonl(3373419657UL);
p->payload_len = 4;
if (StreamTcpReassembleHandleSegment(&tv, ra_ctx,&ssn, &ssn.client, p, &pq) == -1) {
printf("failed in segment reassmebling\n");
result &= 0;
goto end;
}
if(ssn.client.seg_list_tail->payload_len != 2) {
printf("failed in segment reassmebling\n");
result &= 0;
}
end:
StreamTcpReturnStreamSegments(&ssn.client);
StreamTcpFreeConfig(TRUE);
if (stream_memuse == 0) {
result &= 1;
} else {
printf("stream_memuse %"PRIu64"\n", stream_memuse);
}
SCFree(p);
return result;
}
/**
* \test Test the initialization of tcp streams with congestion flags
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest25(void) {
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
uint8_t payload[4];
TCPHdr tcph;
TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx();
int ret = 0;
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof (StreamTcpThread));
memset(&tcph, 0, sizeof (TCPHdr));
stt.ra_ctx = ra_ctx;
p->flow = &f;
tcph.th_win = htons(5480);
tcph.th_flags = TH_SYN | TH_CWR;
p->tcph = &tcph;
p->flowflags = FLOW_PKT_TOSERVER;
/* prevent L7 from kicking in */
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOSERVER, 4096);
StreamMsgQueueSetMinChunkLen(FLOW_PKT_TOCLIENT, 4096);
StreamTcpInitConfig(TRUE);
if (StreamTcpPacket(&tv, p, &stt, &pq) == -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, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
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, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
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, 4); /*AAA*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
goto end;
p->flowflags = FLOW_PKT_TOCLIENT;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
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, 4); /*BBB*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
goto end;
p->flowflags = FLOW_PKT_TOCLIENT;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
goto end;
StreamTcpSessionClear(p->flow->protoctx);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
SCFree(p);
return ret;
}
/**
* \test Test the initialization of tcp streams with congestion flags
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest26(void) {
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
uint8_t payload[4];
TCPHdr tcph;
TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx();
int ret = 0;
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof (StreamTcpThread));
memset(&tcph, 0, sizeof (TCPHdr));
stt.ra_ctx = ra_ctx;
p->flow = &f;
tcph.th_win = htons(5480);
tcph.th_flags = TH_SYN | TH_ECN;
p->tcph = &tcph;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpInitConfig(TRUE);
if (StreamTcpPacket(&tv, p, &stt, &pq) == -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, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
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, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
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, 4); /*AAA*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
goto end;
p->flowflags = FLOW_PKT_TOCLIENT;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
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, 4); /*BBB*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
goto end;
p->flowflags = FLOW_PKT_TOCLIENT;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
goto end;
StreamTcpSessionClear(p->flow->protoctx);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
SCFree(p);
return ret;
}
/**
* \test Test the initialization of tcp streams with congestion flags
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest27(void) {
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
uint8_t payload[4];
TCPHdr tcph;
TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx();
int ret = 0;
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof (StreamTcpThread));
memset(&tcph, 0, sizeof (TCPHdr));
stt.ra_ctx = ra_ctx;
p->flow = &f;
tcph.th_win = htons(5480);
tcph.th_flags = TH_SYN | TH_CWR | TH_ECN;
p->tcph = &tcph;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpInitConfig(TRUE);
if (StreamTcpPacket(&tv, p, &stt, &pq) == -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, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
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, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
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, 4); /*AAA*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
goto end;
p->flowflags = FLOW_PKT_TOCLIENT;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
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, 4); /*BBB*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
goto end;
p->flowflags = FLOW_PKT_TOCLIENT;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL)
goto end;
StreamTcpSessionClear(p->flow->protoctx);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
SCFree(p);
return ret;
}
/** \test Test the memcap incrementing/decrementing and memcap check */
static int StreamTcpTest28(void)
{
uint8_t ret = 0;
StreamTcpInitConfig(TRUE);
uint32_t memuse = stream_memuse;
StreamTcpIncrMemuse(500);
if (stream_memuse != (memuse+500)) {
printf("failed in incrementing the memory");
goto end;
}
StreamTcpDecrMemuse(500);
if (stream_memuse != memuse) {
printf("failed in decrementing the memory");
goto end;
}
if (StreamTcpCheckMemcap(500) != 1) {
printf("failed in validating the memcap");
goto end;
}
if (StreamTcpCheckMemcap((memuse + stream_config.memcap)) != 0) {
printf("failed in validating the memcap");
goto end;
}
StreamTcpFreeConfig(TRUE);
if (stream_memuse != 0) {
printf("failed in clearing the memory");
goto end;
}
ret = 1;
return ret;
end:
StreamTcpFreeConfig(TRUE);
return ret;
}
#if 0
/**
* \test Test the resetting of the sesison with bad checksum packet and later
* send the malicious contents on the session. Engine should drop the
* packet with the bad checksum.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest29(void)
{
Packet p;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
TcpSession ssn;
IPV4Hdr ipv4h;
TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx();
struct in_addr addr;
struct in_addr addr1;
TCPCache tcpc;
TCPVars tcpvars;
TcpStream server;
TcpStream client;
memset (&p, 0, SIZE_OF_PACKET);
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof (StreamTcpThread));
memset(&tcph, 0, sizeof (TCPHdr));
memset (&ipv4h, 0, sizeof(IPV4Hdr));
memset (&addr, 0, sizeof(addr));
memset (&addr1, 0, sizeof(addr1));
memset (&tcpc, 0, sizeof(tcpc));
memset (&tcpvars, 0, sizeof(tcpvars));
memset(&ssn, 0, sizeof (TcpSession));
memset(&server, 0, sizeof (TcpStream));
memset(&client, 0, sizeof (TcpStream));
uint8_t packet[1460] = "";
int result = 1;
StreamTcpInitConfig(TRUE);
/* prevent L7 from kicking in */
ssn.client.os_policy = OS_POLICY_BSD;
p.src.family = AF_INET;
p.dst.family = AF_INET;
p.proto = IPPROTO_TCP;
p.flow = &f;
tcph.th_win = 5480;
p.tcph = &tcph;
p.payload = packet;
p.ip4h = &ipv4h;
p.tcpc = tcpc;
p.tcpc.comp_csum = -1;
tcpvars.hlen = 20;
p.tcpvars = tcpvars;
ssn.state = TCP_ESTABLISHED;
addr.s_addr = inet_addr("10.1.3.53");
p.dst.address.address_un_data32[0] = addr.s_addr;
addr1.s_addr = inet_addr("10.1.3.7");
p.src.address.address_un_data32[0] = addr1.s_addr;
f.protoctx = &ssn;
stt.ra_ctx = ra_ctx;
ssn.server = server;
ssn.client = client;
ssn.client.isn = 10;
ssn.client.window = 5184;
ssn.client.last_ack = 10;
ssn.client.ra_base_seq = 10;
ssn.client.next_win = 5184;
ssn.server.isn = 119197101;
ssn.server.window = 5184;
ssn.server.next_win = 5184;
ssn.server.last_ack = 119197101;
ssn.server.ra_base_seq = 119197101;
tcph.th_flags = TH_PUSH | TH_ACK;
p.flowflags = FLOW_PKT_TOSERVER;
p.tcph->th_seq = htonl(11);
p.tcph->th_ack = htonl(119197102);
p.payload_len = 4;
p.ip4h->ip_src = addr1;
p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src),
(uint16_t *)p.tcph,
(p.payload_len +
p.tcpvars.hlen) );
if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) {
printf("failed in segment reassmebling\n");
result &= 0;
goto end;
}
tcph.th_flags = TH_ACK;
p.flowflags = FLOW_PKT_TOCLIENT;
p.tcph->th_seq = htonl(119197102);
p.tcph->th_ack = htonl(15);
p.payload_len = 0;
p.ip4h->ip_src = addr;
p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src),
(uint16_t *)p.tcph,
(p.payload_len +
p.tcpvars.hlen) );
if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) {
printf("failed in segment reassmebling\n");
result &= 0;
goto end;
}
tcph.th_flags = TH_RST | TH_ACK;
p.flowflags = FLOW_PKT_TOSERVER;
p.tcph->th_seq = htonl(15);
p.tcph->th_ack = htonl(119197102);
p.payload_len = 0;
p.ip4h->ip_src = addr1;
p.tcph->th_sum = 12345;
if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) {
printf("failed in segment reassmebling\n");
result &= 0;
goto end;
}
if (ssn.state != TCP_ESTABLISHED) {
printf("the ssn.state should be TCP_ESTABLISHED(%"PRIu8"), not %"PRIu8""
"\n", TCP_ESTABLISHED, ssn.state);
result &= 0;
goto end;
}
end:
StreamTcpReturnStreamSegments(&ssn.client);
StreamTcpFreeConfig(TRUE);
return result;
}
/**
* \test Test the overlapping of the packet with bad checksum packet and later
* send the malicious contents on the session. Engine should drop the
* packet with the bad checksum.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest30(void)
{
Packet p;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
TcpSession ssn;
IPV4Hdr ipv4h;
TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx();
struct in_addr addr;
struct in_addr addr1;
TCPCache tcpc;
TCPVars tcpvars;
TcpStream server;
TcpStream client;
memset (&p, 0, SIZE_OF_PACKET);
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof (StreamTcpThread));
memset(&tcph, 0, sizeof (TCPHdr));
memset (&ipv4h, 0, sizeof(IPV4Hdr));
memset (&addr, 0, sizeof(addr));
memset (&addr1, 0, sizeof(addr1));
memset (&tcpc, 0, sizeof(tcpc));
memset (&tcpvars, 0, sizeof(tcpvars));
memset(&ssn, 0, sizeof (TcpSession));
memset(&server, 0, sizeof (TcpStream));
memset(&client, 0, sizeof (TcpStream));
uint8_t payload[9] = "AAAAAAAAA";
uint8_t payload1[9] = "GET /EVIL";
uint8_t expected_content[9] = { 0x47, 0x45, 0x54, 0x20, 0x2f, 0x45, 0x56,
0x49, 0x4c };
int result = 1;
StreamTcpInitConfig(TRUE);
/* prevent L7 from kicking in */
ssn.client.os_policy = OS_POLICY_BSD;
p.src.family = AF_INET;
p.dst.family = AF_INET;
p.proto = IPPROTO_TCP;
p.flow = &f;
tcph.th_win = 5480;
p.tcph = &tcph;
p.payload = payload;
p.ip4h = &ipv4h;
p.tcpc = tcpc;
p.tcpc.comp_csum = -1;
p.tcpvars = tcpvars;
ssn.state = TCP_ESTABLISHED;
addr.s_addr = inet_addr("10.1.3.53");
p.dst.address.address_un_data32[0] = addr.s_addr;
addr1.s_addr = inet_addr("10.1.3.7");
p.src.address.address_un_data32[0] = addr1.s_addr;
f.protoctx = &ssn;
stt.ra_ctx = ra_ctx;
ssn.server = server;
ssn.client = client;
ssn.client.isn = 10;
ssn.client.window = 5184;
ssn.client.last_ack = 10;
ssn.client.ra_base_seq = 10;
ssn.client.next_win = 5184;
ssn.server.isn = 1351079940;
ssn.server.window = 5184;
ssn.server.next_win = 1351088132;
ssn.server.last_ack = 1351079940;
ssn.server.ra_base_seq = 1351079940;
tcph.th_flags = TH_PUSH | TH_ACK;
p.flowflags = FLOW_PKT_TOSERVER;
p.tcph->th_seq = htonl(11);
p.tcph->th_ack = htonl(1351079940);
p.payload_len = 9;
p.ip4h->ip_src = addr1;
p.tcph->th_sum = 12345;
if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) {
printf("failed in segment reassmebling\n");
result &= 0;
goto end;
}
tcph.th_flags = TH_PUSH | TH_ACK;
p.flowflags = FLOW_PKT_TOSERVER;
p.tcph->th_seq = htonl(11);
p.tcph->th_ack = htonl(1351079940);
p.payload = payload1;
p.payload_len = 9;
p.ip4h->ip_src = addr1;
p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src),
(uint16_t *)p.tcph,
(p.payload_len +
p.tcpvars.hlen) );
if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) {
printf("failed in segment reassmebling\n");
result &= 0;
goto end;
}
tcph.th_flags = TH_ACK;
p.flowflags = FLOW_PKT_TOCLIENT;
p.tcph->th_seq = htonl(1351079940);
p.tcph->th_ack = htonl(20);
p.payload_len = 0;
p.ip4h->ip_src = addr;
p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src),
(uint16_t *)p.tcph,
(p.payload_len +
p.tcpvars.hlen) );
if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) {
printf("failed in segment reassmebling\n");
result &= 0;
goto end;
}
if (StreamTcpCheckStreamContents(expected_content, 9, &ssn.client) != 1) {
printf("the contents are not as expected(GET /EVIL), contents are: ");
PrintRawDataFp(stdout, ssn.client.seg_list->payload, 9);
result &= 0;
goto end;
}
end:
StreamTcpReturnStreamSegments(&ssn.client);
StreamTcpFreeConfig(TRUE);
return result;
}
/**
* \test Test the multiple SYN packet handling with bad checksum and timestamp
* value. Engine should drop the bad checksum packet and establish
* TCP session correctly.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest31(void)
{
Packet p;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
TcpSession ssn;
IPV4Hdr ipv4h;
TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx();
struct in_addr addr;
struct in_addr addr1;
TCPCache tcpc;
TCPVars tcpvars;
TcpStream server;
TcpStream client;
TCPOpt tcpopt;
memset (&p, 0, SIZE_OF_PACKET);
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof (StreamTcpThread));
memset(&tcph, 0, sizeof (TCPHdr));
memset (&ipv4h, 0, sizeof(IPV4Hdr));
memset (&addr, 0, sizeof(addr));
memset (&addr1, 0, sizeof(addr1));
memset (&tcpc, 0, sizeof(tcpc));
memset (&tcpvars, 0, sizeof(tcpvars));
memset(&ssn, 0, sizeof (TcpSession));
memset(&server, 0, sizeof (TcpStream));
memset(&client, 0, sizeof (TcpStream));
memset(&tcpopt, 0, sizeof (TCPOpt));
int result = 1;
StreamTcpInitConfig(TRUE);
/* prevent L7 from kicking in */
ssn.client.os_policy = OS_POLICY_LINUX;
p.src.family = AF_INET;
p.dst.family = AF_INET;
p.proto = IPPROTO_TCP;
p.flow = &f;
tcph.th_win = 5480;
p.tcph = &tcph;
p.ip4h = &ipv4h;
p.tcpc = tcpc;
p.tcpc.comp_csum = -1;
p.tcpvars = tcpvars;
p.tcpvars.ts = &tcpopt;
addr.s_addr = inet_addr("10.1.3.53");
p.dst.address.address_un_data32[0] = addr.s_addr;
addr1.s_addr = inet_addr("10.1.3.7");
p.src.address.address_un_data32[0] = addr1.s_addr;
f.protoctx = &ssn;
stt.ra_ctx = ra_ctx;
ssn.server = server;
ssn.client = client;
ssn.client.isn = 10;
ssn.client.window = 5184;
ssn.client.last_ack = 10;
ssn.client.ra_base_seq = 10;
ssn.client.next_win = 5184;
ssn.server.isn = 1351079940;
ssn.server.window = 5184;
ssn.server.next_win = 1351088132;
ssn.server.last_ack = 1351079940;
ssn.server.ra_base_seq = 1351079940;
tcph.th_flags = TH_SYN;
p.flowflags = FLOW_PKT_TOSERVER;
p.tcph->th_seq = htonl(10);
p.payload_len = 0;
p.ip4h->ip_src = addr1;
p.tcpc.ts1 = 100;
p.tcph->th_sum = 12345;
if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) {
printf("failed in segment reassmebling\n");
result &= 0;
goto end;
}
tcph.th_flags = TH_SYN;
p.flowflags = FLOW_PKT_TOSERVER;
p.tcph->th_seq = htonl(10);
p.payload_len = 0;
p.ip4h->ip_src = addr1;
p.tcpc.ts1 = 10;
p.tcpc.comp_csum = -1;
p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src),
(uint16_t *)p.tcph,
(p.payload_len +
p.tcpvars.hlen) );
if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) {
printf("failed in segment reassmebling\n");
result &= 0;
goto end;
}
ssn.flags |= STREAMTCP_FLAG_TIMESTAMP;
tcph.th_flags = TH_SYN | TH_ACK;
p.flowflags = FLOW_PKT_TOCLIENT;
p.tcph->th_seq = htonl(1351079940);
p.tcph->th_ack = htonl(11);
p.payload_len = 0;
p.tcpc.ts1 = 10;
p.ip4h->ip_src = addr;
p.tcpc.comp_csum = -1;
p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src),
(uint16_t *)p.tcph,
(p.payload_len +
p.tcpvars.hlen) );
if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) {
printf("failed in segment reassmebling\n");
result &= 0;
goto end;
}
tcph.th_flags = TH_ACK;
p.flowflags = FLOW_PKT_TOSERVER;
p.tcph->th_seq = htonl(11);
p.tcph->th_ack = htonl(1351079941);
p.payload_len = 0;
p.tcpc.ts1 = 10;
p.ip4h->ip_src = addr1;
p.tcpc.comp_csum = -1;
p.tcph->th_sum = TCPCalculateChecksum((uint16_t *)&(p.ip4h->ip_src),
(uint16_t *)p.tcph,
(p.payload_len +
p.tcpvars.hlen) );
if (StreamTcp(&tv, &p, (void *)&stt, NULL, NULL) != TM_ECODE_OK) {
printf("failed in segment reassmebling\n");
result &= 0;
goto end;
}
if (ssn.state != TCP_ESTABLISHED) {
printf("the should have been changed to TCP_ESTABLISHED!!\n ");
result &= 0;
goto end;
}
end:
StreamTcpReturnStreamSegments(&ssn.client);
StreamTcpFreeConfig(TRUE);
return result;
}
/**
* \test Test the initialization of tcp streams with ECN & CWR flags
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest32(void) {
Packet p;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
uint8_t payload[4];
TCPHdr tcph;
TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx();
int ret = 0;
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
memset (&p, 0, SIZE_OF_PACKET);
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof (StreamTcpThread));
memset(&tcph, 0, sizeof (TCPHdr));
stt.ra_ctx = ra_ctx;
p.flow = &f;
tcph.th_win = htons(5480);
tcph.th_flags = TH_SYN | TH_CWR | TH_ECN;
p.tcph = &tcph;
p.flowflags = FLOW_PKT_TOSERVER;
StreamTcpInitConfig(TRUE);
if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1)
goto end;
p.tcph->th_ack = htonl(1);
p.tcph->th_flags = TH_SYN | TH_ACK | TH_ECN;
p.flowflags = FLOW_PKT_TOCLIENT;
if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1 || (TcpSession *)p.flow->protoctx == NULL) {
printf("failed in processing packet\n");
goto end;
}
p.tcph->th_ack = htonl(1);
p.tcph->th_seq = htonl(1);
p.tcph->th_flags = TH_ACK | TH_ECN | TH_CWR;
p.flowflags = FLOW_PKT_TOSERVER;
if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1 || (TcpSession *)p.flow->protoctx == NULL) {
printf("failed in processing packet\n");
goto end;
}
p.tcph->th_ack = htonl(1);
p.tcph->th_seq = htonl(2);
p.tcph->th_flags = TH_PUSH | TH_ACK | TH_ECN | TH_CWR;
p.flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
p.payload = payload;
p.payload_len = 3;
if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1 || (TcpSession *)p.flow->protoctx == NULL) {
printf("failed in processing packet\n");
goto end;
}
p.flowflags = FLOW_PKT_TOCLIENT;
p.tcph->th_flags = TH_ACK;
if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1 || (TcpSession *)p.flow->protoctx == NULL) {
printf("failed in processing packet\n");
goto end;
}
if (((TcpSession *)p.flow->protoctx)->state != TCP_ESTABLISHED) {
printf("the TCP state should be TCP_ESTABLISEHD\n");
goto end;
}
StreamTcpSessionClear(p.flow->protoctx);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
return ret;
}
/**
* \test Test the allocation of TCP session for a given packet when the same
* ports have been used to start the new session after resetting the
* previous session.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest33 (void) {
Packet p;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
TcpReassemblyThreadCtx ra_ctx;
StreamMsgQueue stream_q;
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
memset(&stream_q, 0, sizeof(StreamMsgQueue));
memset(&ra_ctx, 0, sizeof(TcpReassemblyThreadCtx));
memset (&p, 0, SIZE_OF_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;
ra_ctx.stream_q = &stream_q;
stt.ra_ctx = &ra_ctx;
StreamTcpInitConfig(TRUE);
if (StreamTcpPacket(&tv, &p, &stt, &pq) == -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, &pq) == -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, &pq) == -1)
goto end;
p.tcph->th_ack = htonl(1);
p.tcph->th_seq = htonl(1);
p.tcph->th_flags = TH_RST | TH_ACK;
p.flowflags = FLOW_PKT_TOSERVER;
if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1)
goto end;
if (((TcpSession *)(p.flow->protoctx))->state != TCP_CLOSED) {
printf("Tcp session should have been closed\n");
goto end;
}
p.tcph->th_seq = htonl(1);
p.tcph->th_flags = TH_SYN;
p.flowflags = FLOW_PKT_TOSERVER;
if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1)
goto end;
p.tcph->th_seq = htonl(1);
p.tcph->th_ack = htonl(2);
p.tcph->th_flags = TH_SYN | TH_ACK;
p.flowflags = FLOW_PKT_TOCLIENT;
if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1)
goto end;
p.tcph->th_ack = htonl(2);
p.tcph->th_seq = htonl(2);
p.tcph->th_flags = TH_ACK;
p.flowflags = FLOW_PKT_TOSERVER;
if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1)
goto end;
if (((TcpSession *)(p.flow->protoctx))->state != TCP_ESTABLISHED) {
printf("Tcp session should have been ESTABLISHED\n");
goto end;
}
ret = 1;
end:
StreamTcpSessionClear(p.flow->protoctx);
StreamTcpFreeConfig(TRUE);
return ret;
}
/**
* \test Test the allocation of TCP session for a given packet when the SYN
* packet is sent with the PUSH flag set.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest34 (void) {
Packet p;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
TcpReassemblyThreadCtx ra_ctx;
StreamMsgQueue stream_q;
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
memset(&stream_q, 0, sizeof(StreamMsgQueue));
memset(&ra_ctx, 0, sizeof(TcpReassemblyThreadCtx));
memset (&p, 0, SIZE_OF_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|TH_PUSH;
p.tcph = &tcph;
p.flowflags = FLOW_PKT_TOSERVER;
int ret = 0;
ra_ctx.stream_q = &stream_q;
stt.ra_ctx = &ra_ctx;
StreamTcpInitConfig(TRUE);
if (StreamTcpPacket(&tv, &p, &stt, &pq) == -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, &pq) == -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, &pq) == -1)
goto end;
if (((TcpSession *)(p.flow->protoctx))->state != TCP_ESTABLISHED) {
printf("Tcp session should have been establisehd\n");
goto end;
}
ret = 1;
end:
StreamTcpSessionClear(p.flow->protoctx);
StreamTcpFreeConfig(TRUE);
return ret;
}
/**
* \test Test the allocation of TCP session for a given packet when the SYN
* packet is sent with the URG flag set.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest35 (void) {
Packet p;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
TCPHdr tcph;
TcpReassemblyThreadCtx ra_ctx;
StreamMsgQueue stream_q;
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
memset(&stream_q, 0, sizeof(StreamMsgQueue));
memset(&ra_ctx, 0, sizeof(TcpReassemblyThreadCtx));
memset (&p, 0, SIZE_OF_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|TH_URG;
p.tcph = &tcph;
p.flowflags = FLOW_PKT_TOSERVER;
int ret = 0;
ra_ctx.stream_q = &stream_q;
stt.ra_ctx = &ra_ctx;
StreamTcpInitConfig(TRUE);
if (StreamTcpPacket(&tv, &p, &stt, &pq) == -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, &pq) == -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, &pq) == -1)
goto end;
if (((TcpSession *)(p.flow->protoctx))->state != TCP_ESTABLISHED) {
printf("Tcp session should have been establisehd\n");
goto end;
}
ret = 1;
end:
StreamTcpSessionClear(p.flow->protoctx);
StreamTcpFreeConfig(TRUE);
return ret;
}
/**
* \test Test the processing of PSH and URG flag in tcp session.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest36(void) {
Packet p;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
uint8_t payload[4];
TCPHdr tcph;
TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx();
int ret = 0;
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
memset (&p, 0, SIZE_OF_PACKET);
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof (StreamTcpThread));
memset(&tcph, 0, sizeof (TCPHdr));
stt.ra_ctx = ra_ctx;
p.flow = &f;
tcph.th_win = htons(5480);
tcph.th_flags = TH_SYN;
p.tcph = &tcph;
p.flowflags = FLOW_PKT_TOSERVER;
StreamTcpInitConfig(TRUE);
if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1) {
printf("failed in processing packet\n");
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, &pq) == -1 || (TcpSession *)p.flow->protoctx == NULL) {
printf("failed in processing packet\n");
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, &pq) == -1 || (TcpSession *)p.flow->protoctx == NULL) {
printf("failed in processing packet\n");
goto end;
}
if (((TcpSession *)p.flow->protoctx)->state != TCP_ESTABLISHED) {
printf("the TCP state should be TCP_ESTABLISEHD\n");
goto end;
}
p.tcph->th_ack = htonl(2);
p.tcph->th_seq = htonl(1);
p.tcph->th_flags = TH_PUSH | TH_ACK | TH_URG;
p.flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
p.payload = payload;
p.payload_len = 3;
if (StreamTcpPacket(&tv, &p, &stt, &pq) == -1 || (TcpSession *)p.flow->protoctx == NULL) {
printf("failed in processing packet\n");
goto end;
}
if (((TcpSession *)p.flow->protoctx)->client.next_seq != 4) {
printf("the ssn->client.next_seq should be 4, but it is %"PRIu32"\n",
((TcpSession *)p.flow->protoctx)->client.next_seq);
goto end;
}
StreamTcpSessionClear(p.flow->protoctx);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
return ret;
}
#endif
/**
* \test Test the processing of out of order FIN packets in tcp session.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest37(void) {
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
uint8_t payload[4];
TCPHdr tcph;
TcpReassemblyThreadCtx *ra_ctx = StreamTcpReassembleInitThreadCtx();
int ret = 0;
PacketQueue pq;
memset(&pq,0,sizeof(PacketQueue));
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof (StreamTcpThread));
memset(&tcph, 0, sizeof (TCPHdr));
FLOW_INITIALIZE(&f);
stt.ra_ctx = ra_ctx;
p->flow = &f;
tcph.th_win = htons(5480);
tcph.th_flags = TH_SYN;
p->tcph = &tcph;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpInitConfig(TRUE);
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
printf("failed in processing packet\n");
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, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL) {
printf("failed in processing packet\n");
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, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL) {
printf("failed in processing packet\n");
goto end;
}
if (((TcpSession *)p->flow->protoctx)->state != TCP_ESTABLISHED) {
printf("the TCP state should be TCP_ESTABLISEHD\n");
goto end;
}
p->tcph->th_ack = htonl(2);
p->tcph->th_seq = htonl(4);
p->tcph->th_flags = TH_ACK|TH_FIN;
p->flowflags = FLOW_PKT_TOSERVER;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL) {
printf("failed in processing packet\n");
goto end;
}
if (((TcpSession *)p->flow->protoctx)->state != TCP_CLOSE_WAIT) {
printf("the TCP state should be TCP_CLOSE_WAIT\n");
goto end;
}
p->tcph->th_ack = htonl(1);
p->tcph->th_seq = htonl(1);
p->tcph->th_flags = TH_PUSH | TH_ACK;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL) {
printf("failed in processing packet\n");
goto end;
}
p->tcph->th_ack = htonl(4);
p->tcph->th_seq = htonl(2);
p->tcph->th_flags = TH_ACK;
p->payload_len = 0;
p->flowflags = FLOW_PKT_TOCLIENT;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1 || (TcpSession *)p->flow->protoctx == NULL) {
printf("failed in processing packet\n");
goto end;
}
if (((TcpSession *)p->flow->protoctx)->client.ra_raw_base_seq != 3) {
printf("the ssn->client.next_seq should be 3, but it is %"PRIu32"\n",
((TcpSession *)p->flow->protoctx)->client.ra_raw_base_seq);
goto end;
}
StreamTcpSessionClear(p->flow->protoctx);
ret = 1;
end:
StreamTcpFreeConfig(TRUE);
SCFree(p);
return ret;
}
/**
* \test Test the validation of the ACK number before setting up the
* stream.last_ack.
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest38 (void) {
int ret = 0;
Flow f;
ThreadVars tv;
StreamTcpThread stt;
uint8_t payload[4];
TCPHdr tcph;
TcpReassemblyThreadCtx ra_ctx;
StreamMsgQueue stream_q;
PacketQueue pq;
memset(&stream_q, 0, sizeof(StreamMsgQueue));
memset(&ra_ctx, 0, sizeof(TcpReassemblyThreadCtx));
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof (StreamTcpThread));
memset(&tcph, 0, sizeof (TCPHdr));
memset(&pq,0,sizeof(PacketQueue));
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
p->flow = &f;
tcph.th_win = htons(5480);
tcph.th_flags = TH_SYN;
p->tcph = &tcph;
p->flowflags = FLOW_PKT_TOSERVER;
ra_ctx.stream_q = &stream_q;
stt.ra_ctx = &ra_ctx;
StreamTcpInitConfig(TRUE);
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
printf("failed in processing packet in StreamTcpPacket\n");
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, &pq) == -1) {
printf("failed in processing packet in StreamTcpPacket\n");
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, &pq) == -1) {
printf("failed in processing packet in StreamTcpPacket\n");
goto end;
}
p->tcph->th_ack = htonl(29847);
p->tcph->th_seq = htonl(2);
p->tcph->th_flags = TH_PUSH | TH_ACK;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
printf("failed in processing packet in StreamTcpPacket\n");
goto end;
}
/* last_ack value should be 1 as the previous sent ACK value is out of
window */
if (((TcpSession *)(p->flow->protoctx))->server.last_ack != 1) {
printf("the server.last_ack should be 1, but it is %"PRIu32"\n",
((TcpSession *)(p->flow->protoctx))->server.last_ack);
goto end;
}
p->tcph->th_ack = htonl(2984);
p->tcph->th_seq = htonl(5);
p->tcph->th_flags = TH_PUSH | TH_ACK;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
printf("failed in processing packet in StreamTcpPacket\n");
goto end;
}
/* last_ack value should be 2984 as the previous sent ACK value is inside
window */
if (((TcpSession *)(p->flow->protoctx))->server.last_ack != 2984) {
printf("the server.last_ack should be 2984, but it is %"PRIu32"\n",
((TcpSession *)(p->flow->protoctx))->server.last_ack);
goto end;
}
ret = 1;
end:
StreamTcpSessionClear(p->flow->protoctx);
StreamTcpFreeConfig(TRUE);
return ret;
}
/**
* \test Test the validation of the ACK number before setting up the
* stream.last_ack and update the next_seq after loosing the .
*
* \retval On success it returns 1 and on failure 0.
*/
static int StreamTcpTest39 (void) {
Flow f;
ThreadVars tv;
StreamTcpThread stt;
uint8_t payload[4];
TCPHdr tcph;
TcpReassemblyThreadCtx ra_ctx;
StreamMsgQueue stream_q;
PacketQueue pq;
memset(&stream_q, 0, sizeof(StreamMsgQueue));
memset(&ra_ctx, 0, sizeof(TcpReassemblyThreadCtx));
memset (&f, 0, sizeof(Flow));
memset(&tv, 0, sizeof (ThreadVars));
memset(&stt, 0, sizeof (StreamTcpThread));
memset(&tcph, 0, sizeof (TCPHdr));
memset(&pq,0,sizeof(PacketQueue));
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
memset(p, 0, SIZE_OF_PACKET);
p->pkt = (uint8_t *)(p + 1);
p->flow = &f;
tcph.th_win = htons(5480);
tcph.th_flags = TH_SYN;
p->tcph = &tcph;
p->flowflags = FLOW_PKT_TOSERVER;
int ret = 0;
ra_ctx.stream_q = &stream_q;
stt.ra_ctx = &ra_ctx;
StreamTcpInitConfig(TRUE);
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
printf("failed in processing packet in StreamTcpPacket\n");
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, &pq) == -1) {
printf("failed in processing packet in StreamTcpPacket\n");
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, &pq) == -1) {
printf("failed in processing packet in StreamTcpPacket\n");
goto end;
}
p->tcph->th_ack = htonl(2984);
p->tcph->th_seq = htonl(2);
p->tcph->th_flags = TH_PUSH | TH_ACK;
p->flowflags = FLOW_PKT_TOSERVER;
StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
printf("failed in processing packet in StreamTcpPacket\n");
goto end;
}
/* last_ack value should be 2984 as the previous sent ACK value is inside
window */
if (((TcpSession *)(p->flow->protoctx))->server.last_ack != 2984) {
printf("the server.last_ack should be 2984, but it is %"PRIu32"\n",
((TcpSession *)(p->flow->protoctx))->server.last_ack);
goto end;
}
p->tcph->th_seq = htonl(2984);
p->tcph->th_ack = htonl(5);
p->tcph->th_flags = TH_PUSH | TH_ACK;
p->flowflags = FLOW_PKT_TOCLIENT;
StreamTcpCreateTestPacket(payload, 0x41, 3, 4); /*AAA*/
p->payload = payload;
p->payload_len = 3;
if (StreamTcpPacket(&tv, p, &stt, &pq) == -1) {
printf("failed in processing packet in StreamTcpPacket\n");
goto end;
}
/* next_seq value should be 2987 as the previous sent ACK value is inside
window */
if (((TcpSession *)(p->flow->protoctx))->server.next_seq != 2987) {
printf("the server.next_seq should be 2987, but it is %"PRIu32"\n",
((TcpSession *)(p->flow->protoctx))->server.next_seq);
goto end;
}
ret = 1;
end:
StreamTcpSessionClear(p->flow->protoctx);
StreamTcpFreeConfig(TRUE);
return ret;
}
static int StreamTcpTest40(void) {
uint8_t raw_vlan[] = {
0x00, 0x20, 0x08, 0x00, 0x45, 0x00, 0x00, 0x34,
0x3b, 0x36, 0x40, 0x00, 0x40, 0x06, 0xb7, 0xc9,
0x83, 0x97, 0x20, 0x81, 0x83, 0x97, 0x20, 0x15,
0x04, 0x8a, 0x17, 0x70, 0x4e, 0x14, 0xdf, 0x55,
0x4d, 0x3d, 0x5a, 0x61, 0x80, 0x10, 0x6b, 0x50,
0x3c, 0x4c, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a,
0x00, 0x04, 0xf0, 0xc8, 0x01, 0x99, 0xa3, 0xf3
};
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
ThreadVars tv;
DecodeThreadVars dtv;
memset(&tv, 0, sizeof(ThreadVars));
PACKET_INITIALIZE(p);
SET_PKT_LEN(p, sizeof(raw_vlan));
memcpy(GET_PKT_DATA(p), raw_vlan, sizeof(raw_vlan));
memset(&dtv, 0, sizeof(DecodeThreadVars));
FlowInitConfig(FLOW_QUIET);
DecodeVLAN(&tv, &dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), NULL);
if(p->vlanh == NULL) {
SCFree(p);
return 0;
}
if(p->tcph == NULL) {
SCFree(p);
return 0;
}
Packet *np = StreamTcpPseudoSetup(p, GET_PKT_DATA(p), GET_PKT_LEN(p));
if (np == NULL) {
printf("the packet received from packet allocation is NULL: ");
return 0;
}
StreamTcpPseudoPacketSetupHeader(np,p);
if (((uint8_t *)p->tcph - (uint8_t *)p->ip4h) != ((uint8_t *)np->tcph - (uint8_t *)np->ip4h)) {
return 0;
}
FlowShutdown();
PACKET_CLEANUP(p);
return 1;
}
static int StreamTcpTest41(void) {
/* IPV6/TCP/no eth header */
uint8_t raw_ip[] = {
0x60, 0x00, 0x00, 0x00, 0x00, 0x28, 0x06, 0x40,
0x20, 0x01, 0x06, 0x18, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x51, 0x99, 0xcc, 0x70,
0x20, 0x01, 0x06, 0x18, 0x00, 0x01, 0x80, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
0x8c, 0x9b, 0x00, 0x50, 0x6a, 0xe7, 0x07, 0x36,
0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, 0x16, 0x30,
0x29, 0x9c, 0x00, 0x00, 0x02, 0x04, 0x05, 0x8c,
0x04, 0x02, 0x08, 0x0a, 0x00, 0xdd, 0x1a, 0x39,
0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x02 };
Packet *p = SCMalloc(SIZE_OF_PACKET);
if (p == NULL)
return 0;
ThreadVars tv;
DecodeThreadVars dtv;
memset(&dtv, 0, sizeof(DecodeThreadVars));
memset(&tv, 0, sizeof(ThreadVars));
PACKET_INITIALIZE(p);
if (PacketCopyData(p, raw_ip, sizeof(raw_ip)) == -1) {
PACKET_CLEANUP(p);
SCFree(p);
return 1;
}
FlowInitConfig(FLOW_QUIET);
DecodeRaw(&tv, &dtv, p, raw_ip, GET_PKT_LEN(p), NULL);
if (p->ip6h == NULL) {
printf("expected a valid ipv6 header but it was NULL: ");
FlowShutdown();
SCFree(p);
return 1;
}
if(p->tcph == NULL) {
SCFree(p);
return 0;
}
Packet *np = StreamTcpPseudoSetup(p, GET_PKT_DATA(p), GET_PKT_LEN(p));
if (np == NULL) {
printf("the packet received from packet allocation is NULL: ");
return 0;
}
StreamTcpPseudoPacketSetupHeader(np,p);
if (((uint8_t *)p->tcph - (uint8_t *)p->ip6h) != ((uint8_t *)np->tcph - (uint8_t *)np->ip6h)) {
return 0;
}
FlowShutdown();
PACKET_CLEANUP(p);
SCFree(p);
return 1;
}
#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);
UtRegisterTest("StreamTcpTest10 -- No missed packet Async stream",
StreamTcpTest10, 1);
UtRegisterTest("StreamTcpTest11 -- SYN missed Async stream",
StreamTcpTest11, 1);
UtRegisterTest("StreamTcpTest12 -- SYN/ACK missed Async stream",
StreamTcpTest12, 1);
UtRegisterTest("StreamTcpTest13 -- opposite stream packets for Async "
"stream", StreamTcpTest13, 1);
UtRegisterTest("StreamTcp4WHSTest01", StreamTcp4WHSTest01, 1);
UtRegisterTest("StreamTcp4WHSTest02", StreamTcp4WHSTest02, 1);
UtRegisterTest("StreamTcp4WHSTest03", StreamTcp4WHSTest03, 1);
UtRegisterTest("StreamTcpTest14 -- setup OS policy", StreamTcpTest14, 1);
UtRegisterTest("StreamTcpTest15 -- setup OS policy", StreamTcpTest15, 1);
UtRegisterTest("StreamTcpTest16 -- setup OS policy", StreamTcpTest16, 1);
UtRegisterTest("StreamTcpTest17 -- setup OS policy", StreamTcpTest17, 1);
UtRegisterTest("StreamTcpTest18 -- setup OS policy", StreamTcpTest18, 1);
UtRegisterTest("StreamTcpTest19 -- setup OS policy", StreamTcpTest19, 1);
UtRegisterTest("StreamTcpTest20 -- setup OS policy", StreamTcpTest20, 1);
UtRegisterTest("StreamTcpTest21 -- setup OS policy", StreamTcpTest21, 1);
UtRegisterTest("StreamTcpTest22 -- setup OS policy", StreamTcpTest22, 1);
UtRegisterTest("StreamTcpTest23 -- stream memory leaks", StreamTcpTest23, 1);
UtRegisterTest("StreamTcpTest24 -- stream memory leaks", StreamTcpTest24, 1);
UtRegisterTest("StreamTcpTest25 -- test ecn/cwr sessions",
StreamTcpTest25, 1);
UtRegisterTest("StreamTcpTest26 -- test ecn/cwr sessions",
StreamTcpTest26, 1);
UtRegisterTest("StreamTcpTest27 -- test ecn/cwr sessions",
StreamTcpTest27, 1);
UtRegisterTest("StreamTcpTest28 -- Memcap Test", StreamTcpTest28, 1);
#if 0 /* VJ 2010/09/01 disabled since they blow up on Fedora and Fedora is
* right about blowing up. The checksum functions are not used properly
* in the tests. */
UtRegisterTest("StreamTcpTest29 -- Badchecksum Reset Test", StreamTcpTest29, 1);
UtRegisterTest("StreamTcpTest30 -- Badchecksum Overlap Test", StreamTcpTest30, 1);
UtRegisterTest("StreamTcpTest31 -- MultipleSyns Test", StreamTcpTest31, 1);
UtRegisterTest("StreamTcpTest32 -- Bogus CWR Test", StreamTcpTest32, 1);
UtRegisterTest("StreamTcpTest33 -- RST-SYN Again Test", StreamTcpTest33, 1);
UtRegisterTest("StreamTcpTest34 -- SYN-PUSH Test", StreamTcpTest34, 1);
UtRegisterTest("StreamTcpTest35 -- SYN-URG Test", StreamTcpTest35, 1);
UtRegisterTest("StreamTcpTest36 -- PUSH-URG Test", StreamTcpTest36, 1);
#endif
UtRegisterTest("StreamTcpTest37 -- Out of order FIN Test", StreamTcpTest37, 1);
UtRegisterTest("StreamTcpTest38 -- validate ACK", StreamTcpTest38, 1);
UtRegisterTest("StreamTcpTest39 -- update next_seq", StreamTcpTest39, 1);
UtRegisterTest("StreamTcpTest40 -- pseudo setup", StreamTcpTest40, 1);
UtRegisterTest("StreamTcpTest41 -- pseudo setup", StreamTcpTest41, 1);
/* set up the reassembly tests as well */
StreamTcpReassembleRegisterTests();
StreamTcpSackRegisterTests ();
#endif /* UNITTESTS */
}