HeldPacket::HeldPacket(DAQ_Msg_h msg, uint32_t seq, const timeval& exp, TcpStreamTracker& trk)
: daq_msg(msg), seq_num(seq), expiration(exp), tracker(trk), expired(false)
-{
-}
+{ }
HeldPacketQueue::iter_t HeldPacketQueue::append(DAQ_Msg_h msg, uint32_t seq,
TcpStreamTracker& trk)
auto held_packet = q.begin();
if ( held_packet->has_expired(cur_time) )
{
- assert(held_packet == held_packet->get_tracker().held_packet);
held_packet->get_tracker().perform_partial_flush();
tcpStats.held_packet_timeouts++;
}
{
if ( srod.direction & SSN_DIR_FROM_SERVER )
{
- tcpssn->server.flush_policy = STREAM_FLPOLICY_IGNORE;
+ tcpssn->server.set_flush_policy(STREAM_FLPOLICY_IGNORE);
Stream::set_splitter(lwssn, true);
}
if ( srod.direction & SSN_DIR_FROM_CLIENT )
{
- tcpssn->client.flush_policy = STREAM_FLPOLICY_IGNORE;
+ tcpssn->client.set_flush_policy(STREAM_FLPOLICY_IGNORE);
Stream::set_splitter(lwssn, false);
}
}
// FIXIT-M PAF need to check for ips / on-data
if ( srod.direction & SSN_DIR_FROM_SERVER )
{
- tcpssn->server.flush_policy = STREAM_FLPOLICY_ON_ACK;
+ tcpssn->server.set_flush_policy(STREAM_FLPOLICY_ON_ACK);
Stream::set_splitter(lwssn, true, new AtomSplitter(true));
}
if ( srod.direction & SSN_DIR_FROM_CLIENT )
{
- tcpssn->client.flush_policy = STREAM_FLPOLICY_ON_ACK;
+ tcpssn->client.set_flush_policy(STREAM_FLPOLICY_ON_ACK);
Stream::set_splitter(lwssn, false, new AtomSplitter(false));
}
}
StreamSplitter* ss = Stream::get_splitter(flow, true);
CHECK( ( ss != nullptr ) );
CHECK( ( !ss->is_paf() ) );
- CHECK( ( ( ( TcpSession* )pkt->flow->session)->server.flush_policy
+ CHECK( ( ( ( TcpSession* )pkt->flow->session)->server.get_flush_policy()
== STREAM_FLPOLICY_IGNORE ) );
}
#endif
#define SEGMENT_OVERLAP_EDITOR_H
#include "normalize/normalize.h"
+#include "stream/paf.h"
#include "tcp_segment_node.h"
class TcpSession;
const uint8_t* rdata;
TcpSegmentList seglist;
- StreamPolicy reassembly_policy;
-
uint32_t seglist_base_seq; /* seq of first queued segment */
uint32_t seg_count; /* number of current queued segments */
uint32_t seg_bytes_total; /* total bytes currently queued */
uint16_t len;
uint16_t rsize;
int8_t tcp_ips_data;
+ StreamPolicy reassembly_policy;
bool keep_segment;
void init_soe(TcpSegmentDescriptor& tsd, TcpSegmentNode* left, TcpSegmentNode* right);
};
+/* Only track a maximum number of alerts per session */
+#define MAX_SESSION_ALERTS 8
+struct StreamAlertInfo
+{
+ /* For storing alerts that have already been seen on the session */
+ uint32_t gid;
+ uint32_t sid;
+ uint32_t seq;
+ // if we log extra data, event_* is used to correlate with alert
+ uint32_t event_id;
+ uint32_t event_second;
+};
+
struct TcpReassemblerState
{
SegmentOverlapState sos;
TcpStreamTracker* tracker;
uint32_t flush_count; // number of flushed queued segments
uint32_t xtradata_mask; // extra data available to log
- bool server_side;
+ StreamAlertInfo alerts[MAX_SESSION_ALERTS];
+ uint8_t alert_count = 0;
uint8_t ignore_dir;
uint8_t packet_dir;
+ bool server_side;
+ PAF_State paf_state;
};
class SegmentOverlapEditor
0,
API_RESERVED,
API_OPTIONS,
- MOD_NAME,
- MOD_HELP,
+ STREAM_TCP_MOD_NAME,
+ STREAM_TCP_MOD_HELP,
mod_ctor,
mod_dtor
},
RMT(a, rcv_nxt, b), RMT(a, r_win_base, b), a.get_iss(), a.get_irs());
fprintf(stdout, "\n");
- unsigned paf = (a.splitter and a.splitter->is_paf()) ? 2 : 0;
- unsigned fpt = a.flush_policy ? 192 : 0;
+ unsigned paf = ( a.is_splitter_paf() ) ? 2 : 0;
+ unsigned fpt = a.get_flush_policy() ? 192 : 0;
fprintf(stdout, " FP=%s:%-4u SC=%-4u FL=%-4u SL=%-5u BS=%-4u",
- flushxt[a.flush_policy + paf], fpt,
+ flushxt[a.get_flush_policy() + paf], fpt,
a.reassembler.get_seg_count(), a.reassembler.get_flush_count(),
a.reassembler.get_seg_bytes_logical(),
a.reassembler.get_seglist_base_seq() - b.get_iss());
/* actions */
#define ACTION_NOTHING 0x00000000
-#define ACTION_FLUSH_SENDER_STREAM 0x00000001
-#define ACTION_FLUSH_RECEIVER_STREAM 0x00000002
-#define ACTION_DROP_SESSION 0x00000004
-#define ACTION_ACK_SENDER_DATA 0x00000008
-#define ACTION_ACK_RECEIVER_DATA 0x00000010
-#define ACTION_SET_SSN 0x00000040
-#define ACTION_COMPLETE_TWH 0x00000080
-#define ACTION_RST 0x00000100
-#define ACTION_BAD_SEQ 0x00000200
-#define ACTION_BAD_PKT 0x00000400
-#define ACTION_LWSSN_CLOSED 0x00000800
-#define ACTION_DISABLE_INSPECTION 0x00001000
+#define ACTION_RST 0x00000001
+#define ACTION_BAD_PKT 0x00000002
+#define ACTION_LWSSN_CLOSED 0x00000004
+#define ACTION_DISABLE_INSPECTION 0x00000008
#define TF_NONE 0x0000
#define TF_WSCALE 0x0001
TcpEventLogger() = default;
void clear_tcp_events()
- {
- tcp_events = 0;
- }
+ { tcp_events = 0; }
void set_tcp_event(int eventcode)
- {
- tcp_events |= eventcode;
- }
+ { tcp_events |= eventcode; }
- void set_tcp_internal_syn_event();
void log_tcp_events();
void log_internal_event(uint32_t eventSid);
{ CountType::SUM, "restarts", "sessions restarted" },
{ CountType::SUM, "resyns", "SYN received on established session" },
{ CountType::SUM, "discards", "tcp packets discarded" },
+ { CountType::SUM, "discards_skipped", "tcp packet discards skipped due to normalization disabled" },
+ { CountType::SUM, "invalid_seq_num", "tcp packets received with an invalid sequence number" },
+ { CountType::SUM, "invalid_ack", "tcp packets received with an invalid ack number" },
+ { CountType::SUM, "no_flags_set", "tcp packets received with no TCP flags set" },
{ CountType::SUM, "events", "events generated" },
{ CountType::SUM, "ignored", "tcp packets ignored" },
{ CountType::SUM, "untracked", "tcp packets not tracked" },
"number of times the maximum queued segment limit was reached" },
{ CountType::SUM, "exceeded_max_bytes",
"number of times the maximum queued byte limit was reached" },
+ { CountType::SUM, "payload_fully_trimmed", "segments with no data after trimming" },
{ CountType::SUM, "internal_events", "135:X events generated" },
{ CountType::SUM, "client_cleanups",
"number of times data from server was flushed when session released" },
};
StreamTcpModule::StreamTcpModule() :
- Module(MOD_NAME, MOD_HELP, s_params)
+ Module(STREAM_TCP_MOD_NAME, STREAM_TCP_MOD_HELP, s_params)
{
config = nullptr;
}
switch ( index )
{
case 0:
- name = MOD_NAME;
+ name = STREAM_TCP_MOD_NAME;
parent = nullptr;
return &s5TcpPerfStats;
extern const PegInfo tcp_pegs[];
extern THREAD_LOCAL snort::ProfileStats s5TcpPerfStats;
-extern THREAD_LOCAL snort::ProfileStats streamSizePerfStats;
struct TcpStats
{
PegCount restarts;
PegCount resyns;
PegCount discards;
+ PegCount discards_skipped;
+ PegCount invalid_seq_num;
+ PegCount invalid_ack;
+ PegCount no_flags_set;
PegCount events;
PegCount ignored;
PegCount no_pickups;
PegCount gaps;
PegCount exceeded_max_segs;
PegCount exceeded_max_bytes;
+ PegCount payload_fully_trimmed;
PegCount internalEvents;
PegCount client_cleanups;
PegCount server_cleanups;
extern THREAD_LOCAL struct TcpStats tcpStats;
-inline void inc_tcp_discards()
-{
- tcpStats.discards++;
-}
-
//-------------------------------------------------------------------------
// stream_tcp module
//-------------------------------------------------------------------------
-#define MOD_NAME "stream_tcp"
-#define MOD_HELP "stream inspector for TCP flow tracking and stream normalization and reassembly"
+#define STREAM_TCP_MOD_NAME "stream_tcp"
+#define STREAM_TCP_MOD_HELP "stream inspector for TCP flow tracking and stream normalization and reassembly"
namespace snort
{
#include "tcp_normalizer.h"
+#include "tcp_module.h"
#include "tcp_stream_session.h"
#include "tcp_stream_tracker.h"
}
void TcpNormalizer::trim_payload(
- TcpNormalizerState&,
- TcpSegmentDescriptor& tsd, uint32_t max, NormMode mode, TcpPegCounts peg)
+ TcpNormalizerState&, TcpSegmentDescriptor& tsd, uint32_t max, NormMode mode, TcpPegCounts peg)
{
if (mode == NORM_MODE_ON)
{
}
bool TcpNormalizer::strip_tcp_timestamp(
- TcpNormalizerState&,
- TcpSegmentDescriptor& tsd, const tcp::TcpOption* opt, NormMode mode)
+ TcpNormalizerState&, TcpSegmentDescriptor& tsd, const tcp::TcpOption* opt, NormMode mode)
{
tcp_norm_stats[PC_TCP_TS_NOP][mode]++;
if (mode == NORM_MODE_ON)
{
tsd.drop_packet();
+ tcpStats.discards++;
return true;
}
+ tcpStats.discards_skipped++;
return false;
}
else
{
/* bail, we've got a packet outside the PAWS window! */
- //inc_tcp_discards();
tns.session->tel.set_tcp_event(EVENT_BAD_TIMESTAMP);
packet_dropper(tns, tsd, NORM_TCP_OPT);
return ACTION_BAD_PKT;
PAWS_24DAYS ) )
{
/* this packet is from way too far into the future */
- //inc_tcp_discards();
tns.session->tel.set_tcp_event(EVENT_BAD_TIMESTAMP);
packet_dropper(tns, tsd, NORM_TCP_OPT);
return ACTION_BAD_PKT;
( tns.tracker->get_tf_flags() & TF_TSTAMP ) )
{
tns.session->tel.set_tcp_event(EVENT_BAD_TIMESTAMP);
+ packet_dropper(tns, tsd, NORM_TCP_BLOCK);
return ACTION_BAD_PKT;
}
}
return ACTION_RST;
}
else
- {
- inc_tcp_discards();
return ACTION_NOTHING;
- }
}
static inline int handle_repeated_syn_bsd(
return ACTION_RST;
}
else
- {
- inc_tcp_discards();
return ACTION_NOTHING;
- }
}
// Linux, Win2k3 et al. do not support timestamps if the 3whs used a 0 timestamp.
TcpNormalizerState&, TcpSegmentDescriptor&)
{
/* MACOS ignores a 2nd SYN, regardless of the sequence number. */
- inc_tcp_discards();
return ACTION_NOTHING;
}
{
// needed by stream_reassemble:action disable; can fire on rebuilt
// packets, yanking the splitter out from under us :(
- if (!trs.tracker->flush_policy or !trs.tracker->splitter)
+ if ( !trs.tracker->is_reassembly_enabled() )
return false;
- if ( (trs.tracker->flush_policy == STREAM_FLPOLICY_ON_DATA) or
- trs.tracker->splitter->is_paf() )
+ if ( (trs.tracker->get_flush_policy() == STREAM_FLPOLICY_ON_DATA) or trs.tracker->is_splitter_paf() )
return ( is_segment_pending_flush(trs) );
return ( get_pending_segment_count(trs, 2) > 1 ); // FIXIT-L return false?
const int32_t new_size = len - slide - trunc_len;
assert(new_size >= 0);
+ // if trimming will delete all data, don't insert this segment in the queue
if ( new_size <= 0 )
{
- // Zero size data because of trimming. Don't insert it.
- inc_tcp_discards();
+ tcpStats.payload_fully_trimmed++;
trs.tracker->normalizer.trim_win_payload(tsd);
return;
}
*retSeg = tsn;
}
+bool TcpReassembler::add_alert(TcpReassemblerState& trs, uint32_t gid, uint32_t sid)
+{
+ if ( trs.alert_count >= MAX_SESSION_ALERTS)
+ return false;
+
+ StreamAlertInfo* ai = trs.alerts + trs.alert_count;
+ ai->gid = gid;
+ ai->sid = sid;
+ ai->seq = 0;
+ ai->event_id = 0;
+ ai->event_second = 0;
+
+ trs.alert_count++;
+
+ return true;
+}
+
+bool TcpReassembler::check_alerted(TcpReassemblerState& trs, uint32_t gid, uint32_t sid)
+{
+ for (int i = 0; i < trs.alert_count; i++)
+ {
+ /* This is a rebuilt packet and if we've seen this alert before,
+ * return that we have previously alerted on original packet.
+ */
+ if (trs.alerts[i].gid == gid && trs.alerts[i].sid == sid)
+ return true;
+ }
+
+ return false;
+}
+
+int TcpReassembler::update_alert(TcpReassemblerState& trs, uint32_t gid, uint32_t sid,
+ uint32_t event_id, uint32_t event_second)
+{
+ // FIXIT-M comparison of seq_num is wrong, compare value is always 0, should be seq_num of wire packet
+ uint32_t seq_num = 0;
+
+ for (unsigned i = 0; i < trs.alert_count; i++)
+ {
+ StreamAlertInfo* ai = &trs.alerts[i];
+
+ if (ai->gid == gid && ai->sid == sid && SEQ_EQ(ai->seq, seq_num))
+ {
+ ai->event_id = event_id;
+ ai->event_second = event_second;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
void TcpReassembler::purge_alerts(TcpReassemblerState& trs)
{
Flow* flow = trs.sos.session->flow;
- for (int i = 0; i < trs.tracker->alert_count; i++)
+ for (int i = 0; i < trs.alert_count; i++)
{
- StreamAlertInfo* ai = trs.tracker->alerts + i;
+ StreamAlertInfo* ai = trs.alerts + i;
Stream::log_extra_data(flow, trs.xtradata_mask, ai->event_id, ai->event_second);
}
if ( !flow->is_suspended() )
- trs.tracker->alert_count = 0;
+ trs.alert_count = 0;
}
void TcpReassembler::purge_to_seq(TcpReassemblerState& trs, uint32_t flush_seq)
unsigned int flush_len = ( tsn->c_len <= max ) ? tsn->c_len : max;
// copy only to flush point
- if ( paf_active(&trs.tracker->paf_state) && SEQ_GT(tsn->c_seq + flush_len, to_seq) )
+ if ( paf_active(&trs.paf_state) && SEQ_GT(tsn->c_seq + flush_len, to_seq) )
flush_len = to_seq - tsn->c_seq;
return flush_len;
TcpSegmentNode* tsn = trs.sos.seglist.cur_rseg;
unsigned bytes_copied = 0;
unsigned bytes_to_copy = get_flush_data_len(
- trs, tsn, to_seq, trs.tracker->splitter->max(p->flow));
+ trs, tsn, to_seq, trs.tracker->get_splitter()->max(p->flow));
assert(bytes_to_copy);
if ( !tsn->next or (bytes_to_copy < tsn->c_len) or
SEQ_EQ(tsn->c_seq + bytes_to_copy, to_seq) or
- (total_flushed + tsn->c_len > trs.tracker->splitter->get_max_pdu()) )
+ (total_flushed + tsn->c_len > trs.tracker->get_splitter()->get_max_pdu()) )
{
flags |= PKT_PDU_TAIL;
}
- const StreamBuffer sb = trs.tracker->splitter->reassemble(
+ const StreamBuffer sb = trs.tracker->get_splitter()->reassemble(
trs.sos.session->flow, total, total_flushed, tsn->payload(),
bytes_to_copy, flags, bytes_copied);
if ( footprint > Packet::max_dsize ) // max stream buffer size
footprint = Packet::max_dsize;
- if ( trs.tracker->splitter->is_paf() and
- ( trs.tracker->get_tf_flags() & TF_MISSING_PREV_PKT ) )
+ if ( trs.tracker->is_splitter_paf()
+ and ( trs.tracker->get_tf_flags() & TF_MISSING_PREV_PKT ) )
fallback(*trs.tracker, trs.server_side);
Packet* pdu = initialize_pdu(trs, p, pkt_flags, trs.sos.seglist.cur_rseg->tv);
}
// FIXIT-L must check because above may clear trs.sos.session
- if ( trs.tracker->splitter )
- trs.tracker->splitter->update();
+ if ( trs.tracker->get_splitter() )
+ trs.tracker->get_splitter()->update();
// FIXIT-L abort should be by PAF callback only since recovery may be possible
if ( trs.tracker->get_tf_flags() & TF_MISSING_PKT )
return 0;
if ( !flush_data_ready(trs) and !(trs.tracker->get_tf_flags() & TF_FORCE_FLUSH) and
- !trs.tracker->splitter->is_paf() )
+ !trs.tracker->is_splitter_paf() )
return 0;
trs.tracker->clear_tf_flags(TF_MISSING_PKT | TF_MISSING_PREV_PKT);
{
unsigned bytes_copied = 0;
- const StreamBuffer sb = trs.tracker->splitter->reassemble(
+ const StreamBuffer sb = trs.tracker->get_splitter()->reassemble(
trs.sos.session->flow, 0, 0, nullptr, 0, (PKT_PDU_HEAD | PKT_PDU_TAIL), bytes_copied);
if ( sb.data )
show_rebuilt_packet(trs, pdu);
Analyzer::get_local_analyzer()->inspect_rebuilt(pdu);
- if ( trs.tracker->splitter )
- trs.tracker->splitter->update();
+ if ( trs.tracker->get_splitter() )
+ trs.tracker->get_splitter()->update();
}
return bytes_copied;
trs.sos.seglist.cur_rseg = tsn;
}
uint32_t len = 0;
- const uint32_t limit = trs.tracker->splitter->get_max_pdu();
+ const uint32_t limit = trs.tracker->get_splitter()->get_max_pdu();
while ( len < limit and next_no_gap(*tsn) )
{
TcpReassemblerState& trs, Packet* p, uint32_t dir, bool final_flush)
{
// this is not always redundant; stream_reassemble rule option causes trouble
- if ( !trs.tracker->flush_policy or !trs.tracker->splitter )
+ if ( !trs.tracker->is_reassembly_enabled() )
return 0;
uint32_t bytes;
p = set_packet(flow, trs.packet_dir, trs.server_side);
}
- bool pending = clear and paf_initialized(&trs.tracker->paf_state)
- and (!trs.tracker->splitter or trs.tracker->splitter->finish(flow) );
+ bool pending = clear and paf_initialized(&trs.paf_state)
+ and (!trs.tracker->get_splitter() or trs.tracker->get_splitter()->finish(flow) );
if ( pending and !(flow->ssn_state.ignore_direction & trs.ignore_dir) )
final_flush(trs, p, trs.packet_dir);
if ( !tsn )
tsn = trs.sos.seglist.cur_rseg;
- else if ( paf_initialized(&trs.tracker->paf_state) )
+ else if ( paf_initialized(&trs.paf_state) )
{
assert(trs.sos.seglist.cur_rseg);
total = tsn->c_seq - trs.sos.seglist.cur_rseg->c_seq;
total += tsn->c_len;
uint32_t end = tsn->c_seq + tsn->c_len;
- uint32_t pos = paf_position(&trs.tracker->paf_state);
+ uint32_t pos = paf_position(&trs.paf_state);
- if ( paf_initialized(&trs.tracker->paf_state) && SEQ_LEQ(end, pos) )
+ if ( paf_initialized(&trs.paf_state) && SEQ_LEQ(end, pos) )
{
if ( !next_no_gap(*tsn) )
return -1;
}
int32_t flush_pt = paf_check(
- trs.tracker->splitter, &trs.tracker->paf_state, p, tsn->payload(),
+ trs.tracker->get_splitter(), &trs.paf_state, p, tsn->payload(),
tsn->c_len, total, tsn->c_seq, flags);
if (flush_pt >= 0)
static inline void fallback(TcpStreamTracker& trk, bool server_side, uint16_t max)
{
- delete trk.splitter;
- trk.splitter = new AtomSplitter(!server_side, max);
- trk.paf_state.paf = StreamSplitter::START;
+ trk.set_splitter(new AtomSplitter(!server_side, max));
+ trk.reassembler.reset_paf();
tcpStats.partial_fallbacks++;
}
{
uint32_t size = tsn->c_len;
uint32_t end = tsn->c_seq + tsn->c_len;
- uint32_t pos = paf_position(&trs.tracker->paf_state);
+ uint32_t pos = paf_position(&trs.paf_state);
- if ( paf_initialized(&trs.tracker->paf_state) && SEQ_LEQ(end, pos) )
+ if ( paf_initialized(&trs.paf_state) && SEQ_LEQ(end, pos) )
{
total += size;
tsn = tsn->next;
total += size;
int32_t flush_pt = paf_check(
- trs.tracker->splitter, &trs.tracker->paf_state, p, tsn->payload(),
+ trs.tracker->get_splitter(), &trs.paf_state, p, tsn->payload(),
size, total, tsn->c_seq, flags);
if ( flush_pt >= 0 )
// instead of creating more, but smaller, packets
// FIXIT-L just flush to end of segment to avoid splitting
// instead of all avail?
- if ( !trs.tracker->splitter->is_paf() )
+ if ( !trs.tracker->is_splitter_paf() )
{
// get_q_footprint() w/o side effects
int32_t avail = trs.tracker->r_win_base - trs.sos.seglist_base_seq;
if ( avail > flush_pt )
{
- paf_jump(&trs.tracker->paf_state, avail - flush_pt);
+ paf_jump(&trs.paf_state, avail - flush_pt);
return avail;
}
}
uint32_t flushed = 0;
last_pdu = nullptr;
- switch ( trs.tracker->flush_policy )
+ switch ( trs.tracker->get_flush_policy() )
{
case STREAM_FLPOLICY_IGNORE:
return 0;
flush_amt = flush_pdu_ips(trs, &flags, p);
}
- if ( !flags && trs.tracker->splitter->is_paf() )
+ if ( !flags && trs.tracker->is_splitter_paf() )
{
fallback(*trs.tracker, trs.server_side);
return flush_on_data_policy(trs, p);
if ( trs.tracker->is_retransmit_of_held_packet(p) )
flushed = perform_partial_flush(trs, p, flushed);
- // FIXIT-H a drop rule will yoink the seglist out from under us
+ // FIXIT-M a drop rule will yoink the seglist out from under us
// because apply_delayed_action is only deferred to end of context
// this is causing stability issues
if ( flushed and trs.sos.seg_count and
uint32_t flushed = 0;
last_pdu = nullptr;
- switch (trs.tracker->flush_policy)
+ switch ( trs.tracker->get_flush_policy() )
{
case STREAM_FLPOLICY_IGNORE:
return 0;
if ( !flush_amt )
flush_amt = trs.sos.seglist.cur_rseg->c_seq - trs.sos.seglist_base_seq;
- if ( trs.tracker->paf_state.paf == StreamSplitter::ABORT )
- trs.tracker->splitter->finish(p->flow);
+ if ( trs.paf_state.paf == StreamSplitter::ABORT )
+ trs.tracker->get_splitter()->finish(p->flow);
// for consistency with other cases, should return total
// but that breaks flushing pipelined pdus
break; // bail if nothing flushed
}
- if ( (trs.tracker->paf_state.paf == StreamSplitter::ABORT) &&
- (trs.tracker->splitter && trs.tracker->splitter->is_paf()) )
+ if ( (trs.paf_state.paf == StreamSplitter::ABORT) && trs.tracker->is_splitter_paf() )
{
fallback(*trs.tracker, trs.server_side);
return flush_on_ack_policy(trs, p);
// are not null.
uint32_t TcpReassembler::perform_partial_flush(TcpReassemblerState& trs, Packet* p, uint32_t flushed)
{
- if ( trs.tracker->splitter->init_partial_flush(p->flow) )
+ if ( trs.tracker->get_splitter()->init_partial_flush(p->flow) )
{
flushed += flush_stream(trs, p, trs.packet_dir, false);
- paf_jump(&trs.tracker->paf_state, flushed);
+ paf_jump(&trs.paf_state, flushed);
tcpStats.partial_flushes++;
tcpStats.partial_flush_bytes += flushed;
if ( trs.sos.seg_count )
virtual int flush_on_data_policy(TcpReassemblerState&, snort::Packet*);
virtual int flush_on_ack_policy(TcpReassemblerState&, snort::Packet*);
virtual void trace_segments(TcpReassemblerState&);
+ virtual bool add_alert(TcpReassemblerState&, uint32_t gid, uint32_t sid);
+ virtual bool check_alerted(TcpReassemblerState&, uint32_t gid, uint32_t sid);
+ virtual int update_alert(TcpReassemblerState&, uint32_t gid, uint32_t sid,
+ uint32_t event_id, uint32_t event_second);
virtual void purge_alerts(TcpReassemblerState&);
uint32_t perform_partial_flush(TcpReassemblerState&, snort::Flow*);
void queue_packet_for_reassembly(TcpSegmentDescriptor& tsd)
{ reassembler->queue_packet_for_reassembly(trs, tsd); }
+ bool add_alert(uint32_t gid, uint32_t sid)
+ { return reassembler->add_alert(trs, gid, sid); }
+
+ bool check_alerted(uint32_t gid, uint32_t sid)
+ { return reassembler->check_alerted(trs, gid, sid); }
+
+ int update_alert(uint32_t gid, uint32_t sid, uint32_t event_id, uint32_t event_second)
+ { return reassembler->update_alert(trs, gid, sid, event_id, event_second); }
+
void purge_alerts()
{ reassembler->purge_alerts(trs); }
uint32_t perform_partial_flush(snort::Flow* flow)
{ return reassembler->perform_partial_flush(trs, flow); }
+ void reset_paf()
+ { paf_reset(&trs.paf_state); }
+
+ void clear_paf()
+ { paf_clear(&trs.paf_state); }
+
+ void setup_paf()
+ { paf_setup(&trs.paf_state); }
+
private:
TcpReassembler* reassembler = nullptr;
TcpReassemblerState trs;
pkt->dsize = seg_len;
}
+ bool is_data_segment() const
+ { return pkt->dsize > 0; }
+
void update_len(int32_t offset)
{
assert(!meta_ack_packet);
memory::MemoryCap::update_deallocations(sizeof(*this));
}
-bool TcpSession::setup(Packet* p)
+bool TcpSession::setup(Packet*)
{
- TcpStreamSession::setup(p);
+ client.init_tcp_state();
+ server.init_tcp_state();
+ lws_init = tcp_init = false;
+ generate_3whs_alert = true;
+ cleaning = false;
splitter_init = false;
+ pkt_action_mask = ACTION_NOTHING;
+ ecn = 0;
+ ingress_index = egress_index = 0;
+ ingress_group = egress_group = 0;
+ daq_flags = address_space_id = 0;
+
tcp_config = get_tcp_cfg(flow->ssn_server);
flow->set_default_session_timeout(tcp_config->session_timeout, false);
set_os_policy();
if ( restart )
{
flow->restart(free_flow_data);
- paf_reset(&client.paf_state);
- paf_reset(&server.paf_state);
-
+ client.reassembler.reset_paf();
+ server.reassembler.reset_paf();
}
else
{
flow->clear(free_flow_data);
- paf_clear(&client.paf_state);
- paf_clear(&server.paf_state);
+ client.reassembler.clear_paf();
+ server.reassembler.clear_paf();
}
set_splitter(true, nullptr);
{
TcpStreamTracker* listener = tsd.get_listener();
- if ( listener->flush_policy == STREAM_FLPOLICY_IGNORE )
+ if ( listener->get_flush_policy() == STREAM_FLPOLICY_IGNORE )
return true;
// FIXIT-M any discards must be counted and in many cases alerted as well
else
listener->order = 1;
break;
+
case 1:
if ( aligned )
{
listener->order = 2;
}
break;
+
default:
if ( aligned )
tsd.set_packet_flags(PKT_STREAM_ORDER_OK);
if (seq == listener->rcv_nxt)
{
/* check if we're in the window */
- if (tcp_config->policy != StreamPolicy::OS_PROXY
- and listener->normalizer.get_stream_window(tsd) == 0)
+ if ( tcp_config->policy != StreamPolicy::OS_PROXY
+ and listener->normalizer.get_stream_window(tsd) == 0 )
{
listener->normalizer.trim_win_payload(tsd);
return STREAM_UNALIGNED;
// FIXIT-L for ips, must move all the way to first hole or right end
listener->rcv_nxt = tsd.get_end_seq();
- if (tsd.get_len() != 0)
+ if ( tsd.is_data_segment() )
{
update_stream_order(tsd, true);
process_tcp_stream(tsd);
// some cases.
/* check if we're in the window */
- if (tcp_config->policy != StreamPolicy::OS_PROXY
- and listener->normalizer.get_stream_window(tsd) == 0)
+ if ( tcp_config->policy != StreamPolicy::OS_PROXY
+ and listener->normalizer.get_stream_window(tsd) == 0 )
{
listener->normalizer.trim_win_payload(tsd);
return STREAM_UNALIGNED;
}
- if (tsd.get_len() != 0)
+ if ( tsd.is_data_segment() )
{
update_stream_order(tsd, false);
process_tcp_stream(tsd);
/* Got SYN/RST. We're done. */
listener->normalizer.trim_syn_payload(tsd);
listener->normalizer.trim_rst_payload(tsd);
- pkt_action_mask |= ACTION_RST;
+ set_pkt_action_flag(ACTION_RST);
return false;
}
else if ( tcph->is_syn_only() )
}
tsd.set_packet_flags(PKT_IGNORE);
- pkt_action_mask |= ACTION_DISABLE_INSPECTION;
+ set_pkt_action_flag(ACTION_DISABLE_INSPECTION);
tcpStats.ignored++;
}
}
{
listener->normalizer.trim_syn_payload(tsd);
tel.set_tcp_event(EVENT_DATA_ON_SYN);
- pkt_action_mask |= ACTION_BAD_PKT;
+ set_pkt_action_flag(ACTION_BAD_PKT);
}
}
{
/* got a window too large, alert! */
tel.set_tcp_event(EVENT_WINDOW_TOO_LARGE);
- inc_tcp_discards();
listener->normalizer.packet_dropper(tsd, NORM_TCP_BLOCK);
- pkt_action_mask |= ACTION_BAD_PKT;
+ set_pkt_action_flag(ACTION_BAD_PKT);
return true;
}
else if ( tsd.is_packet_from_client() && (tsd.get_wnd() <= SLAM_MAX)
{
/* got a window slam alert! */
tel.set_tcp_event(EVENT_WINDOW_SLAM);
- inc_tcp_discards();
-
- if ( listener->normalizer.packet_dropper(tsd, NORM_TCP_BLOCK) )
- {
- pkt_action_mask |= ACTION_BAD_PKT;
- return true;
- }
+ listener->normalizer.packet_dropper(tsd, NORM_TCP_BLOCK);
+ set_pkt_action_flag(ACTION_BAD_PKT);
+ return true;
}
return false;
// FIXIT-M move this to normalizer base class, handle OS_PROXY in derived class
if ( tcp_config->policy != StreamPolicy::OS_PROXY )
{
- /* check for valid sequence/retrans */
+ // drop packet if sequence num is invalid
if ( !listener->is_segment_seq_valid(tsd) )
+ {
+ tcpStats.invalid_seq_num++;
+ listener->normalizer.trim_win_payload(tsd);
return;
+ }
// these normalizations can't be done if we missed setup. and
// window is zero in one direction until we've seen both sides.
if ( !(flow->get_session_flags() & SSNFLAG_MIDSTREAM) && flow->two_way_traffic() )
{
- // sender of syn w/mss limits payloads from peer since we store mss on
- // sender side, use listener mss same reasoning for window size
- TcpStreamTracker* st = listener;
+ // trim to fit in listener's window and mss
+ listener->normalizer.trim_win_payload
+ (tsd, (listener->r_win_base + listener->get_snd_wnd() - listener->rcv_nxt));
- // trim to fit in window and mss as needed
- st->normalizer.trim_win_payload(
- tsd, (st->r_win_base + st->get_snd_wnd() - st->rcv_nxt));
+ if ( listener->get_mss() )
+ listener->normalizer.trim_mss_payload(tsd, listener->get_mss());
- // FIXIT-H: MSS is not set on client so packets sent to client are not trimmed
- // use case?
- if ( st->get_mss() )
- st->normalizer.trim_mss_payload(tsd, st->get_mss());
-
- st->normalizer.ecn_stripper(tsd);
+ listener->normalizer.ecn_stripper(tsd);
}
}
- // dunno if this is RFC but fragroute testing expects it for the record,
- // I've seen FTP data sessions that send data packets with no tcp flags set
- if ( (tsd.get_tcph()->th_flags != 0) or (tcp_config->policy == StreamPolicy::OS_LINUX)
- or (tcp_config->policy == StreamPolicy::OS_PROXY) )
- {
- process_tcp_data(tsd);
- }
- else
- {
- tel.set_tcp_event(EVENT_DATA_WITHOUT_FLAGS);
- listener->normalizer.packet_dropper(tsd, NORM_TCP_BLOCK);
- }
+ process_tcp_data(tsd);
}
listener->reassembler.flush_on_data_policy(tsd.get_pkt());
if ( !SEQ_EQ(tsd.get_seq(), talker->get_iss()) and
listener->normalizer.packet_dropper(tsd, NORM_TCP_BLOCK) )
{
- action = ACTION_BAD_PKT;
+ set_pkt_action_flag(ACTION_BAD_PKT);
}
else if ( talker->get_tcp_state() >= TcpStreamTracker::TCP_ESTABLISHED and
talker->get_tcp_state() < TcpStreamTracker::TCP_CLOSED )
void TcpSession::flush_tracker(
TcpStreamTracker& tracker, Packet* p, uint32_t dir, bool final_flush)
{
- if ( final_flush && (!tracker.splitter || !tracker.splitter->finish(flow)) )
+ if ( final_flush && (!tracker.get_splitter() || !tracker.get_splitter()->finish(flow)) )
return;
tracker.set_tf_flags(TF_FORCE_FLUSH);
void TcpSession::check_events_and_actions(const TcpSegmentDescriptor& tsd)
{
+ tel.log_tcp_events();
+
if ( tsd.is_meta_ack_packet() )
return;
- tel.log_tcp_events();
-
Packet* p = tsd.get_pkt();
if ( !(pkt_action_mask & ACTION_LWSSN_CLOSED) )
{
if ( flow->ssn_state.ignore_direction != SSN_DIR_NONE )
{
- server.flush_policy = STREAM_FLPOLICY_IGNORE;
- client.flush_policy = STREAM_FLPOLICY_IGNORE;
+ server.set_flush_policy(STREAM_FLPOLICY_IGNORE);
+ client.set_flush_policy(STREAM_FLPOLICY_IGNORE);
return true;
}
void TcpSession::init_tcp_packet_analysis(TcpSegmentDescriptor& tsd)
{
- if ( !splitter_init and tsd.get_len() > 0 )
+ if ( !splitter_init and tsd.is_data_segment() )
{
if ( !(tcp_config->flags & STREAM_CONFIG_NO_REASSEMBLY) )
{
if ( tsd.is_policy_inline() )
if ( tsd.get_tcph()->is_ack() && !listener->is_ack_valid(tsd.get_ack()) )
- pkt_action_mask |= ACTION_BAD_PKT;
+ {
+ listener->normalizer.packet_dropper(tsd, NORM_TCP_BLOCK);
+ set_pkt_action_flag(ACTION_BAD_PKT);
+ }
if ( !tsd.is_meta_ack_packet() )
- pkt_action_mask |= listener->normalizer.handle_paws(tsd);
+ set_pkt_action_flag(listener->normalizer.handle_paws(tsd));
return ( pkt_action_mask & ACTION_BAD_PKT ) ? false : true;
}
int TcpSession::process_tcp_packet(TcpSegmentDescriptor& tsd)
{
- if ( tsm->eval(tsd) )
- {
- check_events_and_actions(tsd);
- S5TraceTCP(tsd);
- }
- else
- {
- if ( pkt_action_mask & ACTION_BAD_PKT )
- {
- inc_tcp_discards();
- check_events_and_actions(tsd);
- }
+ tsm->eval(tsd);
+ check_events_and_actions(tsd);
- tel.log_tcp_events();
- S5TraceTCP(tsd);
- }
+ S5TraceTCP(tsd);
return ACTION_NOTHING;
}
TcpStateCloseWait::TcpStateCloseWait(TcpStateMachine& tsm) :
TcpStateHandler(TcpStreamTracker::TCP_CLOSE_WAIT, tsm)
-{
-}
+{ }
bool TcpStateCloseWait::syn_recv(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
trk.normalizer.ecn_tracker(tsd.get_tcph(), trk.session->tcp_config->require_3whs() );
- if ( tsd.get_len() )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_on_syn(tsd);
return true;
{
if ( !flow->two_way_traffic() )
trk.set_tf_flags(TF_FORCE_FLUSH);
- if ( tsd.get_len() > 0 )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_segment(tsd);
}
+
return true;
}
trk.session->set_pkt_action_flag(ACTION_RST);
tsd.get_flow()->session_state |= STREAM_STATE_CLOSED;
}
- else
- {
- trk.session->tel.set_tcp_event(EVENT_BAD_RST);
- }
+
return true;
}
TcpStateClosed::TcpStateClosed(TcpStateMachine& tsm) :
TcpStateHandler(TcpStreamTracker::TCP_CLOSED, tsm)
-{
-}
+{ }
bool TcpStateClosed::syn_sent(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
{
trk.update_tracker_ack_recv(tsd);
- if( tsd.get_len() > 0 )
+ if( tsd.is_data_segment() )
{
if ( trk.is_rst_pkt_sent() )
trk.session->tel.set_tcp_event(EVENT_DATA_AFTER_RESET);
trk.session->update_perf_base_state(TcpStreamTracker::TCP_CLOSING);
trk.session->set_pkt_action_flag(ACTION_RST);
}
- else
- {
- trk.session->tel.set_tcp_event(EVENT_BAD_RST);
- }
+
return true;
}
TcpStateClosing::TcpStateClosing(TcpStateMachine& tsm) :
TcpStateHandler(TcpStreamTracker::TCP_CLOSING, tsm)
-{
-}
+{ }
bool TcpStateClosing::syn_sent(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
bool TcpStateClosing::syn_recv(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
trk.normalizer.ecn_tracker(tsd.get_tcph(), trk.session->tcp_config->require_3whs());
- if ( tsd.get_len() )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_on_syn(tsd);
return true;
}
trk.session->set_pkt_action_flag(ACTION_RST);
tsd.get_flow()->session_state |= STREAM_STATE_CLOSED;
}
- else
- {
- trk.session->tel.set_tcp_event(EVENT_BAD_RST);
- }
+
return true;
}
TcpStateEstablished::TcpStateEstablished(TcpStateMachine& tsm) :
TcpStateHandler(TcpStreamTracker::TCP_ESTABLISHED, tsm)
-{
-}
+{ }
bool TcpStateEstablished::syn_sent(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
bool TcpStateEstablished::fin_recv(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
trk.update_tracker_ack_recv(tsd);
- if ( tsd.get_len() > 0 )
- {
+ if ( tsd.is_data_segment() )
trk.session->handle_data_segment(tsd);
- trk.flush_data_on_fin_recv(tsd);
- }
if ( trk.update_on_fin_recv(tsd) )
{
trk.session->update_perf_base_state(TcpStreamTracker::TCP_CLOSING);
trk.session->set_pkt_action_flag(ACTION_RST);
}
- else
- {
- trk.session->tel.set_tcp_event(EVENT_BAD_RST);
- }
// FIXIT-L might be good to create alert specific to RST with data
- if ( tsd.get_len() > 0 )
+ if ( tsd.is_data_segment() )
trk.session->tel.set_tcp_event(EVENT_DATA_AFTER_RST_RCVD);
return true;
TcpStateFinWait1::TcpStateFinWait1(TcpStateMachine& tsm) :
TcpStateHandler(TcpStreamTracker::TCP_FIN_WAIT1, tsm)
-{
-}
+{ }
bool TcpStateFinWait1::syn_sent(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
bool TcpStateFinWait1::syn_recv(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
trk.normalizer.ecn_tracker(tsd.get_tcph(), trk.session->tcp_config->require_3whs());
- if ( tsd.get_len() )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_on_syn(tsd);
return true;
}
bool TcpStateFinWait1::syn_ack_recv(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
- if ( tsd.get_len() )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_on_syn(tsd);
return true;
}
trk.update_tracker_ack_recv(tsd);
if ( check_for_window_slam(tsd, trk) )
{
- if ( tsd.get_len() > 0 )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_segment(tsd);
}
return true;
bool is_ack_valid = false;
if ( check_for_window_slam(tsd, trk, &is_ack_valid) )
{
- if ( tsd.get_len() > 0 )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_segment(tsd);
if ( !flow->two_way_traffic() )
trk.session->set_pkt_action_flag(ACTION_RST);
tsd.get_flow()->session_state |= STREAM_STATE_CLOSED;
}
- else
- {
- trk.session->tel.set_tcp_event(EVENT_BAD_RST);
- }
// FIXIT-L might be good to create alert specific to RST with data
- if ( tsd.get_len() > 0 )
+ if ( tsd.is_data_segment() )
trk.session->tel.set_tcp_event(EVENT_DATA_AFTER_RST_RCVD);
return true;
}
&& (tsd.get_wnd() == 0))
{
trk.session->tel.set_tcp_event(EVENT_WINDOW_SLAM);
- inc_tcp_discards();
-
- if (trk.normalizer.packet_dropper(tsd, NORM_TCP_BLOCK))
- {
- trk.session->set_pkt_action_flag(ACTION_BAD_PKT);
- return false;
- }
+ trk.normalizer.packet_dropper(tsd, NORM_TCP_BLOCK);
+ trk.session->set_pkt_action_flag(ACTION_BAD_PKT);
+ return false;
}
trk.set_tcp_state(TcpStreamTracker::TCP_FIN_WAIT2);
TcpStateFinWait2::TcpStateFinWait2(TcpStateMachine& tsm) :
TcpStateHandler(TcpStreamTracker::TCP_FIN_WAIT2, tsm)
-{
-}
+{ }
bool TcpStateFinWait2::syn_sent(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
bool TcpStateFinWait2::syn_recv(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
trk.normalizer.ecn_tracker(tsd.get_tcph(), trk.session->tcp_config->require_3whs());
- if ( tsd.get_len() )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_on_syn(tsd);
return true;
}
bool TcpStateFinWait2::syn_ack_recv(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
- if ( tsd.get_len() )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_on_syn(tsd);
return true;
}
}
else
trk.update_tracker_ack_recv(tsd);
+
return true;
}
else
{
trk.update_tracker_ack_recv(tsd);
- if ( tsd.get_len() > 0 )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_segment(tsd);
}
+
return true;
}
trk.update_tracker_ack_recv(tsd);
if ( trk.update_on_fin_recv(tsd) )
{
- if ( tsd.get_len() > 0 )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_segment(tsd);
if ( !flow->two_way_traffic() )
trk.session->set_pkt_action_flag(ACTION_RST);
tsd.get_flow()->session_state |= STREAM_STATE_CLOSED;
}
- else
- {
- trk.session->tel.set_tcp_event(EVENT_BAD_RST);
- }
+
return true;
}
using namespace std;
TcpStateHandler::TcpStateHandler(TcpStreamTracker::TcpState state, TcpStateMachine& tsm)
-{
- tsm.register_state_handler(state, *this);
-}
+{ tsm.register_state_handler(state, *this); }
bool TcpStateHandler::do_pre_sm_packet_actions(TcpSegmentDescriptor&, TcpStreamTracker&)
-{
- return true;
-}
+{ return true; }
bool TcpStateHandler::do_post_sm_packet_actions(TcpSegmentDescriptor&, TcpStreamTracker&)
-{
- return true;
-}
+{ return true; }
bool TcpStateHandler::eval(TcpSegmentDescriptor& tsd, TcpStreamTracker& tracker)
{
case TcpStreamTracker::TCP_RST_RECV_EVENT:
return rst_recv(tsd, tracker);
+ case TcpStreamTracker::TCP_NO_FLAGS_EVENT:
+ return no_flags(tsd, tracker);
+
default:
break;
}
virtual bool fin_recv(TcpSegmentDescriptor&, TcpStreamTracker&) { return true; }
virtual bool rst_sent(TcpSegmentDescriptor&, TcpStreamTracker&) { return true; }
virtual bool rst_recv(TcpSegmentDescriptor&, TcpStreamTracker&) { return true; }
+ virtual bool no_flags(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
+ {
+ trk.normalizer.packet_dropper(tsd, NORM_TCP_BLOCK);
+ return false;
+ }
};
#endif
TcpStateLastAck::TcpStateLastAck(TcpStateMachine& tsm) :
TcpStateHandler(TcpStreamTracker::TCP_LAST_ACK, tsm)
-{
-}
+{ }
bool TcpStateLastAck::syn_sent(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
bool TcpStateLastAck::syn_recv(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
trk.normalizer.ecn_tracker(tsd.get_tcph(), trk.session->tcp_config->require_3whs());
- if ( tsd.get_len() )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_on_syn(tsd);
return true;
}
trk.session->update_perf_base_state(TcpStreamTracker::TCP_CLOSING);
trk.session->set_pkt_action_flag(ACTION_RST);
}
- else
- {
- trk.session->tel.set_tcp_event(EVENT_BAD_RST);
- }
// FIXIT-L might be good to create alert specific to RST with data
- if ( tsd.get_len() > 0 )
+ if ( tsd.is_data_segment() )
trk.session->tel.set_tcp_event(EVENT_DATA_AFTER_RST_RCVD);
return true;
}
bool TcpStateListen::syn_sent(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
- if ( trk.session->tcp_config->require_3whs() || tsd.has_wscale() || ( tsd.get_len() > 0 ) )
+ if ( trk.session->tcp_config->require_3whs() || tsd.has_wscale() || ( tsd.is_data_segment() ) )
{
if ( tsd.is_packet_from_server() )
trk.session->tel.set_tcp_event(EVENT_4WHS);
trk.init_on_syn_recv(tsd);
trk.normalizer.ecn_tracker(tsd.get_tcph(), trk.session->tcp_config->require_3whs());
trk.session->set_pkt_action_flag(trk.normalizer.handle_paws(tsd) );
- if ( tsd.get_len() > 0 )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_on_syn(tsd);
return true;
}
if ( !trk.session->tcp_config->require_3whs() or trk.session->is_midstream_allowed(tsd) )
{
trk.init_on_synack_recv(tsd);
- if ( tsd.get_len() > 0 )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_segment(tsd);
}
else if ( trk.session->tcp_config->require_3whs() )
bool TcpStateListen::ack_sent(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
if ( trk.session->tcp_config->midstream_allowed(tsd.get_pkt())
- && (tsd.has_wscale() || (tsd.get_len() > 0 )) )
+ && (tsd.has_wscale() || (tsd.is_data_segment() )) )
{
Flow* flow = tsd.get_flow();
flow->session_state |= ( STREAM_STATE_ACK | STREAM_STATE_SYN_ACK |
bool TcpStateListen::ack_recv(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
- if ( trk.session->is_midstream_allowed(tsd) && (tsd.has_wscale() || (tsd.get_len() > 0 )) )
+ if ( trk.session->is_midstream_allowed(tsd) && (tsd.has_wscale() || (tsd.is_data_segment() )) )
{
Flow* flow = tsd.get_flow();
TcpStateNone::TcpStateNone(TcpStateMachine& tsm) :
TcpStateHandler(TcpStreamTracker::TCP_STATE_NONE, tsm)
-{
-}
+{ }
bool TcpStateNone::syn_sent(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
bool TcpStateNone::syn_recv(TcpSegmentDescriptor&, TcpStreamTracker&)
{
- // FIXIT-H syn received on undefined client, figure this out and do the right thing
return true;
}
{
trk.init_on_synack_recv(tsd);
trk.normalizer.ecn_tracker(tsd.get_tcph(), trk.session->tcp_config->require_3whs());
- if ( tsd.get_len() > 0 )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_segment(tsd);
}
else if ( trk.session->tcp_config->require_3whs() )
bool TcpStateNone::ack_sent(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
- if ( trk.session->is_midstream_allowed(tsd) && (tsd.has_wscale() || (tsd.get_len() > 0)) )
+ if ( trk.session->is_midstream_allowed(tsd) && (tsd.has_wscale() || (tsd.is_data_segment())) )
{
Flow* flow = tsd.get_flow();
- // FIXIT-H do we need to verify the ACK field is >= the seq of the SYN-ACK?
- // 3-way Handshake complete, create TCP session
flow->session_state |= ( STREAM_STATE_ACK | STREAM_STATE_SYN_ACK |
STREAM_STATE_ESTABLISHED );
trk.init_on_3whs_ack_sent(tsd);
bool TcpStateNone::ack_recv(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
- if ( trk.session->is_midstream_allowed(tsd) && (tsd.has_wscale() || (tsd.get_len() > 0)) )
+ if ( trk.session->is_midstream_allowed(tsd) && (tsd.has_wscale() || (tsd.is_data_segment())) )
{
Flow* flow = tsd.get_flow();
trk.session->update_perf_base_state(TcpStreamTracker::TCP_CLOSING);
trk.session->set_pkt_action_flag(ACTION_RST);
}
- else
- {
- trk.session->tel.set_tcp_event(EVENT_BAD_RST);
- }
+
return true;
}
bool TcpStateSynRecv::syn_recv(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
- if ( tsd.get_len() )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_on_syn(tsd);
return true;
}
{
Flow* flow = tsd.get_flow();
- // FIXIT-H verify ack being sent is valid...
- // norm/drop + discard
trk.finish_server_init(tsd);
trk.normalizer.ecn_tracker(tsd.get_tcph(), trk.session->tcp_config->require_3whs());
flow->session_state |= STREAM_STATE_SYN_ACK;
flow->session_state |= ( STREAM_STATE_ACK | STREAM_STATE_ESTABLISHED );
trk.session->update_perf_base_state(TcpStreamTracker::TCP_ESTABLISHED);
trk.set_tcp_state(TcpStreamTracker::TCP_ESTABLISHED);
- if ( tsd.get_len() )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_on_syn(tsd);
}
return true;
flow->session_state |= ( STREAM_STATE_ACK | STREAM_STATE_ESTABLISHED );
trk.session->update_perf_base_state(TcpStreamTracker::TCP_ESTABLISHED);
trk.set_tcp_state(TcpStreamTracker::TCP_ESTABLISHED);
- if ( tsd.get_len() > 0 )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_segment(tsd);
else
trk.session->check_for_window_slam(tsd);
trk.session->update_perf_base_state(TcpStreamTracker::TCP_ESTABLISHED);
trk.set_tcp_state(TcpStreamTracker::TCP_ESTABLISHED);
}
- if ( tsd.get_len() > 0 )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_segment(tsd);
return true;
}
trk.update_tracker_ack_recv(tsd);
trk.session->set_pkt_action_flag(trk.normalizer.handle_paws(tsd));
flow->session_state |= STREAM_STATE_ACK;
- if ( tsd.get_len() > 0 )
- {
+ if ( tsd.is_data_segment() )
trk.session->handle_data_segment(tsd);
- trk.flush_data_on_fin_recv(tsd);
- }
if ( trk.update_on_fin_recv(tsd) )
{
}
else
{
- inc_tcp_discards();
trk.session->tel.set_tcp_event(EVENT_BAD_RST);
trk.normalizer.packet_dropper(tsd, NORM_TCP_BLOCK);
+ trk.session->set_pkt_action_flag(ACTION_BAD_PKT);
}
// FIXIT-L might be good to create alert specific to RST with data
- if ( tsd.get_len() > 0 )
+ if ( tsd.is_data_segment() )
trk.session->tel.set_tcp_event(EVENT_DATA_AFTER_RST_RCVD);
return true;
}
TcpStateSynSent::TcpStateSynSent(TcpStateMachine& tsm) :
TcpStateHandler(TcpStreamTracker::TCP_SYN_SENT, tsm)
-{
-}
+{ }
bool TcpStateSynSent::syn_sent(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
bool TcpStateSynSent::syn_recv(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
trk.finish_client_init(tsd);
- if ( tsd.get_len() )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_on_syn(tsd);
trk.set_tcp_state(TcpStreamTracker::TCP_SYN_RECV);
return true;
if ( trk.update_on_3whs_ack(tsd) )
{
trk.session->update_timestamp_tracking(tsd);
- if ( tsd.get_len() )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_on_syn(tsd);
}
else
trk.session->set_pkt_action_flag(ACTION_BAD_PKT);
+
return true;
}
{
Flow* flow = tsd.get_flow();
- // FIXIT-H verify ack being sent is valid...
- // norm/drop + discard
trk.update_tracker_ack_sent(tsd);
flow->set_session_flags(SSNFLAG_ESTABLISHED);
flow->session_state |= ( STREAM_STATE_ACK | STREAM_STATE_ESTABLISHED );
bool TcpStateSynSent::ack_recv(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
- if ( tsd.get_len() > 0 )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_segment(tsd);
return true;
}
{
Flow* flow = tsd.get_flow();
- // FIXIT-H verify ack being sent is valid...
- // norm/drop + discard
trk.update_tracker_ack_sent(tsd);
flow->set_session_flags(SSNFLAG_ESTABLISHED);
flow->session_state |= ( STREAM_STATE_ACK | STREAM_STATE_ESTABLISHED );
bool TcpStateSynSent::fin_recv(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
- if ( tsd.get_len() > 0 )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_segment(tsd);
return true;
}
trk.session->set_pkt_action_flag(ACTION_RST);
tsd.get_flow()->session_state |= STREAM_STATE_CLOSED;
}
- else
- {
- trk.session->tel.set_tcp_event(EVENT_BAD_RST);
- }
// FIXIT-L might be good to create alert specific to RST with data
- if ( tsd.get_len() > 0 )
+ if ( tsd.is_data_segment() )
trk.session->tel.set_tcp_event(EVENT_DATA_AFTER_RST_RCVD);
return true;
}
bool TcpStateTimeWait::syn_recv(TcpSegmentDescriptor& tsd, TcpStreamTracker& trk)
{
trk.normalizer.ecn_tracker(tsd.get_tcph(), trk.session->tcp_config->require_3whs());
- if ( tsd.get_len() )
+ if ( tsd.is_data_segment() )
trk.session->handle_data_on_syn(tsd);
return true;
trk.normalizer.packet_dropper(tsd, NORM_TCP_BLOCK);
trk.session->set_pkt_action_flag(ACTION_BAD_PKT);
}
- else if ( tsd.get_len() > 0 )
+ else if ( tsd.is_data_segment() )
trk.session->handle_data_segment(tsd);
return true;
trk.session->update_perf_base_state(TcpStreamTracker::TCP_CLOSING);
trk.session->set_pkt_action_flag(ACTION_RST);
}
- else
- {
- trk.session->tel.set_tcp_event(EVENT_BAD_RST);
- }
- // FIXIT-L might be good to create alert specific to RST with data
- // FIXIT-L refactoring required? seen this in many places
- if ( tsd.get_len() > 0 )
+ if ( tsd.is_data_segment() )
trk.session->tel.set_tcp_event(EVENT_DATA_AFTER_RST_RCVD);
return true;
void TcpStreamSession::update_session_on_syn_ack()
{
/* If session is already marked as established */
- if ( !( flow->session_state & STREAM_STATE_ESTABLISHED ) )
+ if ( !(flow->session_state & STREAM_STATE_ESTABLISHED) )
{
/* SYN-ACK from server */
if (flow->session_state != STREAM_STATE_NONE)
void TcpStreamSession::update_session_on_ack()
{
/* If session is already marked as established */
- if ( !( flow->session_state & STREAM_STATE_ESTABLISHED ) )
+ if ( !(flow->session_state & STREAM_STATE_ESTABLISHED) )
{
if ( flow->session_state & STREAM_STATE_SYN_ACK )
{
tsd.set_listener(client);
/* If we picked this guy up midstream, finish the initialization */
- if ( !( flow->session_state & STREAM_STATE_ESTABLISHED )
- && ( flow->session_state & STREAM_STATE_MIDSTREAM ) )
+ if ( !(flow->session_state & STREAM_STATE_ESTABLISHED)
+ && (flow->session_state & STREAM_STATE_MIDSTREAM) )
{
- if (tsd.get_tcph()->are_flags_set(TH_ECE)
- && (flow->get_session_flags() & SSNFLAG_ECN_CLIENT_QUERY))
+ if ( tsd.get_tcph()->are_flags_set(TH_ECE)
+ && (flow->get_session_flags() & SSNFLAG_ECN_CLIENT_QUERY) )
flow->set_session_flags(SSNFLAG_ECN_SERVER_REPLY);
- if (flow->get_session_flags() & SSNFLAG_SEEN_CLIENT)
+ if ( flow->get_session_flags() & SSNFLAG_SEEN_CLIENT )
{
// should TCP state go to established too?
flow->session_state |= STREAM_STATE_ESTABLISHED;
&& ( flow->session_state & STREAM_STATE_MIDSTREAM ) )
{
/* Midstream and seen server. */
- if (flow->get_session_flags() & SSNFLAG_SEEN_SERVER)
+ if ( flow->get_session_flags() & SSNFLAG_SEEN_SERVER )
{
flow->session_state |= STREAM_STATE_ESTABLISHED;
flow->set_session_flags(SSNFLAG_ESTABLISHED);
}
}
- if (!flow->inner_client_ttl && !tsd.is_meta_ack_packet() )
+ if ( !flow->inner_client_ttl && !tsd.is_meta_ack_packet() )
flow->set_ttl(tsd.get_pkt(), true);
}
client.reassembler.purge_segment_list();
server.reassembler.purge_segment_list();
- client.flush_policy = STREAM_FLPOLICY_IGNORE;
- server.flush_policy = STREAM_FLPOLICY_IGNORE;
+ client.set_flush_policy(STREAM_FLPOLICY_IGNORE);
+ server.set_flush_policy(STREAM_FLPOLICY_IGNORE);
client.finalize_held_packet(f);
server.finalize_held_packet(f);
{
uint8_t dir = SSN_DIR_NONE;
- if (server.get_flush_policy() != STREAM_FLPOLICY_IGNORE)
+ if ( server.get_flush_policy() != STREAM_FLPOLICY_IGNORE )
dir |= SSN_DIR_FROM_CLIENT;
- if (client.get_flush_policy() != STREAM_FLPOLICY_IGNORE)
+ if ( client.get_flush_policy() != STREAM_FLPOLICY_IGNORE )
dir |= SSN_DIR_FROM_SERVER;
return dir;
bool TcpStreamSession::is_sequenced(uint8_t dir)
{
- if (dir & SSN_DIR_FROM_CLIENT)
+ if ( dir & SSN_DIR_FROM_CLIENT )
{
if ( server.get_tf_flags() & ( TF_MISSING_PREV_PKT | TF_PKT_MISSED ) )
return false;
* packet if reassembly for this direction was set mid-session */
uint8_t TcpStreamSession::missing_in_reassembled(uint8_t dir)
{
- if (dir & SSN_DIR_FROM_CLIENT)
+ if ( dir & SSN_DIR_FROM_CLIENT )
{
if ( (server.get_tf_flags() & TF_MISSING_PKT)
- && (server.get_tf_flags() & TF_MISSING_PREV_PKT))
+ && (server.get_tf_flags() & TF_MISSING_PREV_PKT) )
return SSN_MISSING_BOTH;
- else if (server.get_tf_flags() & TF_MISSING_PREV_PKT)
+ else if ( server.get_tf_flags() & TF_MISSING_PREV_PKT )
return SSN_MISSING_BEFORE;
- else if (server.get_tf_flags() & TF_MISSING_PKT)
+ else if ( server.get_tf_flags() & TF_MISSING_PKT )
return SSN_MISSING_AFTER;
}
- else if (dir & SSN_DIR_FROM_SERVER)
+ else if ( dir & SSN_DIR_FROM_SERVER )
{
- if ((client.get_tf_flags() & TF_MISSING_PKT)
- && (client.get_tf_flags() & TF_MISSING_PREV_PKT))
+ if ( (client.get_tf_flags() & TF_MISSING_PKT)
+ && (client.get_tf_flags() & TF_MISSING_PREV_PKT) )
return SSN_MISSING_BOTH;
- else if (client.get_tf_flags() & TF_MISSING_PREV_PKT)
+ else if ( client.get_tf_flags() & TF_MISSING_PREV_PKT )
return SSN_MISSING_BEFORE;
- else if (client.get_tf_flags() & TF_MISSING_PKT)
+ else if ( client.get_tf_flags() & TF_MISSING_PKT )
return SSN_MISSING_AFTER;
}
bool TcpStreamSession::are_packets_missing(uint8_t dir)
{
- if (dir & SSN_DIR_FROM_CLIENT)
+ if ( dir & SSN_DIR_FROM_CLIENT )
{
- if (server.get_tf_flags() & TF_PKT_MISSED)
+ if ( server.get_tf_flags() & TF_PKT_MISSED )
return true;
}
- if (dir & SSN_DIR_FROM_SERVER)
+ if ( dir & SSN_DIR_FROM_SERVER )
{
- if (client.get_tf_flags() & TF_PKT_MISSED)
+ if ( client.get_tf_flags() & TF_PKT_MISSED )
return true;
}
bool TcpStreamSession::add_alert(Packet* p, uint32_t gid, uint32_t sid)
{
- TcpStreamTracker& st = p->ptrs.ip_api.get_src()->equals(flow->client_ip) ? server : client;
- StreamAlertInfo* ai;
-
- if (st.alert_count >= MAX_SESSION_ALERTS)
- return false;
-
- ai = st.alerts + st.alert_count;
- ai->gid = gid;
- ai->sid = sid;
- ai->seq = 0;
- ai->event_id = 0;
- ai->event_second = 0;
-
- st.alert_count++;
+ TcpReassemblerPolicy& trp = p->ptrs.ip_api.get_src()->equals(flow->client_ip) ?
+ server.reassembler : client.reassembler;
- return true;
+ return trp.add_alert(gid, sid);
}
bool TcpStreamSession::check_alerted(Packet* p, uint32_t gid, uint32_t sid)
{
- /* If this is not a rebuilt packet, no need to check further */
- if (!(p->packet_flags & PKT_REBUILT_STREAM))
+ // only check alerts on rebuilt packets
+ if ( !(p->packet_flags & PKT_REBUILT_STREAM) )
return false;
- const TcpStreamTracker& st = p->ptrs.ip_api.get_src()->equals(flow->client_ip) ? server : client;
- for (int i = 0; i < st.alert_count; i++)
- {
- /* This is a rebuilt packet and if we've seen this alert before,
- * return that we have previously alerted on original packet.
- */
- if (st.alerts[i].gid == gid && st.alerts[i].sid == sid)
- return true;
- }
+ TcpReassemblerPolicy& trp = p->ptrs.ip_api.get_src()->equals(flow->client_ip) ?
+ server.reassembler : client.reassembler;
- return false;
+ return trp.check_alerted(gid, sid);
}
int TcpStreamSession::update_alert(Packet* p, uint32_t gid, uint32_t sid,
uint32_t event_id, uint32_t event_second)
{
- uint32_t seq_num = 0;
- TcpStreamTracker& st = p->ptrs.ip_api.get_src()->equals(flow->client_ip) ? server : client;
-
- for (unsigned i = 0; i < st.alert_count; i++)
- {
- StreamAlertInfo* ai = &st.alerts[i];
+ TcpReassemblerPolicy& trp = p->ptrs.ip_api.get_src()->equals(flow->client_ip) ?
+ server.reassembler : client.reassembler;
- if (ai->gid == gid && ai->sid == sid && SEQ_EQ(ai->seq, seq_num))
- {
- ai->event_id = event_id;
- ai->event_second = event_second;
- return 0;
- }
- }
-
- return -1;
+ return trp.update_alert(gid, sid, event_id, event_second);
}
bool TcpStreamSession::set_packet_action_to_hold(Packet* p)
void TcpStreamSession::reset()
{
- if (tcp_init)
+ if ( tcp_init )
clear_session(true, false, false );
}
-bool TcpStreamSession::setup(Packet*)
-{
- client.init_tcp_state();
- server.init_tcp_state();
- lws_init = tcp_init = false;
- generate_3whs_alert = true;
- cleaning = false;
- pkt_action_mask = ACTION_NOTHING;
- ecn = 0;
- ingress_index = egress_index = 0;
- ingress_group = egress_group = 0;
- daq_flags = address_space_id = 0;
- tcp_config = nullptr;
-
- return true;
-}
-
void TcpStreamSession::cleanup(Packet* p)
{
if ( cleaning )
void TcpStreamSession::clear()
{
if ( tcp_init )
- // this does NOT flush data
clear_session( true, false, false );
TcpHAManager::process_deletion(*flow);
}
void TcpStreamSession::start_proxy()
-{
- tcp_config->policy = StreamPolicy::OS_PROXY;
-}
+{ tcp_config->policy = StreamPolicy::OS_PROXY; }
public:
~TcpStreamSession() override;
- bool setup(snort::Packet*) override;
void clear() override;
void cleanup(snort::Packet* = nullptr) override;
void set_splitter(bool, snort::StreamSplitter*) override;
snort::StreamSplitter* get_splitter(bool) override;
- bool is_sequenced(uint8_t /*dir*/) override;
- bool are_packets_missing(uint8_t /*dir*/) override;
+ bool is_sequenced(uint8_t dir) override;
+ bool are_packets_missing(uint8_t dir) override;
void disable_reassembly(snort::Flow*) override;
uint8_t get_reassembly_direction() override;
- uint8_t missing_in_reassembled(uint8_t /*dir*/) override;
+ uint8_t missing_in_reassembled(uint8_t dir) override;
bool add_alert(snort::Packet*, uint32_t gid, uint32_t sid) override;
bool check_alerted(snort::Packet*, uint32_t gid, uint32_t sid) override;
- int update_alert(snort::Packet*, uint32_t /*gid*/, uint32_t /*sid*/,
- uint32_t /*event_id*/, uint32_t /*event_second*/) override;
+ int update_alert(snort::Packet*, uint32_t gid, uint32_t sid,
+ uint32_t event_id, uint32_t event_second) override;
bool set_packet_action_to_hold(snort::Packet*) override;
void get_packet_header_foo(DAQ_PktHdr_t*, uint32_t dir);
void set_no_ack(bool);
bool no_ack_mode_enabled() { return no_ack; }
-
virtual void update_perf_base_state(char) = 0;
virtual void clear_session(
bool free_flow_data, bool flush_segments, bool restart, snort::Packet* p = nullptr) = 0;
-
virtual void flush() = 0;
-
virtual TcpStreamTracker::TcpState get_talker_state(TcpSegmentDescriptor&) = 0;
-
virtual TcpStreamTracker::TcpState get_listener_state(TcpSegmentDescriptor&) = 0;
-
TcpStreamTracker::TcpState get_peer_state(TcpStreamTracker* me)
{ return me == &client ? server.get_tcp_state() : client.get_tcp_state(); }
bool generate_3whs_alert = true;
TcpStreamConfig* tcp_config = nullptr;
TcpEventLogger tel;
+ bool cleaning = false;
private:
bool no_ack = false;
- bool cleaning = false;
protected:
TcpStreamSession(snort::Flow*);
#include "held_packet_queue.h"
#include "segment_overlap_editor.h"
-#include "tcp_module.h"
#include "tcp_normalizers.h"
#include "tcp_reassemblers.h"
#include "tcp_session.h"
tcp_event = TCP_FIN_SENT_EVENT;
else if ( tcph->is_ack() || tcph->is_psh() )
{
- if ( tsd.get_len() > 0 )
+ if ( tsd.is_data_segment() )
tcp_event = TCP_DATA_SEG_SENT_EVENT;
else
tcp_event = TCP_ACK_SENT_EVENT;
}
- else if ( tsd.get_len() > 0 ) // FIXIT-H no flags set, how do we handle this?
- // discard; drop if normalizing
- tcp_event = TCP_DATA_SEG_SENT_EVENT;
else
- tcp_event = TCP_ACK_SENT_EVENT;
+ {
+ // count no flags set on the talker side...
+ tcpStats.no_flags_set++;
+ tcp_event = TCP_NO_FLAGS_EVENT;
+ }
}
else
{
}
else if ( tcph->is_ack() || tcph->is_psh() )
{
- if ( tsd.get_len() > 0 )
+ if ( tsd.is_data_segment() )
tcp_event = TCP_DATA_SEG_RECV_EVENT;
else
tcp_event = TCP_ACK_RECV_EVENT;
}
- else if ( tsd.get_len() > 0 ) // FIXIT-H no flags set, how do we handle this?
- // discard; drop if normalizing
- tcp_event = TCP_DATA_SEG_RECV_EVENT;
else
- tcp_event = TCP_ACK_RECV_EVENT;
+ {
+ tcp_event = TCP_NO_FLAGS_EVENT;
+ }
}
return tcp_event;
{
tcp_state = ( client_tracker ) ?
TcpStreamTracker::TCP_STATE_NONE : TcpStreamTracker::TCP_LISTEN;
- flush_policy = STREAM_FLPOLICY_IGNORE;
- paf_setup(&paf_state);
+
snd_una = snd_nxt = snd_wnd = 0;
- rcv_nxt = r_win_base = iss = ts_last = ts_last_packet = 0;
- small_seg_count = wscale = mss = 0;
+ rcv_nxt = r_win_base = iss = 0;
+ ts_last = ts_last_packet = 0;
+ small_seg_count = 0;
+ wscale = 0;
+ mss = 0;
tf_flags = 0;
- alert_count = 0;
mac_addr_valid = false;
fin_final_seq = 0;
fin_seq_status = TcpStreamTracker::FIN_NOT_SEEN;
rst_pkt_sent = false;
order = 0;
held_packet = null_iterator;
+ flush_policy = STREAM_FLPOLICY_IGNORE;
+ reassembler.setup_paf();
}
//-------------------------------------------------------------------------
void TcpStreamTracker::init_flush_policy()
{
- if ( splitter == nullptr )
+ if ( !splitter )
flush_policy = STREAM_FLPOLICY_IGNORE;
else if ( normalizer.is_tcp_ips_enabled() )
flush_policy = STREAM_FLPOLICY_ON_DATA;
flush_policy = STREAM_FLPOLICY_IGNORE;
else
{
- paf_setup(&paf_state);
+ reassembler.setup_paf();
reassembler.reset_paf_segment();
}
}
ts_last = tsd.get_timestamp();
if (ts_last == 0)
tf_flags |= TF_TSTAMP_ZERO;
- tf_flags |= tsd.init_mss(&mss);
tf_flags |= tsd.init_wscale(&wscale);
cache_mac_address(tsd, FROM_CLIENT);
ts_last = tsd.get_timestamp();
if (ts_last == 0)
tf_flags |= TF_TSTAMP_ZERO;
- tf_flags |= ( tsd.init_mss(&mss) | tsd.init_wscale(&wscale) );
+ tf_flags |= tsd.init_wscale(&wscale);
cache_mac_address(tsd, tsd.get_direction() );
tcp_state = TcpStreamTracker::TCP_ESTABLISHED;
void TcpStreamTracker::update_tracker_ack_sent(TcpSegmentDescriptor& tsd)
{
- // ** this is how we track the last seq number sent
- // as is l_unackd is the "last left" seq recvd
- //snd_una = tsd.get_seg_seq();
-
- // FIXIT-H add check to validate ack...
- // norm/drop + discard
-
if ( SEQ_GT(tsd.get_end_seq(), snd_nxt) )
snd_nxt = tsd.get_end_seq();
bool TcpStreamTracker::update_on_3whs_ack(TcpSegmentDescriptor& tsd)
{
- bool good_ack = true;
+ bool good_ack = is_ack_valid(tsd.get_ack());
- if ( is_ack_valid(tsd.get_ack()) )
+ if ( good_ack )
{
Flow* flow = tsd.get_flow();
flow->session_state |= ( STREAM_STATE_ACK | STREAM_STATE_ESTABLISHED );
tcp_state = TcpStreamTracker::TCP_ESTABLISHED;
}
- else
- {
- inc_tcp_discards();
- normalizer.trim_win_payload(tsd);
- good_ack = false;
- }
return good_ack;
}
bool TcpStreamTracker::update_on_rst_recv(TcpSegmentDescriptor& tsd)
{
- bool good_rst = true;
-
normalizer.trim_rst_payload(tsd);
- if ( normalizer.validate_rst(tsd) )
+ bool good_rst = normalizer.validate_rst(tsd);
+ if ( good_rst )
{
Flow* flow = tsd.get_flow();
}
else
{
- inc_tcp_discards();
+ session->tel.set_tcp_event(EVENT_BAD_RST);
normalizer.packet_dropper(tsd, NORM_TCP_BLOCK);
- good_rst = false;
+ session->set_pkt_action_flag(ACTION_BAD_PKT);
}
return good_rst;
rst_pkt_sent = true;
}
-void TcpStreamTracker::flush_data_on_fin_recv(TcpSegmentDescriptor& tsd)
-{
- if ( (flush_policy != STREAM_FLPOLICY_ON_ACK)
- && (flush_policy != STREAM_FLPOLICY_ON_DATA)
- && normalizer.is_tcp_ips_enabled())
- {
- tsd.set_packet_flags(PKT_PDU_TAIL);
- }
-
- reassembler.flush_on_data_policy(tsd.get_pkt());
-}
-
bool TcpStreamTracker::update_on_fin_recv(TcpSegmentDescriptor& tsd)
{
if ( SEQ_LT(tsd.get_end_seq(), r_win_base) )
else
left_seq = r_win_base;
- if ( tsd.get_len() )
+ if ( tsd.is_data_segment() )
right_ok = SEQ_GT(tsd.get_end_seq(), left_seq);
else
right_ok = SEQ_GEQ(tsd.get_end_seq(), left_seq);
uint32_t win = normalizer.get_stream_window(tsd);
if ( SEQ_LEQ(tsd.get_seq(), r_win_base + win) )
- {
return true;
- }
else
- {
valid_seq = false;
- }
}
else
- {
valid_seq = false;
- }
-
- if ( !valid_seq )
- {
- inc_tcp_discards();
- normalizer.trim_win_payload(tsd);
- }
return valid_seq;
}
#include "segment_overlap_editor.h"
#include "tcp_defs.h"
+#include "tcp_module.h"
#include "tcp_normalizers.h"
#include "tcp_reassemblers.h"
#include "tcp_segment_descriptor.h"
-/* Only track a maximum number of alerts per session */
-#define MAX_SESSION_ALERTS 8
-struct StreamAlertInfo
-{
- /* For storing alerts that have already been seen on the session */
- uint32_t sid;
- uint32_t gid;
- uint32_t seq;
- // if we log extra data, event_* is used to correlate with alert
- uint32_t event_id;
- uint32_t event_second;
-};
-
extern const char* tcp_state_names[];
extern const char* tcp_event_names[];
TCP_FIN_RECV_EVENT,
TCP_RST_SENT_EVENT,
TCP_RST_RECV_EVENT,
+ TCP_NO_FLAGS_EVENT,
TCP_MAX_EVENTS
};
bool is_ack_valid(uint32_t cur)
{
- // If we haven't seen anything, ie, low & high are 0, return true
if ( ( snd_una == 0 ) && ( snd_nxt == 0 ) )
return true;
- return ( SEQ_GEQ(cur, snd_una) && SEQ_LEQ(cur, snd_nxt) );
+ bool valid = SEQ_GEQ(cur, snd_una) && SEQ_LEQ(cur, snd_nxt);
+ if ( !valid )
+ tcpStats.invalid_ack++;
+
+ return valid;
}
// ack number must ack syn
bool is_rst_pkt_sent() const
{ return rst_pkt_sent; }
- snort::StreamSplitter* get_splitter()
- { return splitter; }
+ void set_flush_policy(FlushPolicy policy)
+ { flush_policy = policy; }
FlushPolicy get_flush_policy()
{ return flush_policy; }
virtual void init_tcp_state();
virtual void init_flush_policy();
-
virtual void set_splitter(snort::StreamSplitter* ss);
virtual void set_splitter(const snort::Flow* flow);
+ snort::StreamSplitter* get_splitter()
+ { return splitter; }
+
+ bool is_splitter_paf() const
+ { return splitter && splitter->is_paf(); }
+
+ bool is_reassembly_enabled() const
+ { return ( splitter and (flush_policy != STREAM_FLPOLICY_IGNORE) ); }
+
virtual void init_on_syn_sent(TcpSegmentDescriptor&);
virtual void init_on_syn_recv(TcpSegmentDescriptor&);
virtual void init_on_synack_sent(TcpSegmentDescriptor&);
virtual bool update_on_fin_recv(TcpSegmentDescriptor&);
virtual bool update_on_fin_sent(TcpSegmentDescriptor&);
virtual bool is_segment_seq_valid(TcpSegmentDescriptor&);
- virtual void flush_data_on_fin_recv(TcpSegmentDescriptor&);
bool set_held_packet(snort::Packet*);
bool is_retransmit_of_held_packet(snort::Packet*);
void finalize_held_packet(snort::Packet*);
public:
TcpNormalizerPolicy normalizer;
TcpReassemblerPolicy reassembler;
-
- StreamAlertInfo alerts[MAX_SESSION_ALERTS];
-
- // this is intended to be private to paf but is included
- // directly to avoid the need for allocation; do not directly
- // manipulate within this module.
- PAF_State paf_state; // for tracking protocol aware flushing
-
TcpSession* session = nullptr;
- snort::StreamSplitter* splitter = nullptr;
-
- FlushPolicy flush_policy = STREAM_FLPOLICY_IGNORE;
uint32_t r_win_base = 0; // remote side window base sequence number (the last ack we got)
uint32_t small_seg_count = 0;
-
- uint16_t wscale = 0; /* window scale setting */
- uint16_t mss = 0; /* max segment size */
-
- uint8_t alert_count = 0;
uint8_t order = 0;
-
FinSeqNumStatus fin_seq_status = TcpStreamTracker::FIN_NOT_SEEN;
- std::list<HeldPacket>::iterator held_packet;
protected:
- // FIXIT-H reorganize per-flow structs to minimize padding
+ static const std::list<HeldPacket>::iterator null_iterator;
+ std::list<HeldPacket>::iterator held_packet;
+ snort::StreamSplitter* splitter = nullptr;
uint32_t ts_last_packet = 0;
- uint32_t ts_last = 0; /* last timestamp (for PAWS) */
-
+ uint32_t ts_last = 0; // last timestamp (for PAWS)
uint32_t fin_final_seq = 0;
uint32_t fin_seq_adjust = 0;
-
+ uint16_t mss = 0; // max segment size
+ uint16_t wscale = 0; // window scale setting
uint16_t tf_flags = 0;
-
uint8_t mac_addr[6] = { };
uint8_t tcp_options_len = 0;
+ FlushPolicy flush_policy = STREAM_FLPOLICY_IGNORE;
bool mac_addr_valid = false;
bool fin_seq_set = false; // FIXIT-M should be obviated by tcp state
-
- static const std::list<HeldPacket>::iterator null_iterator;
};
// <--- note -- the 'state' parameter must be a reference