ctrlPkt, type, ip_proto, srcIP, srcPort, dstIP, dstPort, direction, fd);
}
+void Stream::start_proxy(Flow* flow)
+{
+ assert(flow and flow->session and flow->pkt_type == PktType::TCP);
+
+ TcpStreamSession* tcp_session = (TcpStreamSession*)flow->session;
+ tcp_session->start_proxy();
+}
+
void Stream::stop_inspection(
Flow* flow, Packet* p, char dir,
int32_t /*bytes*/, int /*response*/)
// packet_flags field of the Packet struct to indicate the direction determined.
static uint32_t get_packet_direction(Packet*);
+ // Set the stream normalization mode to PROXY. In this mode wire packets go thru a proxy before snort
+ // sees them. All stream normalizations are turned off in this mode.
+ static void start_proxy(Flow*);
+
// Stop inspection on a flow for up to count bytes (-1 to ignore for life or until resume).
// If response flag is set, automatically resume inspection up to count bytes when a data
// packet in the other direction is seen. Also marks the packet to be ignored
{ CountType::MAX, "max_bytes", "maximum number of bytes queued in any flow" },
{ CountType::SUM, "zero_len_tcp_opt", "number of zero length tcp options" },
{ CountType::SUM, "zero_win_probes", "number of tcp zero window probes" },
+ { CountType::SUM, "proxy_mode_flows", "number of flows set to proxy normalization policy" },
{ CountType::END, nullptr, nullptr }
};
PegCount max_bytes;
PegCount zero_len_tcp_opt;
PegCount zero_win_probes;
+ PegCount proxy_mode_flows;
};
extern THREAD_LOCAL struct TcpStats tcpStats;
#include "tcp_normalizer.h"
+#include "stream/stream.h"
+
#include "tcp_module.h"
#include "tcp_stream_session.h"
#include "tcp_stream_tracker.h"
using namespace snort;
+TcpNormalizer::NormStatus TcpNormalizer::apply_normalizations(
+ TcpNormalizerState& tns, TcpSegmentDescriptor& tsd, uint32_t seq, bool stream_is_inorder)
+{
+ // if this is a midstream pickup then skip normalizations
+ if ( Stream::is_midstream(tsd.get_flow()) )
+ return NORM_OK;
+
+ // these normalizations can't be done if we missed setup. and
+ // window is zero in one direction until we've seen both sides.
+ if ( tsd.get_flow()->two_way_traffic() )
+ {
+ // drop packet if sequence num is invalid
+ if ( !tns.tracker->is_segment_seq_valid(tsd) )
+ {
+ tcpStats.invalid_seq_num++;
+ trim_win_payload(tns, tsd);
+ return NORM_BAD_SEQ;
+ }
+
+ // trim to fit in listener's window and mss
+ trim_win_payload(tns, tsd,
+ (tns.tracker->r_win_base + tns.tracker->get_snd_wnd() - tns.tracker->rcv_nxt));
+
+ if ( tns.tracker->get_mss() )
+ trim_mss_payload(tns, tsd, tns.tracker->get_mss());
+
+ ecn_stripper(tns, tsd);
+ }
+
+ if ( stream_is_inorder )
+ {
+ if ( get_stream_window(tns, tsd) == 0 )
+ {
+ if ( !data_inside_window(tns, tsd) )
+ {
+ trim_win_payload(tns, tsd, 0, tsd.is_nap_policy_inline());
+ return NORM_TRIMMED;
+ }
+
+ if ( tns.tracker->get_iss() )
+ {
+ tcpStats.zero_win_probes++;
+ set_zwp_seq(tns, seq);
+ trim_win_payload(tns, tsd, MAX_ZERO_WIN_PROBE_LEN, tsd.is_nap_policy_inline());
+ }
+ }
+ }
+ else if ( get_stream_window(tns, tsd) == 0 )
+ {
+ if ( SEQ_EQ(seq, get_zwp_seq(tns)) )
+ {
+ tcpStats.zero_win_probes++;
+ trim_win_payload(tns, tsd, MAX_ZERO_WIN_PROBE_LEN, tsd.is_nap_policy_inline());
+ return NORM_TRIMMED;
+ }
+
+ trim_win_payload(tns, tsd, 0, tsd.is_nap_policy_inline());
+ return NORM_TRIMMED;
+ }
+
+ return NORM_OK;
+}
+
bool TcpNormalizer::trim_payload(TcpNormalizerState&, TcpSegmentDescriptor& tsd, uint32_t max,
NormMode mode, PegCounts peg, bool force)
{
{
public:
using State = TcpNormalizerState;
+ enum NormStatus { NORM_BAD_SEQ = -1, NORM_TRIMMED = 0, NORM_OK = 1 };
virtual ~TcpNormalizer() = default;
virtual void init(State&) { }
+
+ virtual NormStatus apply_normalizations(
+ State&, TcpSegmentDescriptor&, uint32_t seq, bool stream_is_inorder);
virtual void session_blocker(State&, TcpSegmentDescriptor&);
virtual bool packet_dropper(State&, TcpSegmentDescriptor&, NormFlags);
virtual bool trim_syn_payload(State&, TcpSegmentDescriptor&, uint32_t max = 0);
static void reset_stats();
+ std::string& get_name()
+ { return my_name; }
+
protected:
TcpNormalizer() = default;
virtual bool is_paws_ts_checked_required(State&, TcpSegmentDescriptor&);
virtual int validate_paws(State&, TcpSegmentDescriptor&);
virtual int handle_paws_no_timestamps(State&, TcpSegmentDescriptor&);
+
+ std::string my_name;
};
#endif
class TcpNormalizerFirst : public TcpNormalizer
{
public:
- TcpNormalizerFirst() = default;
+ TcpNormalizerFirst()
+ { my_name = "OS_First"; }
+
int handle_repeated_syn(TcpNormalizerState&, TcpSegmentDescriptor&) override;
};
class TcpNormalizerLast : public TcpNormalizer
{
public:
- TcpNormalizerLast() = default;
+ TcpNormalizerLast()
+ { my_name = "OS_Last"; }
+
int handle_repeated_syn(TcpNormalizerState&, TcpSegmentDescriptor&) override;
};
class TcpNormalizerLinux : public TcpNormalizer
{
public:
- TcpNormalizerLinux() = default;
+ TcpNormalizerLinux()
+ { my_name = "OS_Linux"; }
+
void init(TcpNormalizerState& tns) override
{
class TcpNormalizerOldLinux : public TcpNormalizer
{
public:
- TcpNormalizerOldLinux() = default;
+ TcpNormalizerOldLinux()
+ { my_name = "OS_OldLinux"; }
+
void init(TcpNormalizerState& tns) override
{ tns.paws_drop_zero_ts = false; }
class TcpNormalizerBSD : public TcpNormalizer
{
public:
- TcpNormalizerBSD() = default;
+ TcpNormalizerBSD()
+ { my_name = "OS_BSD"; }
+
bool validate_rst(TcpNormalizerState&, TcpSegmentDescriptor&) override;
int handle_repeated_syn(TcpNormalizerState&, TcpSegmentDescriptor&) override;
class TcpNormalizerMacOS : public TcpNormalizer
{
public:
- TcpNormalizerMacOS() = default;
+ TcpNormalizerMacOS()
+ { my_name = "OS_MacOS"; }
+
int handle_repeated_syn(TcpNormalizerState&, TcpSegmentDescriptor&) override;
};
class TcpNormalizerSolaris : public TcpNormalizer
{
public:
- TcpNormalizerSolaris() = default;
+ TcpNormalizerSolaris()
+ { my_name = "OS_Solaris"; }
+
void init(TcpNormalizerState& tns) override
{ tns.paws_drop_zero_ts = false; }
class TcpNormalizerIrix : public TcpNormalizer
{
public:
- TcpNormalizerIrix() = default;
+ TcpNormalizerIrix()
+ { my_name = "OS_Irix"; }
+
int handle_repeated_syn(TcpNormalizerState&, TcpSegmentDescriptor&) override;
};
class TcpNormalizerHpux11 : public TcpNormalizer
{
public:
- TcpNormalizerHpux11() = default;
+ TcpNormalizerHpux11()
+ { my_name = "OS_Hpux11"; }
+
bool validate_rst(TcpNormalizerState&, TcpSegmentDescriptor&) override;
bool is_paws_ts_checked_required(TcpNormalizerState&, TcpSegmentDescriptor&) override;
class TcpNormalizerHpux10 : public TcpNormalizer
{
public:
- TcpNormalizerHpux10() = default;
+ TcpNormalizerHpux10()
+ { my_name = "OS_Hpux10"; }
+
int handle_repeated_syn(TcpNormalizerState&, TcpSegmentDescriptor&) override;
};
class TcpNormalizerWindows : public TcpNormalizer
{
public:
- TcpNormalizerWindows() = default;
+ TcpNormalizerWindows()
+ { my_name = "OS_Windows"; }
+
void init(TcpNormalizerState& tns) override
{ tns.paws_drop_zero_ts = false; }
class TcpNormalizerWindows2K3 : public TcpNormalizer
{
public:
- TcpNormalizerWindows2K3() = default;
+ TcpNormalizerWindows2K3()
+ { my_name = "OS_Windows2K3"; }
+
void init(TcpNormalizerState& tns) override
{ tns.paws_drop_zero_ts = false; }
class TcpNormalizerVista : public TcpNormalizer
{
public:
- TcpNormalizerVista() = default;
+ TcpNormalizerVista()
+ { my_name = "OS_Vista"; }
+
void init(TcpNormalizerState& tns) override
{ tns.paws_drop_zero_ts = false; }
class TcpNormalizerProxy : public TcpNormalizer
{
public:
- TcpNormalizerProxy() = default;
+ TcpNormalizerProxy()
+ { my_name = "OS_Proxy"; }
+ TcpNormalizer::NormStatus apply_normalizations(
+ TcpNormalizerState&, TcpSegmentDescriptor&, uint32_t seq, bool stream_is_inorder) override;
bool validate_rst(TcpNormalizerState&, TcpSegmentDescriptor&) override;
int handle_paws(TcpNormalizerState&, TcpSegmentDescriptor&) override;
int handle_repeated_syn(TcpNormalizerState&, TcpSegmentDescriptor&) override;
TcpNormalizerState& tns, TcpSegmentDescriptor& tsd)
{
/* HPUX 11 ignores timestamps for out of order segments */
- if ( (tns.tracker->get_tf_flags() & TF_MISSING_PKT) || !SEQ_EQ(tns.tracker->rcv_nxt,
- tsd.get_seq()) )
+ if ( (tns.tracker->get_tf_flags() & TF_MISSING_PKT)
+ || !SEQ_EQ(tns.tracker->rcv_nxt, tsd.get_seq()) )
return false;
else
return true;
return handle_repeated_syn_mswin(tns.peer_tracker, tns.tracker, tsd, tns.session);
}
+TcpNormalizer::NormStatus TcpNormalizerProxy::apply_normalizations(
+ TcpNormalizerState&, TcpSegmentDescriptor&, uint32_t, bool)
+{
+ // when Proxy policy is active packet normalizations are skipped
+ return NORM_OK;
+}
+
bool TcpNormalizerProxy::validate_rst(
TcpNormalizerState&, TcpSegmentDescriptor&)
-
{
- return false;
+ return true;
}
int TcpNormalizerProxy::handle_paws(
void reset()
{ init(StreamPolicy::OS_DEFAULT, nullptr, nullptr, nullptr); }
+ TcpNormalizer::NormStatus apply_normalizations(TcpSegmentDescriptor& tsd, uint32_t seq, bool stream_is_inorder)
+ { return norm->apply_normalizations(tns, tsd, seq, stream_is_inorder); }
+
void session_blocker(TcpSegmentDescriptor& tsd)
{ norm->session_blocker(tns, tsd); }
return false;
}
-void TcpSession::process_tcp_stream(TcpSegmentDescriptor& tsd)
-{
- if ( tsd.are_packet_flags_set(PKT_IGNORE) )
- return;
-
- set_packet_header_foo(tsd);
-
- if ( flow_exceeds_config_thresholds(tsd) )
- return;
-
- TcpStreamTracker* listener = tsd.get_listener();
-
- listener->reassembler.queue_packet_for_reassembly(tsd);
-
- // Alert if overlap limit exceeded
- if ( (tcp_config->overlap_limit)
- && (listener->reassembler.get_overlap_count() > tcp_config->overlap_limit) )
- {
- tel.set_tcp_event(EVENT_EXCESSIVE_OVERLAP);
- listener->reassembler.set_overlap_count(0);
- }
-}
-
void TcpSession::update_stream_order(const TcpSegmentDescriptor& tsd, bool aligned)
{
TcpStreamTracker* listener = tsd.get_listener();
}
}
-int TcpSession::process_tcp_data(TcpSegmentDescriptor& tsd)
-{
- TcpStreamTracker* listener = tsd.get_listener();
- const tcp::TCPHdr* tcph = tsd.get_tcph();
- uint32_t seq = tsd.get_seq();
-
- if ( tcph->is_syn() )
- seq++;
-
- /* we're aligned, so that's nice anyway */
- if (seq == listener->rcv_nxt)
- {
- /* check if we're in the window */
- if ( tcp_config->policy != StreamPolicy::OS_PROXY
- and !Stream::is_midstream(flow) and listener->normalizer.get_stream_window(tsd) == 0 )
- {
- if ( !listener->normalizer.data_inside_window(tsd) )
- {
- listener->normalizer.trim_win_payload(tsd, 0, tsd.is_nap_policy_inline());
- return STREAM_UNALIGNED;
- }
-
- if( listener->get_iss() )
- {
- tcpStats.zero_win_probes++;
- listener->normalizer.set_zwp_seq(seq);
- listener->normalizer.trim_win_payload(tsd, MAX_ZERO_WIN_PROBE_LEN, tsd.is_nap_policy_inline());
- }
- }
-
- /* move the ack boundary up, this is the only way we'll accept data */
- // FIXIT-L for ips, must move all the way to first hole or right end
- listener->rcv_nxt = tsd.get_end_seq();
-
- if ( tsd.is_data_segment() )
- {
- update_stream_order(tsd, true);
- process_tcp_stream(tsd);
- return STREAM_ALIGNED;
- }
- }
- else
- {
- // pkt is out of order, do some target-based shizzle here...
- // NO, we don't want to simply bail. Some platforms favor unack'd dup data over the
- // original data. Let the reassembly policy decide how to handle the overlapping data.
- // See HP, Solaris, et al. for those that favor duplicate data over the original in
- // some cases.
-
- /* check if we're in the window */
- if ( tcp_config->policy != StreamPolicy::OS_PROXY
- and !Stream::is_midstream(flow) and listener->normalizer.get_stream_window(tsd) == 0 )
- {
- if ( SEQ_EQ(seq, listener->normalizer.get_zwp_seq()) )
- {
- tcpStats.zero_win_probes++;
- listener->normalizer.trim_win_payload(tsd, MAX_ZERO_WIN_PROBE_LEN, tsd.is_nap_policy_inline());
- return STREAM_UNALIGNED;
- }
-
- listener->normalizer.trim_win_payload(tsd, 0, tsd.is_nap_policy_inline());
- return STREAM_UNALIGNED;
- }
- if ( tsd.is_data_segment() )
- {
- update_stream_order(tsd, false);
- process_tcp_stream(tsd);
- }
- }
-
- return STREAM_UNALIGNED;
-}
-
void TcpSession::set_os_policy()
{
StreamPolicy client_os_policy = flow->ssn_policy ?
else
server.set_tcp_options_len(tcp_options_len);
- // FIXIT-M move this to normalizer base class, handle OS_PROXY in derived class
- if ( tcp_config->policy != StreamPolicy::OS_PROXY )
+ uint32_t seq = tsd.get_tcph()->is_syn() ? tsd.get_seq() + 1 : tsd.get_seq();
+ bool stream_is_inorder = ( seq == listener->rcv_nxt );
+
+ int rc = listener->normalizer.apply_normalizations(tsd, seq, stream_is_inorder);
+ switch ( rc )
{
- // these normalizations can't be done if we missed setup. and
- // window is zero in one direction until we've seen both sides.
- if ( !(Stream::is_midstream(flow)) && flow->two_way_traffic() )
+ case TcpNormalizer::NORM_OK:
+ if ( stream_is_inorder )
+ listener->rcv_nxt = tsd.get_end_seq();
+
+ update_stream_order(tsd, stream_is_inorder);
+
+ // don't queue data if we are ignoring or queue thresholds are exceeded
+ if ( !tsd.are_packet_flags_set(PKT_IGNORE) and !flow_exceeds_config_thresholds(tsd) )
{
- // drop packet if sequence num is invalid
- if ( !listener->is_segment_seq_valid(tsd) )
+ set_packet_header_foo(tsd);
+ listener->reassembler.queue_packet_for_reassembly(tsd);
+
+ // Alert if overlap limit exceeded
+ if ( (tcp_config->overlap_limit)
+ && (listener->reassembler.get_overlap_count() > tcp_config->overlap_limit) )
{
- tcpStats.invalid_seq_num++;
- listener->normalizer.trim_win_payload(tsd);
- return;
+ tel.set_tcp_event(EVENT_EXCESSIVE_OVERLAP);
+ listener->reassembler.set_overlap_count(0);
}
+ }
+ break;
- // 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));
+ case TcpNormalizer::NORM_TRIMMED:
+ break;
- if ( listener->get_mss() )
- listener->normalizer.trim_mss_payload(tsd, listener->get_mss());
+ case TcpNormalizer::NORM_BAD_SEQ:
+ return;
- listener->normalizer.ecn_stripper(tsd);
- }
- }
+ default:
+ assert(false);
+ break;
- process_tcp_data(tsd);
+ }
}
if ( flush )
private:
int process_tcp_packet(TcpSegmentDescriptor&, const snort::Packet*);
- void process_tcp_stream(TcpSegmentDescriptor&);
- int process_tcp_data(TcpSegmentDescriptor&);
void set_os_policy() override;
bool flow_exceeds_config_thresholds(TcpSegmentDescriptor&);
void update_stream_order(const TcpSegmentDescriptor&, bool aligned);
#include "tcp_stream_session.h"
#include "framework/data_bus.h"
+#include "packet_tracer/packet_tracer.h"
#include "pub_sub/stream_event_ids.h"
#include "stream/stream.h"
#include "stream/tcp/tcp_ha.h"
}
void TcpStreamSession::start_proxy()
-{ tcp_config->policy = StreamPolicy::OS_PROXY; }
+{
+ if ( PacketTracer::is_active() )
+ PacketTracer::log("Stream TCP normalization policy set to Proxy mode. Normalizations will be skipped\n");
+
+ tcp_config->policy = StreamPolicy::OS_PROXY;
+ client.normalizer.init(tcp_config->policy, this, &client, &server);
+ server.normalizer.init(tcp_config->policy, this, &server, &client);
+ ++tcpStats.proxy_mode_flows;
+}
void TcpStreamSession::set_established(const TcpSegmentDescriptor& tsd)
{