From: Mike Stepanek (mstepane) Date: Mon, 26 Aug 2019 17:05:41 +0000 (-0400) Subject: Merge pull request #1703 in SNORT/snort3 from ~MASHASAN/snort3:rna_unified_log to... X-Git-Tag: 3.0.0-260~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b5ed176cb5f5be36813b9a01af7637ab17df72ec;p=thirdparty%2Fsnort3.git Merge pull request #1703 in SNORT/snort3 from ~MASHASAN/snort3:rna_unified_log to master Squashed commit of the following: commit 35a9980eefe2fe7848bd936e77a66d90e8a603a3 Author: Masud Hasan Date: Tue Aug 6 09:30:45 2019 -0400 rna: Support for rna unified2 logging --- diff --git a/src/hash/lru_cache_shared.h b/src/hash/lru_cache_shared.h index d621b1bdb..2ffd1a84c 100644 --- a/src/hash/lru_cache_shared.h +++ b/src/hash/lru_cache_shared.h @@ -67,6 +67,9 @@ public: // 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 > get_all_data(); @@ -183,6 +186,13 @@ std::shared_ptr LruCacheShared::find(const Key& key) template std::shared_ptr LruCacheShared::operator[](const Key& key) +{ + return find_else_create(key, nullptr); +} + +template +std::shared_ptr LruCacheShared:: +find_else_create(const Key& key, bool* new_data) { LruMapIter map_iter; std::lock_guard cache_lock(cache_mutex); @@ -197,6 +207,8 @@ std::shared_ptr LruCacheShared::operator[](const Key& k stats.find_misses++; stats.adds++; + if ( new_data ) + *new_data = true; Data data = Data(new Value); // Add key/data pair to front of list. diff --git a/src/host_tracker/CMakeLists.txt b/src/host_tracker/CMakeLists.txt index 881c27740..917afdee5 100644 --- a/src/host_tracker/CMakeLists.txt +++ b/src/host_tracker/CMakeLists.txt @@ -1,12 +1,20 @@ +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" +) diff --git a/src/host_tracker/host_cache.h b/src/host_tracker/host_cache.h index 725146acd..ac3e9a095 100644 --- a/src/host_tracker/host_cache.h +++ b/src/host_tracker/host_cache.h @@ -39,7 +39,7 @@ struct HashIp } }; -extern SO_PUBLIC LruCacheShared host_cache; +extern SO_PUBLIC LruCacheShared host_cache; #endif diff --git a/src/host_tracker/host_tracker.cc b/src/host_tracker/host_tracker.cc index d0911ab4b..179b62978 100644 --- a/src/host_tracker/host_tracker.cc +++ b/src/host_tracker/host_tracker.cc @@ -24,11 +24,54 @@ #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 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 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*& p_macs) +{ + std::lock_guard lck(host_tracker_lock); + + p_hops = hops; + p_last_seen = last_seen; + if ( !macs.empty() ) + p_macs = new list(macs.begin(), macs.end()); +} + bool HostTracker::add_service(Port port, IpProtocol proto, AppId appid, bool inferred_appid) { host_tracker_stats.service_adds++; @@ -66,8 +109,41 @@ AppId HostTracker::get_appid(Port port, IpProtocol proto, bool inferred_only) 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 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()); diff --git a/src/host_tracker/host_tracker.h b/src/host_tracker/host_tracker.h index 0c22509be..9e032e6bf 100644 --- a/src/host_tracker/host_tracker.h +++ b/src/host_tracker/host_tracker.h @@ -25,7 +25,9 @@ // configuration or dynamic discovery). It provides a thread-safe API to // set/get the host data. +#include #include +#include #include #include "framework/counts.h" @@ -33,6 +35,7 @@ #include "main/thread.h" #include "network_inspectors/appid/application_ids.h" #include "protocols/protocol_ids.h" +#include "time/packet_time.h" struct HostTrackerStats { @@ -42,6 +45,23 @@ 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; @@ -53,6 +73,17 @@ struct HostApplication 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*& 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, @@ -64,11 +95,11 @@ public: 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 macs; std::vector services; }; - +} // namespace snort #endif - diff --git a/src/host_tracker/host_tracker_module.h b/src/host_tracker/host_tracker_module.h index 00f400030..fffa99a83 100644 --- a/src/host_tracker/host_tracker_module.h +++ b/src/host_tracker/host_tracker_module.h @@ -55,7 +55,7 @@ private: static const snort::Parameter host_tracker_params[]; static const snort::Parameter service_params[]; - HostApplication app; + snort::HostApplication app; snort::SfIp addr; }; diff --git a/src/host_tracker/test/host_cache_module_test.cc b/src/host_tracker/test/host_cache_module_test.cc index 7c2f6587e..501b94d0c 100644 --- a/src/host_tracker/test/host_cache_module_test.cc +++ b/src/host_tracker/test/host_cache_module_test.cc @@ -57,6 +57,7 @@ void LogMessage(const char* format,...) va_end(args); logged_message[LOG_MAX] = '\0'; } +time_t packet_time() { return 0; } } extern "C" diff --git a/src/host_tracker/test/host_cache_test.cc b/src/host_tracker/test/host_cache_test.cc index f177349ac..f83f0f773 100644 --- a/src/host_tracker/test/host_cache_test.cc +++ b/src/host_tracker/test/host_cache_test.cc @@ -40,6 +40,7 @@ char* snort_strdup(const char* str) { return strdup(str); } +time_t packet_time() { return 0; } } TEST_GROUP(host_cache) diff --git a/src/host_tracker/test/host_tracker_module_test.cc b/src/host_tracker/test/host_tracker_module_test.cc index 33e9da5a1..8ba0b2c74 100644 --- a/src/host_tracker/test/host_tracker_module_test.cc +++ b/src/host_tracker/test/host_tracker_module_test.cc @@ -39,6 +39,7 @@ namespace snort { 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. diff --git a/src/host_tracker/test/host_tracker_test.cc b/src/host_tracker/test/host_tracker_test.cc index ae6eb4400..dcc52ce89 100644 --- a/src/host_tracker/test/host_tracker_test.cc +++ b/src/host_tracker/test/host_tracker_test.cc @@ -33,11 +33,13 @@ 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) @@ -66,15 +68,59 @@ TEST(host_tracker, add_find_service_test) 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* 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"); diff --git a/src/network_inspectors/rna/CMakeLists.txt b/src/network_inspectors/rna/CMakeLists.txt index 109169731..56b56b54d 100644 --- a/src/network_inspectors/rna/CMakeLists.txt +++ b/src/network_inspectors/rna/CMakeLists.txt @@ -1,9 +1,15 @@ +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 @@ -19,3 +25,7 @@ add_library( rna OBJECT # ${RNA_SOURCES} # ) #endif (STATIC_INSPECTORS) + +install(FILES ${RNA_INCLUDES} + DESTINATION "${INCLUDE_INSTALL_PATH}/network_inspectors/rna" +) \ No newline at end of file diff --git a/src/network_inspectors/rna/dev_notes.txt b/src/network_inspectors/rna/dev_notes.txt index bd1896d11..3e1bc2d28 100644 --- a/src/network_inspectors/rna/dev_notes.txt +++ b/src/network_inspectors/rna/dev_notes.txt @@ -1,23 +1,23 @@ 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. diff --git a/src/network_inspectors/rna/rna_config.h b/src/network_inspectors/rna/rna_config.h index ff6da49b8..791b4f5b3 100644 --- a/src/network_inspectors/rna/rna_config.h +++ b/src/network_inspectors/rna/rna_config.h @@ -27,6 +27,7 @@ struct RnaModuleConfig 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 diff --git a/src/network_inspectors/rna/rna_event_handler.cc b/src/network_inspectors/rna/rna_event_handler.cc index 8607c5e8d..4e021abcd 100644 --- a/src/network_inspectors/rna/rna_event_handler.cc +++ b/src/network_inspectors/rna/rna_event_handler.cc @@ -44,21 +44,21 @@ void RnaTcpSynEventHandler::handle(DataEvent& event, Flow*) { 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*) diff --git a/src/network_inspectors/rna/rna_inspector.cc b/src/network_inspectors/rna/rna_inspector.cc index 1b3629e76..31f06c5e9 100644 --- a/src/network_inspectors/rna/rna_inspector.cc +++ b/src/network_inspectors/rna/rna_inspector.cc @@ -52,24 +52,25 @@ THREAD_LOCAL ProfileStats rna_perf_stats; 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; } @@ -83,7 +84,7 @@ void RnaInspector::eval(Packet* p) 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); } @@ -102,6 +103,7 @@ void RnaInspector::show(SnortConfig*) 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) @@ -127,18 +129,18 @@ void RnaInspector::tterm() // 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; @@ -178,7 +180,6 @@ bool RnaInspector::load_rna_conf() } in_stream.close(); - return true; } //------------------------------------------------------------------------- diff --git a/src/network_inspectors/rna/rna_inspector.h b/src/network_inspectors/rna/rna_inspector.h index 7e2c43540..53f0b4094 100644 --- a/src/network_inspectors/rna/rna_inspector.h +++ b/src/network_inspectors/rna/rna_inspector.h @@ -44,10 +44,10 @@ public: 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 diff --git a/src/network_inspectors/rna/rna_logger.cc b/src/network_inspectors/rna/rna_logger.cc new file mode 100644 index 000000000..ccc0cd7c2 --- /dev/null +++ b/src/network_inspectors/rna/rna_logger.cc @@ -0,0 +1,65 @@ +//-------------------------------------------------------------------------- +// 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 + +#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(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 diff --git a/src/network_inspectors/rna/rna_logger.h b/src/network_inspectors/rna/rna_logger.h new file mode 100644 index 000000000..24091e2bf --- /dev/null +++ b/src/network_inspectors/rna/rna_logger.h @@ -0,0 +1,56 @@ +//-------------------------------------------------------------------------- +// 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; + +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 diff --git a/src/network_inspectors/rna/rna_logger_common.h b/src/network_inspectors/rna/rna_logger_common.h new file mode 100644 index 000000000..bcdf273d2 --- /dev/null +++ b/src/network_inspectors/rna/rna_logger_common.h @@ -0,0 +1,27 @@ +//-------------------------------------------------------------------------- +// 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 diff --git a/src/network_inspectors/rna/rna_module.cc b/src/network_inspectors/rna/rna_module.cc index 1bbf064e9..98cebdbff 100644 --- a/src/network_inspectors/rna/rna_module.cc +++ b/src/network_inspectors/rna/rna_module.cc @@ -53,6 +53,9 @@ static const Parameter rna_params[] = { "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 } }; @@ -99,6 +102,8 @@ bool RnaModule::set(const char*, Value& v, SnortConfig*) 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; diff --git a/src/network_inspectors/rna/rna_pnd.cc b/src/network_inspectors/rna/rna_pnd.cc index 10d004065..834be4629 100644 --- a/src/network_inspectors/rna/rna_pnd.cc +++ b/src/network_inspectors/rna/rna_pnd.cc @@ -26,36 +26,43 @@ #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; @@ -88,13 +95,13 @@ void RnaPnd::analyze_flow_non_ip(const Packet* p) 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) @@ -105,18 +112,12 @@ 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) @@ -127,20 +128,52 @@ 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 diff --git a/src/network_inspectors/rna/rna_pnd.h b/src/network_inspectors/rna/rna_pnd.h index b19beb408..e7be2d714 100644 --- a/src/network_inspectors/rna/rna_pnd.h +++ b/src/network_inspectors/rna/rna_pnd.h @@ -20,22 +20,27 @@ #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: @@ -45,6 +50,9 @@ 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