// Return data entry associated with key. If doesn't exist, create a new entry.
Data operator[](const Key& key);
+ // Same as operator[]; additionally, sets the boolean if a new entry is created.
+ Data find_else_create(const Key& key, bool* new_data);
+
// Return all data from the LruCache in order (most recently used to least)
std::vector<std::pair<Key, Data> > get_all_data();
template<typename Key, typename Value, typename Hash>
std::shared_ptr<Value> LruCacheShared<Key, Value, Hash>::operator[](const Key& key)
+{
+ return find_else_create(key, nullptr);
+}
+
+template<typename Key, typename Value, typename Hash>
+std::shared_ptr<Value> LruCacheShared<Key, Value, Hash>::
+find_else_create(const Key& key, bool* new_data)
{
LruMapIter map_iter;
std::lock_guard<std::mutex> cache_lock(cache_mutex);
stats.find_misses++;
stats.adds++;
+ if ( new_data )
+ *new_data = true;
Data data = Data(new Value);
// Add key/data pair to front of list.
+set (HOST_TRACKER_INCLUDES
+ host_cache.h
+ host_tracker.h
+)
+
add_library( host_tracker OBJECT
+ ${HOST_TRACKER_INCLUDES}
host_cache.cc
- host_cache.h
host_cache_module.cc
host_cache_module.h
host_tracker_module.cc
host_tracker_module.h
host_tracker.cc
- host_tracker.h
)
add_subdirectory ( test )
+
+install(FILES ${HOST_TRACKER_INCLUDES}
+ DESTINATION "${INCLUDE_INSTALL_PATH}/host_tracker"
+)
}
};
-extern SO_PUBLIC LruCacheShared<snort::SfIp, HostTracker, HashIp> host_cache;
+extern SO_PUBLIC LruCacheShared<snort::SfIp, snort::HostTracker, HashIp> host_cache;
#endif
#include "host_tracker.h"
+#include "utils/util.h"
+
using namespace snort;
using namespace std;
THREAD_LOCAL struct HostTrackerStats host_tracker_stats;
+const uint8_t snort::zero_mac[MAC_SIZE] = {0, 0, 0, 0, 0, 0};
+
+void HostTracker::update_last_seen()
+{
+ std::lock_guard<std::mutex> lck(host_tracker_lock);
+ last_seen = (uint32_t) packet_time();
+}
+
+bool HostTracker::add_mac(const u_int8_t* mac, u_int8_t ttl, u_int8_t primary)
+{
+ if ( !mac or !memcmp(mac, zero_mac, MAC_SIZE) )
+ return false;
+
+ std::lock_guard<std::mutex> lck(host_tracker_lock);
+
+ for ( auto& hm : macs )
+ if ( !memcmp(mac, hm.mac, MAC_SIZE) )
+ return false;
+
+ if ( primary )
+ {
+ // only one primary mac (e.g., from ARP) is maintained at the front
+ if ( !macs.empty() )
+ macs.front().primary = 0;
+ macs.emplace_front(ttl, mac, primary, last_seen);
+ }
+ else
+ macs.emplace_back(ttl, mac, primary, last_seen);
+ return true;
+}
+
+void HostTracker::copy_data(uint8_t& p_hops, uint32_t& p_last_seen, list<HostMac>*& p_macs)
+{
+ std::lock_guard<std::mutex> lck(host_tracker_lock);
+
+ p_hops = hops;
+ p_last_seen = last_seen;
+ if ( !macs.empty() )
+ p_macs = new list<HostMac>(macs.begin(), macs.end());
+}
+
bool HostTracker::add_service(Port port, IpProtocol proto, AppId appid, bool inferred_appid)
{
host_tracker_stats.service_adds++;
return APP_ID_NONE;
}
+static inline string to_time_string(uint32_t p_time)
+{
+ time_t raw_time = (time_t) p_time;
+ struct tm* timeinfo = gmtime(&raw_time);
+ char buffer[30];
+ strftime(buffer, 30, "%F %T", timeinfo);
+ return buffer;
+}
+
+static inline string to_mac_string(const u_int8_t* mac)
+{
+ char mac_addr[18];
+ snprintf(mac_addr, 18, "%02X:%02X:%02X:%02X:%02X:%02X",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ return mac_addr;
+}
+
void HostTracker::stringify(string& str)
{
+ std::lock_guard<std::mutex> lck(host_tracker_lock);
+
+ str += "\n hops: " + to_string(hops) + ", time: " + to_time_string(last_seen);
+
+ if ( !macs.empty() )
+ {
+ str += "\nmacs size: " + to_string(macs.size());
+ for ( const auto& m : macs )
+ {
+ str += "\n mac: " + to_mac_string(m.mac)
+ + ", ttl: " + to_string(m.ttl)
+ + ", primary: " + to_string(m.primary)
+ + ", time: " + to_time_string(m.last_seen);
+ }
+ }
+
if ( !services.empty() )
{
str += "\nservices size: " + to_string(services.size());
// configuration or dynamic discovery). It provides a thread-safe API to
// set/get the host data.
+#include <cstring>
#include <mutex>
+#include <list>
#include <vector>
#include "framework/counts.h"
#include "main/thread.h"
#include "network_inspectors/appid/application_ids.h"
#include "protocols/protocol_ids.h"
+#include "time/packet_time.h"
struct HostTrackerStats
{
extern THREAD_LOCAL struct HostTrackerStats host_tracker_stats;
+namespace snort
+{
+#define MAC_SIZE 6
+extern const uint8_t zero_mac[MAC_SIZE];
+
+struct HostMac
+{
+ HostMac(u_int8_t p_ttl, const u_int8_t* p_mac, u_int8_t p_primary, uint32_t p_last_seen)
+ : ttl(p_ttl), primary(p_primary), last_seen (p_last_seen) { memcpy(mac, p_mac, MAC_SIZE); }
+
+ // the type and order below should match logger's serialization
+ u_int8_t ttl;
+ u_int8_t mac[MAC_SIZE];
+ u_int8_t primary;
+ uint32_t last_seen;
+};
+
struct HostApplication
{
Port port;
class SO_PUBLIC HostTracker
{
public:
+ HostTracker() : hops(-1)
+ { last_seen = (uint32_t) packet_time(); }
+
+ void update_last_seen();
+
+ // Returns true if a new mac entry is added, false otherwise
+ bool add_mac(const u_int8_t* mac, u_int8_t ttl, u_int8_t primary);
+
+ // The caller owns and deletes the copied list of mac addresses
+ void copy_data(uint8_t& p_hops, uint32_t& p_last_seen, std::list<HostMac>*& p_macs);
+
// Appid may not be identified always. Inferred means dynamic/runtime
// appid detected from one flow to another flow such as BitTorrent.
bool add_service(Port port, IpProtocol proto,
void stringify(std::string& str);
private:
- // Ensure that updates to a shared object are safe
- std::mutex host_tracker_lock;
-
+ std::mutex host_tracker_lock; // ensure that updates to a shared object are safe
+ uint8_t hops; // hops from the snort inspector, e.g., zero for ARP
+ uint32_t last_seen; // the last time this host was seen
+ std::list<HostMac> macs;
std::vector<HostApplication> services;
};
-
+} // namespace snort
#endif
-
static const snort::Parameter host_tracker_params[];
static const snort::Parameter service_params[];
- HostApplication app;
+ snort::HostApplication app;
snort::SfIp addr;
};
va_end(args);
logged_message[LOG_MAX] = '\0';
}
+time_t packet_time() { return 0; }
}
extern "C"
{
return strdup(str);
}
+time_t packet_time() { return 0; }
}
TEST_GROUP(host_cache)
{
char* snort_strdup(const char* s)
{ return strdup(s); }
+time_t packet_time() { return 0; }
}
// Fake show_stats to avoid bringing in a ton of dependencies.
using namespace snort;
using namespace std;
+static time_t test_time = 0;
namespace snort
{
// Fake snort_strdup() because sfutil dependencies suck
char* snort_strdup(const char* str)
{ return strdup(str); }
+time_t packet_time() { return test_time; }
}
TEST_GROUP(host_tracker)
CHECK(APP_ID_NONE == ht.get_appid(8080, IpProtocol::UDP));
}
+// Test copying data and deleting copied list
+TEST(host_tracker, copy_data_test)
+{
+ test_time = 1562198400;
+ HostTracker ht;
+ u_int8_t mac[6] = {254, 237, 222, 173, 190, 239};
+ ht.add_mac(mac, 50, 1);
+
+ uint8_t p_hops = 0;
+ uint32_t p_last_seen = 0;
+ list<HostMac>* p_macs = nullptr;
+ ht.copy_data(p_hops, p_last_seen, p_macs);
+
+ CHECK(p_hops == 255);
+ CHECK(p_last_seen == 1562198400);
+ CHECK(p_macs != nullptr);
+ CHECK(p_macs->size() == 1);
+ auto& copied_data = p_macs->front();
+ CHECK(copied_data.ttl == 50);
+ CHECK(copied_data.primary == 1);
+ CHECK(copied_data.last_seen == 1562198400);
+ CHECK(memcmp(copied_data.mac, mac, MAC_SIZE) == 0);
+
+ delete p_macs;
+}
+
TEST(host_tracker, stringify)
{
+ test_time = 1562198400; // this time will be updated and should not be seen in stringify
HostTracker ht;
+
+ u_int8_t mac1[6] = {254, 237, 222, 173, 190, 239};
+ u_int8_t mac2[6] = {202, 254, 192, 255, 238, 0};
+ test_time = 1562198404; // this time should be the time of the first mac address
+ ht.update_last_seen();
+ ht.add_mac(mac1, 9, 0);
+ test_time = 1562198407; // this time should be the time of the second mac address
+ ht.update_last_seen();
+ ht.add_mac(mac2, 3, 1); // this primary mac should go to the front of the list
+
ht.add_service(80, IpProtocol::TCP, 676, true);
+ test_time = 1562198409; // this time should be the last seen time of the host
+ ht.update_last_seen();
ht.add_service(443, IpProtocol::TCP, 1122);
- string host_tracker_string;
+ string host_tracker_string;
ht.stringify(host_tracker_string);
+
STRCMP_EQUAL(host_tracker_string.c_str(),
+ "\n hops: 255, time: 2019-07-04 00:00:09"
+ "\nmacs size: 2"
+ "\n mac: CA:FE:C0:FF:EE:00, ttl: 3, primary: 1, time: 2019-07-04 00:00:07"
+ "\n mac: FE:ED:DE:AD:BE:EF, ttl: 9, primary: 0, time: 2019-07-04 00:00:04"
"\nservices size: 2"
"\n port: 80, proto: 6, appid: 676, inferred"
"\n port: 443, proto: 6, appid: 1122");
+set (RNA_INCLUDES
+ rna_logger.h
+)
set ( RNA_SOURCES
+ ${RNA_INCLUDES}
rna_event_handler.cc
rna_event_handler.h
rna_inspector.cc
rna_inspector.h
+ rna_logger.cc
+ rna_logger_common.h
rna_module.cc
rna_module.h
rna_pnd.cc
# ${RNA_SOURCES}
# )
#endif (STATIC_INSPECTORS)
+
+install(FILES ${RNA_INCLUDES}
+ DESTINATION "${INCLUDE_INSTALL_PATH}/network_inspectors/rna"
+)
\ No newline at end of file
This inspector is still in experimental (work-in-progress) state.
-The Real-time Network Awareness (RNA) inspector provides visibility into
-a network using Passive Network Discovery (PND). RNA analyzes traffic
-to discover hosts on the network, and detect operating systems, protocols and
-applications running on these hosts. It does not generate or alter traffic on its own.
+The Real-time Network Awareness (RNA) inspector provides visibility into a network using
+Passive Network Discovery (PND). RNA analyzes traffic to discover hosts on the network
+and to detect operating system (OS) running on a host. It uses fingerprints for OS detection.
+It logs ip/mac addresses, ports, protocols, OS, and other information about traffic running
+on these hosts. It does not generate or alter traffic on its own.
-RNA generates events for the information it discovers by parsing
-TCP/UDP/IP/Link-layer protocols and analyzing data found by other
-inspectors (e.g., monitoring application IDs, client versions, user-agents from stash).
-Operating systems have different signatures which are apparent in different parameters
-in the packets that it sends. These parameters include things like TCP window sizes,
-TCP options, segment sizes, etc. Such fingerprinting information is provided to RNA
-as input so that RNA can analyze traffic.
+RNA logs information it discovers by parsing TCP/UDP/IP/Link-layer protocols and observing
+data found by other inspectors (e.g., monitoring application IDs, client versions,
+user-agents found by appid or http inspectors). Operating systems have different signatures
+which are apparent in different parameters in the packets that it sends. These parameters
+include things like TCP window sizes, TCP options, segment sizes, etc. Such fingerprinting
+information is provided to RNA as input so that RNA can analyze traffic.
-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.
+RNA discoveries is stored in host tracker objects, which are saved globally in an LRU cache
+and shared among threads. RNA memory and discovery are bounded by the memcap of cache.
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.
+of a session published from stream trackers, these modules (e.g., stream, stream_icmp,
+stream_ip, stream_tcp, and stream_udp) should be enabled whenever RNA module is enabled.
std::string rna_util_lib_path;
std::string fingerprint_dir;
std::string custom_fingerprint_dir;
+ bool enable_logger;
};
// Give default values so that RNA can work even if rna_conf_path is not provided
{
Profile profile(rna_perf_stats);
++rna_stats.tcp_syn;
- pnd.analyze_flow_tcp(event.get_packet(), false);
+ pnd.analyze_flow_tcp(event.get_packet(), TcpPacketType::SYN);
}
void RnaTcpSynAckEventHandler::handle(DataEvent& event, Flow*)
{
Profile profile(rna_perf_stats);
++rna_stats.tcp_syn_ack;
- pnd.analyze_flow_tcp(event.get_packet(), false);
+ pnd.analyze_flow_tcp(event.get_packet(), TcpPacketType::SYN_ACK);
}
void RnaTcpMidstreamEventHandler::handle(DataEvent& event, Flow*)
{
Profile profile(rna_perf_stats);
++rna_stats.tcp_midstream;
- pnd.analyze_flow_tcp(event.get_packet(), true);
+ pnd.analyze_flow_tcp(event.get_packet(), TcpPacketType::MIDSTREAM);
}
void RnaUdpEventHandler::handle(DataEvent& event, Flow*)
RnaInspector::RnaInspector(RnaModule* mod)
{
mod_conf = mod->get_config();
- if (!load_rna_conf())
- WarningMessage("RNA: Failed to load configurations from file! Using defaults.\n");
+ load_rna_conf();
+ pnd = new RnaPnd(mod_conf? mod_conf->enable_logger : false);
}
RnaInspector::~RnaInspector()
{
- delete mod_conf;
+ delete pnd;
delete rna_conf;
+ delete mod_conf;
}
bool RnaInspector::configure(SnortConfig*)
{
- DataBus::subscribe( STREAM_ICMP_NEW_FLOW_EVENT, new RnaIcmpEventHandler(pnd) );
- DataBus::subscribe( STREAM_IP_NEW_FLOW_EVENT, new RnaIpEventHandler(pnd) );
- DataBus::subscribe( STREAM_UDP_NEW_FLOW_EVENT, new RnaUdpEventHandler(pnd) );
- DataBus::subscribe( STREAM_TCP_SYN_EVENT, new RnaTcpSynEventHandler(pnd) );
- DataBus::subscribe( STREAM_TCP_SYN_ACK_EVENT, new RnaTcpSynAckEventHandler(pnd) );
- DataBus::subscribe( STREAM_TCP_MIDSTREAM_EVENT, new RnaTcpMidstreamEventHandler(pnd) );
+ DataBus::subscribe( STREAM_ICMP_NEW_FLOW_EVENT, new RnaIcmpEventHandler(*pnd) );
+ DataBus::subscribe( STREAM_IP_NEW_FLOW_EVENT, new RnaIpEventHandler(*pnd) );
+ DataBus::subscribe( STREAM_UDP_NEW_FLOW_EVENT, new RnaUdpEventHandler(*pnd) );
+ DataBus::subscribe( STREAM_TCP_SYN_EVENT, new RnaTcpSynEventHandler(*pnd) );
+ DataBus::subscribe( STREAM_TCP_SYN_ACK_EVENT, new RnaTcpSynAckEventHandler(*pnd) );
+ DataBus::subscribe( STREAM_TCP_MIDSTREAM_EVENT, new RnaTcpMidstreamEventHandler(*pnd) );
return true;
}
assert( !(BIT((unsigned)p->type()) & PROTO_BIT__ANY_SSN) );
// Handling untracked sessions, e.g., non-IP packets
- // pnd.analyze_flow_non_ip(p);
+ // pnd->analyze_flow_non_ip(p);
UNUSED(p);
}
if (!mod_conf->custom_fingerprint_dir.empty())
LogMessage(" Custom fingerprint dir: %s\n",
mod_conf->custom_fingerprint_dir.c_str());
+ LogMessage(" Enable logger: %d\n", mod_conf->enable_logger);
}
if (rna_conf)
// thread local cleanup
}
-bool RnaInspector::load_rna_conf()
+void RnaInspector::load_rna_conf()
{
if (rna_conf)
delete rna_conf;
rna_conf = new RnaConfig; // initialize with defaults
if (!mod_conf)
- return false;
+ return;
ifstream in_stream(mod_conf->rna_conf_path);
if (!in_stream)
- return false;
+ return;
uint32_t line_num = 0;
}
in_stream.close();
- return true;
}
//-------------------------------------------------------------------------
void tterm() override;
private:
- bool load_rna_conf();
+ void load_rna_conf();
const RnaModuleConfig* mod_conf = nullptr;
RnaConfig* rna_conf = nullptr;
- RnaPnd pnd;
+ RnaPnd* pnd = nullptr;
};
#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2019 Cisco and/or its affiliates. All rights reserved.
+// Copyright (C) 2003-2013 Sourcefire, Inc.
+//
+// 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_logger.h author Masud Hasan <mashasan@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "rna_logger.h"
+
+#include "managers/event_manager.h"
+#include "protocols/packet.h"
+
+#ifdef UNIT_TEST
+#include "catch/snort_catch.h"
+#endif
+
+using namespace snort;
+
+bool RnaLogger::log(uint16_t type, uint16_t subtype, const Packet* p, const RnaTracker* ht,
+ const struct in6_addr* src_ip, const u_int8_t* src_mac)
+{
+ if ( !enabled )
+ return false;
+
+ RnaLoggerEvent rle(type, subtype, ht, src_mac);
+ if ( src_ip and (!IN6_IS_ADDR_V4MAPPED(src_ip) or src_ip->s6_addr32[3]) )
+ rle.ip = src_ip;
+ else
+ rle.ip = nullptr;
+
+ EventManager::call_loggers(nullptr, const_cast<Packet*>(p), "RNA", &rle);
+ return true;
+}
+
+#ifdef UNIT_TEST
+TEST_CASE("RNA logger", "[rna_logger]")
+{
+ SECTION("Checking enabled flag")
+ {
+ RnaLogger logger1(false);
+ CHECK(logger1.log(0, 0, 0, 0, 0, 0) == false);
+
+ RnaLogger logger2(true);
+ CHECK(logger2.log(0, 0, 0, 0, 0, 0) == true);
+ }
+}
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2019 Cisco and/or its affiliates. All rights reserved.
+// Copyright (C) 2003-2013 Sourcefire, Inc.
+//
+// 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.
+//--------------------------------------------------------------------------
+
+#ifndef RNA_LOGGER_H
+#define RNA_LOGGER_H
+
+#include "events/event.h"
+#include "host_tracker/host_cache.h"
+
+namespace snort
+{
+class Flow;
+struct Packet;
+}
+
+using RnaTracker = std::shared_ptr<snort::HostTracker>;
+
+struct RnaLoggerEvent : public Event
+{
+ RnaLoggerEvent(uint16_t p_type, uint16_t p_subtype, const RnaTracker* p_ht,
+ const u_int8_t* p_mac) : type(p_type), subtype(p_subtype), ht(p_ht), mac(p_mac) { }
+ uint16_t type;
+ uint16_t subtype;
+ const RnaTracker* ht;
+ const u_int8_t* mac;
+ const struct in6_addr* ip;
+};
+
+class RnaLogger
+{
+public:
+ RnaLogger(const bool enable) : enabled(enable) { }
+ bool log(uint16_t type, uint16_t subtype, const snort::Packet* p, const RnaTracker* ht,
+ const struct in6_addr* src_ip, const u_int8_t* src_mac);
+
+private:
+ const bool enabled;
+};
+
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2019 Cisco and/or its affiliates. All rights reserved.
+// Copyright (C) 2003-2013 Sourcefire, Inc.
+//
+// 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.
+//--------------------------------------------------------------------------
+
+#ifndef RNA_LOGGER_COMMON_H
+#define RNA_LOGGER_COMMON_H
+
+// Common definitions between rna logger and pnd modules
+#define RNA_EVENT_NEW 1000
+ #define NEW_HOST 1
+
+#endif
{ "custom_fingerprint_dir", Parameter::PT_STRING, nullptr, nullptr,
"directory to custom fingerprint patterns" },
+ { "enable_logger", Parameter::PT_BOOL, nullptr, "true",
+ "enable or disable writing discovery events into logger" },
+
{ nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
};
mod_conf->fingerprint_dir = std::string(v.get_string());
else if (v.is("custom_fingerprint_dir"))
mod_conf->custom_fingerprint_dir = std::string(v.get_string());
+ else if (v.is("enable_logger"))
+ mod_conf->enable_logger = v.get_bool();
else
return false;
#include "rna_pnd.h"
-#include "host_tracker/host_cache.h"
+#include "protocols/eth.h"
+#include "protocols/icmp4.h"
+#include "protocols/packet.h"
+#include "protocols/tcp.h"
-using namespace snort;
+#include "rna_logger_common.h"
+
+#ifdef UNIT_TEST
+#include "catch/snort_catch.h"
+#endif
-static const uint8_t zeromac[6] = {0, 0, 0, 0, 0, 0};
+using namespace snort;
-static inline bool is_eligible_packet(const snort::Packet* p)
+static inline bool is_eligible_packet(const Packet* p)
{
if ( p->has_ip() or
- memcmp(snort::layer::get_eth_layer(p)->ether_src, zeromac, sizeof(zeromac)) )
+ memcmp(snort::layer::get_eth_layer(p)->ether_src, zero_mac, MAC_SIZE) )
return true;
return false;
}
-static inline bool is_eligible_ip(const snort::Packet* p)
+static inline bool is_eligible_ip(const Packet* p)
{
// If payload needs to be inspected ever, allow rebuilt packet when is_proxied
- if ( !is_eligible_packet(p) or p->is_rebuilt() or !p->flow )
+ if ( !p->has_ip() or p->is_rebuilt() or !p->flow )
return false;
return true;
}
-static inline bool is_eligible_tcp(const snort::Packet* p)
+static inline bool is_eligible_tcp(const Packet* p)
{
if ( !is_eligible_ip(p) or p->ptrs.tcph->is_rst() )
return false;
return true;
}
-static inline bool is_eligible_udp(const snort::Packet* p)
+static inline bool is_eligible_udp(const Packet* p)
{
if ( !is_eligible_ip(p) )
return false;
discover_network_non_ip(p);
}
-void RnaPnd::analyze_flow_tcp(const Packet* p, bool is_midstream)
+void RnaPnd::analyze_flow_tcp(const Packet* p, TcpPacketType type)
{
// If and when flow stores rna state, process the flow data here before global cache access
if ( is_eligible_tcp(p) )
discover_network_tcp(p);
- UNUSED(is_midstream);
+ UNUSED(type);
}
void RnaPnd::analyze_flow_udp(const Packet* p)
void RnaPnd::discover_network_icmp(const Packet* p)
{
- if ( !(host_cache[p->flow->client_ip]->
- add_service(p->flow->client_port, p->get_ip_proto_next())) )
- return;
- // process rna discovery for icmp
+ discover_network(p, 0);
}
void RnaPnd::discover_network_ip(const Packet* p)
{
- if ( !(host_cache[p->flow->client_ip]->
- add_service(p->flow->client_port, p->get_ip_proto_next())) )
- return;
- // process rna discovery for ip
+ discover_network(p, p->ptrs.ip_api.ttl());
}
void RnaPnd::discover_network_non_ip(const Packet* p)
void RnaPnd::discover_network_tcp(const Packet* p)
{
- // Track from initiator direction, if not already seen
- if ( !(host_cache[p->flow->client_ip]->
- add_service(p->flow->client_port, p->get_ip_proto_next())) )
- return;
-
- // Add mac address to ht list, ttl, last_seen, etc.
- // Generate new host events
+ // once fingerprints and other stuff are supported, the discovery code will evolve
+ discover_network(p, p->ptrs.ip_api.ttl());
}
void RnaPnd::discover_network_udp(const Packet* p)
{
- if ( !(host_cache[p->flow->client_ip]->
- add_service(p->flow->client_port, p->get_ip_proto_next())) )
- return;
- // process rna discovery for udp
+ const auto& ip_api = p->ptrs.ip_api;
+ if ( IN6_IS_ADDR_MULTICAST(ip_api.get_dst()->get_ip6_ptr()) )
+ discover_network(p, 0);
+ else
+ discover_network(p, ip_api.ttl());
}
+void RnaPnd::discover_network(const Packet* p, u_int8_t ttl)
+{
+ bool new_host = false;
+ const auto& src_ip = p->ptrs.ip_api.get_src();
+ auto ht = host_cache.find_else_create(*src_ip, &new_host);
+ if ( !new_host )
+ ht->update_last_seen(); // this should be done always and foremost
+
+ const auto& src_mac = layer::get_eth_layer(p)->ether_src;
+ ht->add_mac(src_mac, ttl, 0);
+
+ if ( new_host )
+ logger.log(RNA_EVENT_NEW, NEW_HOST, p, &ht,
+ (const struct in6_addr*) src_ip->get_ip6_ptr(), src_mac);
+}
+
+#ifdef UNIT_TEST
+TEST_CASE("RNA pnd", "[non-ip]")
+{
+ SECTION("Testing eligible packet")
+ {
+ Packet p;
+ eth::EtherHdr eh;
+ memcpy(eh.ether_src, zero_mac, MAC_SIZE);
+ p.num_layers = 1;
+ p.layers[0].start = (const uint8_t*) &eh;
+ CHECK(is_eligible_packet(&p) == false);
+
+ ip::IP4Hdr h4;
+ p.ptrs.ip_api.set(&h4);
+ RnaPnd pnd(false);
+ pnd.analyze_flow_non_ip(&p);
+ CHECK(is_eligible_packet(&p) == true);
+ }
+}
+#endif
#ifndef RNA_PND_H
#define RNA_PND_H
-#include "protocols/eth.h"
-#include "protocols/packet.h"
-#include "protocols/tcp.h"
+#include "rna_logger.h"
namespace snort
{
struct Packet;
}
+enum class TcpPacketType
+{
+ SYN, SYN_ACK, MIDSTREAM
+};
+
class RnaPnd
{
public:
+ RnaPnd(const bool en) : logger(RnaLogger(en)) { }
+
void analyze_flow_icmp(const snort::Packet* p);
void analyze_flow_ip(const snort::Packet* p);
void analyze_flow_non_ip(const snort::Packet* p);
- void analyze_flow_tcp(const snort::Packet* p, bool is_midstream);
+ void analyze_flow_tcp(const snort::Packet* p, TcpPacketType type);
void analyze_flow_udp(const snort::Packet* p);
private:
void discover_network_non_ip(const snort::Packet* p);
void discover_network_tcp(const snort::Packet* p);
void discover_network_udp(const snort::Packet* p);
+ void discover_network(const snort::Packet* p, u_int8_t ttl);
+
+ RnaLogger logger;
};
#endif