return false;
}
-bool FlowControl::process(PktType type, Packet* p)
+bool FlowControl::process(PktType type, Packet* p, bool* new_flow)
{
auto& con = proto[to_utype(type)];
set_key(&key, p);
Flow* flow = con.cache->find(&key);
if ( !flow )
+ {
flow = HighAvailabilityManager::import(*p, key);
- if ( !flow )
- {
- if ( !want_flow(type, p) )
- return true;
+ if ( !flow )
+ {
+ if ( !want_flow(type, p) )
+ return true;
- flow = con.cache->get(&key);
+ flow = con.cache->get(&key);
- if ( !flow )
- return true;
+ if ( !flow )
+ return true;
+
+ if ( new_flow )
+ *new_flow = true;
+ }
}
+
if ( !flow->session )
{
flow->init(type);
~FlowControl();
public:
- bool process(PktType, snort::Packet*);
+ bool process(PktType, snort::Packet*, bool* new_flow = nullptr);
snort::Flow* find_flow(const snort::FlowKey*);
snort::Flow* new_flow(const snort::FlowKey*);
// A flow has entered the setup state
#define FLOW_STATE_SETUP_EVENT "flow.state_setup"
+// A new flow is created on this packet
+#define STREAM_ICMP_NEW_FLOW_EVENT "stream.icmp_new_flow"
+#define STREAM_IP_NEW_FLOW_EVENT "stream.ip_new_flow"
+#define STREAM_UDP_NEW_FLOW_EVENT "stream.udp_new_flow"
+
+// A TCP flow has the flag; a midstream flow may not publish other events
+#define STREAM_TCP_SYN_EVENT "stream.tcp_syn"
+#define STREAM_TCP_SYN_ACK_EVENT "stream.tcp_syn_ack"
+#define STREAM_TCP_MIDSTREAM_EVENT "stream.tcp_midstream"
+
// A new standby flow was generated by stream high availability
#define STREAM_HA_NEW_FLOW_EVENT "stream.ha.new_flow"
#endif
RUN_FLAG__MEM_CHECK = 0x02000000,
RUN_FLAG__TRACK_ON_SYN = 0x04000000,
+ RUN_FLAG__IP_FRAGS_ONLY = 0x08000000,
};
enum OutputFlag
bool track_on_syn() const
{ return (run_flags & RUN_FLAG__TRACK_ON_SYN) != 0; }
+ bool ip_frags_only() const
+ { return (run_flags & RUN_FLAG__IP_FRAGS_ONLY) != 0; }
+
+ void clear_run_flags(RunFlag flag)
+ { run_flags &= ~flag; }
+
void set_run_flags(RunFlag flag)
{ run_flags |= flag; }
set ( RNA_SOURCES
+ rna_event_handler.cc
+ rna_event_handler.h
rna_inspector.cc
rna_inspector.h
rna_module.cc
# add_dynamic_module(rna inspectors
# ${RNA_SOURCES}
# )
-#endif (STATIC_INSPECTORS)
\ No newline at end of file
+#endif (STATIC_INSPECTORS)
RNA discoveries will be stored globally in host_tracker objects and to be shared among
multiple threads. RNA memory and discovery are bounded by the memcap in host_tracker.
+
+Packets from untracked sessions (e.g., non-IP) are processed via the eval method as per
+proto-bit registrations. Packets from tracked sessions (e.g., IP, TCP, UDP, and ICMP)
+are processed via events as per subscriptions. Since RNA needs to see the first packet
+of a session published from stream trackers, these modules (e.g., stream, stream_ip,
+stream_tcp, and stream_udp) should be enabled whenever RNA module is enabled.
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// 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 along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// rna_event_handler.cc author Masud Hasan <mashasan@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "rna_event_handler.h"
+
+using namespace snort;
+
+void RnaIcmpEventHandler::handle(snort::DataEvent&, snort::Flow*)
+{
+ Profile profile(rna_perf_stats);
+
+ ++rna_stats.icmp;
+}
+
+void RnaIpEventHandler::handle(snort::DataEvent&, snort::Flow*)
+{
+ Profile profile(rna_perf_stats);
+
+ ++rna_stats.ip;
+}
+
+void RnaUdpEventHandler::handle(snort::DataEvent&, snort::Flow*)
+{
+ Profile profile(rna_perf_stats);
+
+ ++rna_stats.udp;
+}
+
+void RnaTcpSynEventHandler::handle(snort::DataEvent&, snort::Flow*)
+{
+ Profile profile(rna_perf_stats);
+
+ ++rna_stats.tcp_syn;
+}
+
+void RnaTcpSynAckEventHandler::handle(snort::DataEvent&, snort::Flow*)
+{
+ Profile profile(rna_perf_stats);
+
+ ++rna_stats.tcp_syn_ack;
+}
+
+void RnaTcpMidstreamEventHandler::handle(snort::DataEvent&, snort::Flow*)
+{
+ Profile profile(rna_perf_stats);
+
+ ++rna_stats.tcp_midstream;
+}
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// 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 along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// rna_event_handler.h author Masud Hasan <mashasan@cisco.com>
+
+#ifndef RNA_EVENT_HANDLER_H
+#define RNA_EVENT_HANDLER_H
+
+#include "framework/data_bus.h"
+
+#include "rna_module.h"
+
+class RnaIcmpEventHandler : public snort::DataHandler
+{
+public:
+ RnaIcmpEventHandler() : DataHandler(RNA_NAME) { }
+ void handle(snort::DataEvent&, snort::Flow*) override;
+};
+
+class RnaIpEventHandler : public snort::DataHandler
+{
+public:
+ RnaIpEventHandler() : DataHandler(RNA_NAME) { }
+ void handle(snort::DataEvent&, snort::Flow*) override;
+};
+
+class RnaUdpEventHandler : public snort::DataHandler
+{
+public:
+ RnaUdpEventHandler() : DataHandler(RNA_NAME) { }
+ void handle(snort::DataEvent&, snort::Flow*) override;
+};
+
+class RnaTcpSynEventHandler : public snort::DataHandler
+{
+public:
+ RnaTcpSynEventHandler() : DataHandler(RNA_NAME) { }
+ void handle(snort::DataEvent&, snort::Flow*) override;
+};
+
+class RnaTcpSynAckEventHandler : public snort::DataHandler
+{
+public:
+ RnaTcpSynAckEventHandler() : DataHandler(RNA_NAME) { }
+ void handle(snort::DataEvent&, snort::Flow*) override;
+};
+
+class RnaTcpMidstreamEventHandler : public snort::DataHandler
+{
+public:
+ RnaTcpMidstreamEventHandler() : DataHandler(RNA_NAME) { }
+ void handle(snort::DataEvent&, snort::Flow*) override;
+};
+
+#endif
#include "log/messages.h"
#include "managers/inspector_manager.h"
-#include "profiler/profiler.h"
#include "protocols/packet.h"
+#include "rna_event_handler.h"
+
#ifdef UNIT_TEST
#include "catch/snort_catch.h"
#endif
using namespace snort;
using namespace std;
-THREAD_LOCAL SimpleStats rna_stats;
+THREAD_LOCAL RnaStats rna_stats;
THREAD_LOCAL ProfileStats rna_perf_stats;
//-------------------------------------------------------------------------
delete rna_conf;
}
+bool RnaInspector::configure(SnortConfig*)
+{
+ DataBus::subscribe( STREAM_ICMP_NEW_FLOW_EVENT, new RnaIcmpEventHandler() );
+ DataBus::subscribe( STREAM_IP_NEW_FLOW_EVENT, new RnaIpEventHandler() );
+ DataBus::subscribe( STREAM_UDP_NEW_FLOW_EVENT, new RnaUdpEventHandler() );
+ DataBus::subscribe( STREAM_TCP_SYN_EVENT, new RnaTcpSynEventHandler() );
+ DataBus::subscribe( STREAM_TCP_SYN_ACK_EVENT, new RnaTcpSynAckEventHandler() );
+ DataBus::subscribe( STREAM_TCP_MIDSTREAM_EVENT, new RnaTcpMidstreamEventHandler() );
+
+ return true;
+}
+
void RnaInspector::eval(Packet* p)
{
Profile profile(rna_perf_stats);
- if (p->packet_flags & PKT_REBUILT_STREAM)
- return;
+ // Handling untracked sessions, e.g., non-IP packets
+ assert( !p->flow );
+ assert( !(BIT((unsigned)p->type()) & PROTO_BIT__ANY_SSN) );
- ++rna_stats.total_packets;
+ ++rna_stats.other_packets;
+
+#ifdef NDEBUG
+ UNUSED(p);
+#endif
}
void RnaInspector::show(SnortConfig*)
{
string line;
getline(in_stream, line);
- line_num++;
+ ++line_num;
if (line.empty() or line.front() == '#')
continue;
rna_mod_dtor
},
IT_CONTROL,
- PROTO_BIT__ANY_IP,
+ PROTO_BIT__ALL ^ PROTO_BIT__ANY_SSN,
nullptr, // buffers
nullptr, // service
rna_inspector_pinit,
RnaInspector(RnaModule*);
~RnaInspector() override;
+ bool configure(snort::SnortConfig*) override;
void eval(snort::Packet*) override;
void show(snort::SnortConfig*) override;
void tinit() override;
#include <cassert>
#include "log/messages.h"
+#include "main/snort_config.h"
#ifdef UNIT_TEST
#include "catch/snort_catch.h"
using namespace snort;
//-------------------------------------------------------------------------
-// rna params
+// rna params and pegs
//-------------------------------------------------------------------------
static const Parameter rna_params[] =
{ nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
};
+static const PegInfo rna_pegs[] =
+{
+ { CountType::SUM, "icmp", "count of ICMP packets received" },
+ { CountType::SUM, "ip", "count of IP packets received" },
+ { CountType::SUM, "udp", "count of UDP packets received" },
+ { CountType::SUM, "tcp_syn", "count of TCP SYN packets received" },
+ { CountType::SUM, "tcp_syn_ack", "count of TCP SYN-ACK packets received" },
+ { CountType::SUM, "tcp_midstream", "count of TCP midstream packets received" },
+ { CountType::SUM, "other_packets", "count of packets received without session tracking" },
+ { CountType::END, nullptr, nullptr},
+};
+
//-------------------------------------------------------------------------
// rna module
//-------------------------------------------------------------------------
bool RnaModule::begin(const char* fqn, int, SnortConfig*)
{
- if (strcmp(fqn, "rna"))
+ if (strcmp(fqn, RNA_NAME))
return false;
else if (!mod_conf)
mod_conf = new RnaModuleConfig;
return true;
}
-bool RnaModule::end(const char* fqn, int, SnortConfig*)
+bool RnaModule::end(const char* fqn, int, SnortConfig* sc)
{
- if (mod_conf == nullptr and strcmp(fqn, "rna") == 0)
+ if ( mod_conf == nullptr and strcmp(fqn, RNA_NAME) == 0 )
return false;
+ sc->set_run_flags(RUN_FLAG__TRACK_ON_SYN); // Internal flag to track TCP on SYN
+
+ if ( sc->ip_frags_only() )
+ {
+ WarningMessage("RNA: Disabling stream.ip_frags_only option!\n");
+ sc->clear_run_flags(RUN_FLAG__IP_FRAGS_ONLY);
+ }
+
return true;
}
{ return (PegCount*)&rna_stats; }
const PegInfo* RnaModule::get_pegs() const
-{ return snort::simple_pegs; }
+{ return rna_pegs; }
ProfileStats* RnaModule::get_profile() const
{ return &rna_perf_stats; }
SECTION("module begin, set, end")
{
RnaModule mod;
+ SnortConfig sc;
CHECK_FALSE(mod.begin("dummy", 0, nullptr));
CHECK(mod.end("rna", 0, nullptr) == false);
Value v5("dummy");
CHECK(mod.set(nullptr, v5, nullptr) == false);
- CHECK(mod.end("rna", 0, nullptr) == true);
+ CHECK(mod.end("rna", 0, &sc) == true);
RnaModuleConfig* rc = mod.get_config();
CHECK(rc != nullptr);
delete rc;
}
+
+ SECTION("ip_frags_only is false")
+ {
+ RnaModule mod;
+ SnortConfig sc;
+
+ sc.set_run_flags(RUN_FLAG__IP_FRAGS_ONLY);
+ CHECK(sc.ip_frags_only() == true);
+
+ CHECK(mod.begin(RNA_NAME, 0, nullptr) == true);
+ CHECK(mod.end(RNA_NAME, 0, &sc) == true);
+ CHECK(sc.ip_frags_only() == false);
+
+ delete mod.get_config();
+ }
+
+ SECTION("track_on_syn is true")
+ {
+ RnaModule mod;
+ SnortConfig sc;
+
+ sc.clear_run_flags(RUN_FLAG__TRACK_ON_SYN);
+ CHECK(sc.track_on_syn() == false);
+
+ CHECK(mod.begin(RNA_NAME, 0, nullptr) == true);
+ CHECK(mod.end(RNA_NAME, 0, &sc) == true);
+ CHECK(sc.track_on_syn() == true);
+
+ delete mod.get_config();
+ }
}
#endif
#define RNA_MODULE_H
#include "framework/module.h"
+#include "profiler/profiler.h"
#include "rna_config.h"
#define RNA_NAME "rna"
#define RNA_HELP "Real-time network awareness and OS fingerprinting (experimental)"
-extern THREAD_LOCAL SimpleStats rna_stats;
+struct RnaStats
+{
+ PegCount icmp;
+ PegCount ip;
+ PegCount udp;
+ PegCount tcp_syn;
+ PegCount tcp_syn_ack;
+ PegCount tcp_midstream;
+ PegCount other_packets;
+};
+
+extern THREAD_LOCAL RnaStats rna_stats;
extern THREAD_LOCAL snort::ProfileStats rna_perf_stats;
class RnaModule : public snort::Module
#include "flow/flow_control.h"
#include "flow/prune_stats.h"
+#include "framework/data_bus.h"
#include "log/messages.h"
#include "main/snort_config.h"
#include "main/snort_types.h"
public:
StreamBase(const StreamModuleConfig*);
- bool configure(SnortConfig*) override;
void show(SnortConfig*) override;
void tinit() override;
FlushBucket::clear();
}
-bool StreamBase::configure(SnortConfig* sc)
-{
- config.track_on_syn = sc->track_on_syn();
- return true;
-}
-
void StreamBase::show(SnortConfig*)
{
LogMessage("Stream Base config:\n");
break;
case PktType::IP:
- if ( p->has_ip() and ((p->ptrs.decode_flags & DECODE_FRAG) or !config.ip_frags_only) )
- flow_con->process(PktType::IP, p);
+ if ( p->has_ip() and ((p->ptrs.decode_flags & DECODE_FRAG) or
+ !SnortConfig::get_conf()->ip_frags_only()) )
+ {
+ bool new_flow = false;
+ flow_con->process(PktType::IP, p, &new_flow);
+ if ( new_flow )
+ DataBus::publish(STREAM_IP_NEW_FLOW_EVENT, p);
+ }
break;
case PktType::TCP:
flow_con->process(PktType::IP, p);
if ( p->ptrs.udph )
- flow_con->process(PktType::UDP, p);
+ {
+ bool new_flow = false;
+ flow_con->process(PktType::UDP, p, &new_flow);
+ if ( new_flow )
+ DataBus::publish(STREAM_UDP_NEW_FLOW_EVENT, p);
+ }
break;
case PktType::ICMP:
if ( p->ptrs.icmph )
{
- if ( !flow_con->process(PktType::ICMP, p) )
- flow_con->process(PktType::IP, p);
+ bool new_flow = false;
+ if ( !flow_con->process(PktType::ICMP, p, &new_flow) )
+ flow_con->process(PktType::IP, p, &new_flow);
+ if ( new_flow )
+ DataBus::publish(STREAM_ICMP_NEW_FLOW_EVENT, p);
}
break;
#include "detection/rules.h"
#include "log/messages.h"
#include "main/snort.h"
+#include "main/snort_config.h"
#include "main/snort_debug.h"
using namespace snort;
}
else if ( v.is("ip_frags_only") )
{
- config.ip_frags_only = v.get_bool();
+ if ( v.get_bool() )
+ c->set_run_flags(RUN_FLAG__IP_FRAGS_ONLY);
return true;
}
else if ( strstr(fqn, "ip_cache") )
FlowConfig file_cfg;
unsigned footprint;
- bool ip_frags_only;
- bool track_on_syn;
};
class StreamModule : public snort::Module
{
tcp_event = TCP_SYN_RECV_EVENT;
tcpStats.syns++;
+ if ( tcp_state == TcpStreamTracker::TCP_LISTEN )
+ DataBus::publish(STREAM_TCP_SYN_EVENT, tsd.get_pkt());
}
else if ( tcph->is_syn_ack() )
{
tcp_event = TCP_SYN_ACK_RECV_EVENT;
tcpStats.syn_acks++;
+ if ( tcp_state == TcpStreamTracker::TCP_SYN_SENT or
+ (!Stream::is_midstream(tsd.get_flow()) and
+ (tcp_state == TcpStreamTracker::TCP_LISTEN or
+ tcp_state == TcpStreamTracker::TCP_STATE_NONE)) )
+ DataBus::publish(STREAM_TCP_SYN_ACK_EVENT, tsd.get_pkt());
}
else if ( tcph->is_rst() )
{
Flow* flow = tsd.get_flow();
flow->session_state |= STREAM_STATE_MIDSTREAM;
- flow->set_session_flags(SSNFLAG_MIDSTREAM);
+ if ( !Stream::is_midstream(flow) )
+ {
+ flow->set_session_flags(SSNFLAG_MIDSTREAM);
+ DataBus::publish(STREAM_TCP_MIDSTREAM_EVENT, tsd.get_pkt());
+ }
trk.init_on_data_seg_sent(tsd);
trk.session->init_new_tcp_session(tsd);
Flow* flow = tsd.get_flow();
flow->session_state |= STREAM_STATE_MIDSTREAM;
- flow->set_session_flags(SSNFLAG_MIDSTREAM);
+ if ( !Stream::is_midstream(flow) )
+ {
+ flow->set_session_flags(SSNFLAG_MIDSTREAM);
+ DataBus::publish(STREAM_TCP_MIDSTREAM_EVENT, tsd.get_pkt());
+ }
trk.init_on_data_seg_recv(tsd);
trk.normalizer.ecn_tracker(tsd.get_tcph(), trk.session->config->require_3whs());
trk.session->handle_data_segment(tsd);
Flow* flow = tsd.get_flow();
flow->session_state |= STREAM_STATE_MIDSTREAM;
- flow->set_session_flags(SSNFLAG_MIDSTREAM);
+ if ( !Stream::is_midstream(flow) )
+ {
+ flow->set_session_flags(SSNFLAG_MIDSTREAM);
+ DataBus::publish(STREAM_TCP_MIDSTREAM_EVENT, tsd.get_pkt());
+ }
trk.init_on_data_seg_sent(tsd);
trk.session->init_new_tcp_session(tsd);
Flow* flow = tsd.get_flow();
flow->session_state |= STREAM_STATE_MIDSTREAM;
- flow->set_session_flags(SSNFLAG_MIDSTREAM);
+ if ( !Stream::is_midstream(flow) )
+ {
+ flow->set_session_flags(SSNFLAG_MIDSTREAM);
+ DataBus::publish(STREAM_TCP_MIDSTREAM_EVENT, tsd.get_pkt());
+ }
trk.init_on_data_seg_recv(tsd);
trk.normalizer.ecn_tracker(tsd.get_tcph(), trk.session->config->require_3whs());