]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #2339 in SNORT/snort3 from ~MMATIRKO/snort3:rna_mac to master
authorMasud Hasan (mashasan) <mashasan@cisco.com>
Fri, 7 Aug 2020 22:23:25 +0000 (22:23 +0000)
committerMasud Hasan (mashasan) <mashasan@cisco.com>
Fri, 7 Aug 2020 22:23:25 +0000 (22:23 +0000)
Squashed commit of the following:

commit 10b80bae582fe4fc391b26f06cd57f8e90fc5a7d
Author: Michael Matirko <mmatirko@cisco.com>
Date:   Wed Jun 24 16:13:31 2020 -0400

    Add RNA MAC-based discovery logic

21 files changed:
src/hash/hash_key_operations.h
src/helpers/discovery_filter.cc
src/helpers/discovery_filter.h
src/host_tracker/host_tracker.cc
src/host_tracker/host_tracker.h
src/host_tracker/test/host_tracker_test.cc
src/network_inspectors/rna/CMakeLists.txt
src/network_inspectors/rna/dev_notes.txt
src/network_inspectors/rna/rna_inspector.cc
src/network_inspectors/rna/rna_logger.cc
src/network_inspectors/rna/rna_logger.h
src/network_inspectors/rna/rna_logger_common.h
src/network_inspectors/rna/rna_mac_cache.cc [new file with mode: 0644]
src/network_inspectors/rna/rna_mac_cache.h [new file with mode: 0644]
src/network_inspectors/rna/rna_module.cc
src/network_inspectors/rna/rna_module.h
src/network_inspectors/rna/rna_pnd.cc
src/network_inspectors/rna/rna_pnd.h
src/network_inspectors/rna/test/rna_module_mock.h
src/protocols/CMakeLists.txt
src/protocols/bpdu.h [new file with mode: 0644]

index b48ab11fba297c5765df0df0d400f7e2af475925..5b4d6ae3d7278dd23b77fec9a139e78a80e0a4cb 100644 (file)
@@ -65,6 +65,17 @@ static inline int hash_nearest_power_of_2(int nrows)
     return nrows;
 }
 
+static inline uint64_t hash_mac(const uint8_t* a)
+{
+    uint64_t hash = a[0];
+    hash = (hash << 8) | a[1];
+    hash = (hash << 8) | a[2];
+    hash = (hash << 8) | a[3];
+    hash = (hash << 8) | a[4];
+    hash = (hash << 8) | a[5];
+    return hash;
+}
+
 class HashKeyOperations
 {
 public:
index 02ce41736624733d2b4892e4f1f00c03d42a8876..d408530f1cf37dbc92f8b572439ec6036f681c9e 100644 (file)
@@ -203,11 +203,11 @@ bool DiscoveryFilter::is_app_monitored(const Packet* p, uint8_t* flag)
     return is_monitored(p, DF_APP, *flag, DF_APP_CHECKED, DF_APP_MONITORED);
 }
 
-bool DiscoveryFilter::is_host_monitored(const Packet* p, uint8_t* flag)
+bool DiscoveryFilter::is_host_monitored(const Packet* p, uint8_t* flag, const SfIp* ip)
 {
     if ( flag == nullptr )
-        return is_monitored(p, DF_HOST);
-    return is_monitored(p, DF_HOST, *flag, DF_HOST_CHECKED, DF_HOST_MONITORED);
+        return is_monitored(p, DF_HOST, ip);
+    return is_monitored(p, DF_HOST, *flag, DF_HOST_CHECKED, DF_HOST_MONITORED, ip);
 }
 
 bool DiscoveryFilter::is_user_monitored(const Packet* p, uint8_t* flag)
@@ -218,14 +218,14 @@ bool DiscoveryFilter::is_user_monitored(const Packet* p, uint8_t* flag)
 }
 
 bool DiscoveryFilter::is_monitored(const Packet* p, FilterType type, uint8_t& flag,
-    uint8_t checked, uint8_t monitored)
+    uint8_t checked, uint8_t monitored, const SfIp* ip)
 {
     if ( flag & checked )
         return flag & monitored;
 
     flag |= checked;
 
-    if ( is_monitored(p, type) )
+    if ( is_monitored(p, type, ip) )
     {
         flag |= monitored;
         return true;
@@ -235,7 +235,7 @@ bool DiscoveryFilter::is_monitored(const Packet* p, FilterType type, uint8_t& fl
     return false;
 }
 
-bool DiscoveryFilter::is_monitored(const Packet* p, FilterType type)
+bool DiscoveryFilter::is_monitored(const Packet* p, FilterType type, const SfIp* ip)
 {
     if ( !vartable )
         return true; // when not configured, 'any' ip/port/zone are monitored by default
@@ -255,7 +255,11 @@ bool DiscoveryFilter::is_monitored(const Packet* p, FilterType type)
     if (!varip and zone != DF_ANY_ZONE)
         varip = get_list(type, DF_ANY_ZONE, true);
 
-    return sfvar_ip_in(varip, p->ptrs.ip_api.get_src()); // source ip only
+    if (!p->ptrs.ip_api.get_src() and !ip)
+        return true; // Don't check for non-IP, non ARP
+
+    const SfIp* host_ip = (ip) ? ip : p->ptrs.ip_api.get_src();
+    return sfvar_ip_in(varip, host_ip);
 }
 
 bool DiscoveryFilter::is_port_excluded(const Packet* p)
index cd67bab2713ceac5af9fadd207bd989adcce4752..a9616ac1306e9311103ff205dd0b3447625a6b40 100644 (file)
@@ -41,7 +41,8 @@ public:
 
     // If flag is provided (preferable), results are stored in flag to avoid future lookups
     bool is_app_monitored(const snort::Packet* p, uint8_t* flag = nullptr);
-    bool is_host_monitored(const snort::Packet* p, uint8_t* flag = nullptr);
+    bool is_host_monitored(const snort::Packet* p, uint8_t* flag = nullptr,
+        const snort::SfIp* ip = nullptr);
     bool is_user_monitored(const snort::Packet* p, uint8_t* flag = nullptr);
 
 private:
@@ -49,8 +50,8 @@ private:
     enum Direction { CLIENT, SERVER, NUM_DIRECTIONS };
 
     bool is_monitored(const snort::Packet* p, FilterType type, uint8_t& flag,
-        uint8_t checked, uint8_t monitored);
-    bool is_monitored(const snort::Packet* p, FilterType type);
+        uint8_t checked, uint8_t monitored, const snort::SfIp* ip = nullptr);
+    bool is_monitored(const snort::Packet* p, FilterType type, const snort::SfIp* ip = nullptr);
     void add_ip(FilterType type, ZoneType zone, std::string& ip);
     sfip_var_t* get_list(FilterType type, ZoneType zone, bool exclude_empty = false);
 
index 2b15ff49ba944e243f36fc0e170c2fd01c9d44af..0e1f354276eab5b0da78a04317feed65fc0ec627 100644 (file)
@@ -57,16 +57,118 @@ bool HostTracker::add_mac(const uint8_t* mac, uint8_t ttl, uint8_t primary)
         if ( !memcmp(mac, hm.mac, MAC_SIZE) )
             return false;
 
-    if ( primary )
+    macs.emplace_back(ttl, mac, primary, last_seen);
+    return true;
+}
+
+const HostMac* HostTracker::get_hostmac(const uint8_t* mac)
+{
+    if ( !mac or !memcmp(mac, zero_mac, MAC_SIZE) )
+        return nullptr;
+
+    std::lock_guard<std::mutex> lck(host_tracker_lock);
+
+    for ( const auto& hm : macs )
+        if ( !memcmp(mac, hm.mac, MAC_SIZE) )
+            return &hm;
+
+    return nullptr;
+}
+
+bool HostTracker::update_mac_ttl(const uint8_t* mac, uint8_t new_ttl)
+{
+    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) )
+        {
+            if (hm.ttl < new_ttl)
+            {
+                hm.ttl = new_ttl;
+                return true;
+            }
+
+            return false;
+        }
+
+    return false;
+}
+
+bool HostTracker::make_primary(const uint8_t* mac)
+{
+    if ( !mac or !memcmp(mac, zero_mac, MAC_SIZE) )
+        return false;
+
+    HostMac* hm = nullptr;
+
+    std::lock_guard<std::mutex> lck(host_tracker_lock);
+
+    for ( auto& hm_iter : macs )
+        if ( !memcmp(mac, hm_iter.mac, MAC_SIZE) )
+        {
+            hm = &hm_iter;
+            break;
+        }
+
+    if ( !hm )
+        return false;
+
+    if (!hm->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);
+        hm->primary = true;
+        return true;
     }
-    else
-        macs.emplace_back(ttl, mac, primary, last_seen);
-    return true;
+
+    return false;
+}
+
+HostMac* HostTracker::get_max_ttl_hostmac()
+{
+    std::lock_guard<std::mutex> lck(host_tracker_lock);
+
+    HostMac* max_ttl_hm = nullptr;
+    uint8_t max_ttl = 0;
+
+    for ( auto& hm : macs )
+    {
+        if (hm.primary)
+            return &hm;
+
+        if (hm.ttl > max_ttl)
+        {
+            max_ttl = hm.ttl;
+            max_ttl_hm = &hm;
+        }
+    }
+
+    return max_ttl_hm;
+}
+
+void HostTracker::update_vlan(uint16_t vth_pri_cfi_vlan, uint16_t vth_proto)
+{
+    vlan_tag_present = true;
+    vlan_tag.vth_pri_cfi_vlan = vth_pri_cfi_vlan;
+    vlan_tag.vth_proto = vth_proto;
+}
+
+bool HostTracker::has_vlan()
+{
+    return vlan_tag_present;
+}
+
+uint16_t HostTracker::get_vlan()
+{
+    return vlan_tag.vth_pri_cfi_vlan;
+}
+
+void HostTracker::get_vlan_details(uint8_t& cfi, uint8_t& priority, uint16_t& vid)
+{
+    cfi = vlan_tag.cfi();
+    priority = vlan_tag.priority();
+    vid = vlan_tag.vid();
 }
 
 void HostTracker::copy_data(uint8_t& p_hops, uint32_t& p_last_seen, list<HostMac>*& p_macs)
index 639481e3433979042eacea5c62aa28062991edf9..9f42611b6d90424c987be9df05cccdecf2de9e1c 100644 (file)
@@ -36,6 +36,7 @@
 #include "main/thread.h"
 #include "network_inspectors/appid/application_ids.h"
 #include "protocols/protocol_ids.h"
+#include "protocols/vlan.h"
 #include "time/packet_time.h"
 
 struct HostTrackerStats
@@ -71,6 +72,15 @@ struct HostApplication
     bool inferred_appid;
 };
 
+enum HostType
+{
+    HOST_TYPE_HOST=0,
+    HOST_TYPE_ROUTER,
+    HOST_TYPE_BRIDGE,
+    HOST_TYPE_NAT,
+    HOST_TYPE_LB
+};
+
 typedef HostCacheAllocIp<HostMac> HostMacAllocator;
 typedef HostCacheAllocIp<HostApplication> HostAppAllocator;
 
@@ -97,9 +107,32 @@ public:
         return last_event;
     }
 
+    void set_host_type(HostType rht)
+    { host_type = rht; }
+
+    uint8_t get_hops() { return hops; }
+    void update_hops(uint8_t h) { hops = h; }
+
     // Returns true if a new mac entry is added, false otherwise
     bool add_mac(const uint8_t* mac, uint8_t ttl, uint8_t primary);
 
+    // Returns true if a mac entry TTL is updated and decreased, false otherwise
+    bool update_mac_ttl(const uint8_t* mac, uint8_t new_ttl);
+
+    // Returns true if we changed primary (false->true), false otherwise
+    bool make_primary(const uint8_t* mac);
+
+    // Returns the hostmac pointer with the highest TTL
+    HostMac* get_max_ttl_hostmac();
+
+    // Returns the matching host_mac
+    const HostMac* get_hostmac(const uint8_t* mac);
+
+    void update_vlan(uint16_t vth_pri_cfi_vlan, uint16_t vth_proto);
+    bool has_vlan();
+    uint16_t get_vlan();
+    void get_vlan_details(uint8_t& cfi, uint8_t& priority, uint16_t& vid);
+
     // 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);
 
@@ -122,6 +155,9 @@ private:
     uint32_t last_event;          // the last time an event was generated
     std::list<HostMac, HostMacAllocator> macs;
     std::vector<HostApplication, HostAppAllocator> services;
+    bool vlan_tag_present = false;
+    vlan::VlanTagHdr vlan_tag;
+    HostType host_type;
 
     // Hide / delete the constructor from the outside world. We don't want to
     // have zombie host trackers, i.e. host tracker objects that live outside
index 3e521094a755fffdb69687391686b56926a02d59..50116f87e0a1410bb144f22459b455ae291ed9a9 100644 (file)
@@ -114,7 +114,7 @@ TEST(host_tracker, stringify)
     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_mac(mac2, 3, 1); // this primary mac should go to the back 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
@@ -127,8 +127,8 @@ TEST(host_tracker, stringify)
     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"
+        "\n    mac: CA:FE:C0:FF:EE:00, ttl: 3, primary: 1, time: 2019-07-04 00:00:07"
         "\nservices size: 2"
         "\n    port: 80, proto: 6, appid: 676, inferred"
         "\n    port: 443, proto: 6, appid: 1122");
index 3684996d56b070f6389d6006dfe807477ebd93aa..3ab0b729779976349a00232e339d9aa627493010 100644 (file)
@@ -18,6 +18,8 @@ set ( RNA_SOURCES
     rna_inspector.h
     rna_logger.cc
     rna_logger_common.h
+    rna_mac_cache.cc
+    rna_mac_cache.h
     rna_module.cc
     rna_module.h
     rna_pnd.cc
index 3fa1052fa2c798026587c65b4ac79996f918e766..d90c9aaa52ab1cef7987e31a619a43acd8d1a18e 100644 (file)
@@ -50,3 +50,32 @@ or reader is not implemented yet. However, since RNA stores host information int
 to log the discovered hosts into a file, one can
     1) issue socket command: host_cache.dump('file.out'), or
     2) add lua config: host_cache = { dump_file = 'file.out'}.
+
+RNA Uses HostCacheMac, derived from LruCacheSharedMemcap, to track MAC addresses. The implementation
+of the MAC cache is as follows:
+
+   HostCacheMac (LruCacheSharedMemcap<uint8_t*, LruMac, HashMac>)
+      |- - - - Key = uint8_t[6] (mac address)
+      |- - - - Data = LruMac
+      |                 | - - - - Valuetype = HostMacIp (derived from snort::HostMac)
+      |                 | - - - - Data = std::vector<ValueType, MacAllocator<ValueType>>
+      |
+      |- - - - Hash = HashMac
+      |                 |
+                        | - - - - Takes uint8_t[6], returns 64-bit hash of MAC. This allows us to
+                        |             hash a 48 bit value (MAC) to a 64 bit key with relatively
+                                      few collisions. AA:BB:CC:DD:EE:FF becomes 0xAABBCCDDEEFF
+
+In RNAPnd discover_network_ethernet, in some scenarios, we are required to create a host tracker to
+be used for exclusively for logging. The call chain is the following:
+    -> discover_network_ethernet
+        | discover_network_arp
+        | discover_network_bpdu
+            | discover_switch
+
+We pass a host tracker pointer by reference (HostTracker**) from discover_network_ethernet down to
+the lower-level calls. These functions (discover[network_arp|network_bpdu|switch]) are responsible
+for setting the top level pointer to point at their own instantiated host tracker, as it
+needs to be preserved until discover_network_ethernet calls generate_change_vlan_update with this
+newly-created host tracker as an argument. This host tracker is deleted at the top level, and we must
+not return prior to that to avoid leaking any host trackers.
index 3c66c9bb78f486411e992e7aa8ab2d3d2c859634..e1ddfe4c04d97f90a8dd6f5519d533ce4c3c407e 100644 (file)
@@ -96,8 +96,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);
-    UNUSED(p);
+    pnd->analyze_flow_non_ip(p);
 }
 
 void RnaInspector::show(const SnortConfig*) const
index 8110d8e59c8dc7aeb66d9f37fb71d84c8c55a936..01e3b568643ecc3c162df73f02f82734085cc031 100644 (file)
@@ -42,7 +42,7 @@ using namespace snort;
 
 bool RnaLogger::log(uint16_t type, uint16_t subtype, const Packet* p, RnaTracker* ht,
     const struct in6_addr* src_ip, const uint8_t* src_mac, uint32_t event_time,
-    void* cond_var)
+    void* cond_var, const HostMac* hm)
 {
     if ( !enabled )
         return false;
@@ -58,6 +58,9 @@ bool RnaLogger::log(uint16_t type, uint16_t subtype, const Packet* p, RnaTracker
             rle.cond_var = cond_var;
     }
 
+    if (hm)
+        rle.hm = hm;
+
     EventManager::call_loggers(nullptr, const_cast<Packet*>(p), "RNA", &rle);
     return true;
 }
index ddb44e795cb27fef2775e7c640cb6adbe4154624..7bfc487c329518234eb56a31655ea5a3920f8440 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "events/event.h"
 #include "host_tracker/host_cache.h"
+#include "host_tracker/host_tracker.h"
 
 namespace snort
 {
@@ -41,6 +42,7 @@ struct RnaLoggerEvent : public Event
     const uint8_t* mac;
     const struct in6_addr* ip = nullptr;
     void* cond_var = nullptr;
+    const snort::HostMac* hm;
 };
 
 class RnaLogger
@@ -49,7 +51,7 @@ public:
     RnaLogger(const bool enable) : enabled(enable) { }
     bool log(uint16_t type, uint16_t subtype, const snort::Packet* p, RnaTracker* ht,
        const struct in6_addr* src_ip, const uint8_t* src_mac,
-       uint32_t event_time = 0, void* cond_var = nullptr);
+       uint32_t event_time = 0, void* cond_var = nullptr, const snort::HostMac* hm = nullptr);
 
 private:
     const bool enabled;
index caf50599414d0a02f162a1945d1cae6a7b6d840d..ec06c93f46eeab0a8938fca8154b0da86dd7ce8f 100644 (file)
     #define NEW_HOST            1
 
 #define RNA_EVENT_CHANGE    1001
+    #define CHANGE_HOPS                 5
+    #define CHANGE_MAC_INFO            13
+    #define CHANGE_MAC_ADD             14
     #define CHANGE_HOST_UPDATE         15
+    #define CHANGE_VLAN_TAG            18
 
 #endif
diff --git a/src/network_inspectors/rna/rna_mac_cache.cc b/src/network_inspectors/rna/rna_mac_cache.cc
new file mode 100644 (file)
index 0000000..db7cdd1
--- /dev/null
@@ -0,0 +1,108 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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_mac_cache.cc author Michael Matirko <mmatirko@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "rna_mac_cache.h"
+
+#include <cassert>
+#include <vector>
+
+#ifdef UNIT_TEST
+#include "catch/snort_catch.h"
+#endif
+
+HostCacheMac host_cache_mac(MAC_CACHE_INITIAL_SIZE);
+
+void HostMacIp::update_vlan(uint16_t vth_pri_cfi_vlan, uint16_t vth_proto)
+{
+    vlan_tag_present = true;
+    vlan_tag.vth_pri_cfi_vlan = vth_pri_cfi_vlan;
+    vlan_tag.vth_proto = vth_proto;
+}
+
+bool HostMacIp::has_vlan()
+{
+    return vlan_tag_present;
+}
+
+uint16_t HostMacIp::get_vlan()
+{
+    return vlan_tag.vth_pri_cfi_vlan;
+}
+
+void HostMacIp::get_vlan_details(uint8_t& cfi, uint8_t& priority, uint16_t& vid)
+{
+    cfi = vlan_tag.cfi();
+    priority = vlan_tag.priority();
+    vid = vlan_tag.vid();
+}
+
+#ifdef UNIT_TEST
+TEST_CASE("RNA Mac Cache", "[rna_mac_cache]")
+{
+    SECTION("HostCacheMac: store, retrieve")
+    {
+        uint8_t a_mac[6] = {0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6};
+        uint8_t b_mac[6] = {0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6};
+        uint8_t c_mac[6] = {0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6};
+
+        uint8_t test_mac[6] = {0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6};
+
+        MacKey a(a_mac);
+        MacKey b(b_mac);
+        MacKey c(c_mac);
+        MacKey test(test_mac);
+
+        HostMacIp hm_b(b_mac, 1, 0);
+
+        bool new_host = false;
+        bool new_host_test = false;
+
+        auto a_ptr = host_cache_mac.find_else_create(a, &new_host);
+        assert(new_host);
+        assert(a_ptr);
+
+        auto b_ptr = host_cache_mac.find_else_create(b, &new_host);
+        assert(new_host);
+        b_ptr->insert(&hm_b);
+
+        auto c_ptr = host_cache_mac.find_else_create(c, &new_host);
+        assert(new_host);
+        assert(c_ptr);
+
+        // Try to add one of the previous macs again
+        auto test_ptr = host_cache_mac.find_else_create(test, &new_host_test);
+        assert(test_ptr);
+        assert(!new_host_test);
+
+        // Verify that test_ptr contains the host_mac we're looking for
+        assert(!memcmp(test_ptr->data.front().mac, b_mac, MAC_SIZE));
+    }
+
+    SECTION("HostCacheMac: MAC Hashing")
+    {
+        uint8_t a_mac[6] = {0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6};
+        uint64_t hash = snort::hash_mac(a_mac);
+        assert(hash == 0xA1A2A3A4A5A6);
+    }
+}
+#endif
diff --git a/src/network_inspectors/rna/rna_mac_cache.h b/src/network_inspectors/rna/rna_mac_cache.h
new file mode 100644 (file)
index 0000000..f916ea5
--- /dev/null
@@ -0,0 +1,120 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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_mac_cache.h author Michael Matirko <mmatirko@cisco.com>
+
+#ifndef RNA_MAC_CACHE
+#define RNA_MAC_CACHE
+
+#include "hash/hash_key_operations.h"
+// Non-standard, required to include template definition across compiler translation units
+#include "host_tracker/host_cache_allocator.cc"
+#include "host_tracker/host_cache_allocator.h"
+#include "host_tracker/host_cache.h"
+#include "host_tracker/host_tracker.h"
+#include "sfip/sf_ip.h"
+
+using snort::SfIp;
+
+#define MAC_CACHE_INITIAL_SIZE 1024 * 1024 // Default to 1 MB
+
+class HostMacIp : public snort::HostMac
+{
+public:
+    HostMacIp (const uint8_t* p_mac, uint8_t p_primary, uint32_t p_last_seen)
+        : snort::HostMac(0, p_mac, p_primary, p_last_seen), src_ip(nullptr), dst_ip(nullptr)
+    { }
+
+    HostMacIp (const uint8_t* p_mac, uint8_t p_primary, uint32_t p_last_seen,
+        SfIp* src, SfIp* dst) : snort::HostMac(0, p_mac, p_primary, p_last_seen),
+        src_ip(src), dst_ip(dst)
+    { }
+
+    SfIp* src_ip;
+    SfIp* dst_ip;
+
+    snort::HostType host_type;
+
+    void update_vlan(uint16_t vth_pri_cfi_vlan, uint16_t vth_proto);
+    bool has_vlan();
+    uint16_t get_vlan();
+    void get_vlan_details(uint8_t& cfi, uint8_t& priority, uint16_t& vid);
+
+private:
+    bool vlan_tag_present = false;
+    snort::vlan::VlanTagHdr vlan_tag;
+};
+
+class MacKey
+{
+public:
+    MacKey(const uint8_t *ma)
+    { memcpy(mac_addr, ma, MAC_SIZE); }
+
+    bool operator==(const MacKey& mk) const
+    {
+        return !(memcmp(mac_addr, mk.mac_addr, MAC_SIZE));
+    }
+
+    uint8_t mac_addr[MAC_SIZE];
+};
+
+struct HashMac
+{
+    uint64_t operator()(const MacKey& mk) const
+    { return snort::hash_mac(mk.mac_addr); }
+};
+
+template <class T>
+class HostCacheAllocMac : public HostCacheAlloc<T>
+{
+public:
+    template <class U>
+    struct rebind
+    {
+        typedef HostCacheAllocMac<U> other;
+    };
+
+    using HostCacheAlloc<T>::lru;
+
+    HostCacheAllocMac();
+};
+
+class LruMac
+{
+public:
+    typedef HostMacIp ValueType;
+    std::vector<ValueType, HostCacheAllocMac<ValueType>> data;
+
+    bool isempty()
+    { return data.size() == 0; }
+
+    void insert(HostMacIp *hmi)
+    { data.emplace_back(*hmi); }
+
+};
+
+typedef LruCacheSharedMemcap<MacKey, LruMac, HashMac> HostCacheMac;
+extern HostCacheMac host_cache_mac;
+
+template <class T>
+HostCacheAllocMac<T>::HostCacheAllocMac()
+{
+    lru = &host_cache_mac;
+}
+#endif
index 47bcdb99910f278a7028faf5f6f428854e7cd949..462ca62ac942c6547c013ecfcbd7761e4cd1690b 100644 (file)
 #include "rna_module.h"
 
 #include <cassert>
+#include <fstream>
+#include <iomanip>
+#include <sstream>
+#include <sys/stat.h>
 
 #include "log/messages.h"
+#include "lua/lua.h"
 #include "main/snort_config.h"
 #include "main/swapper.h"
 #include "managers/inspector_manager.h"
+#include "managers/module_manager.h"
 #include "src/main.h"
+#include "utils/util.h"
+
+#include "rna_mac_cache.h"
 
 #ifdef UNIT_TEST
 #include "catch/snort_catch.h"
@@ -41,6 +50,13 @@ using namespace snort;
 //-------------------------------------------------------------------------
 // rna commands, params, and pegs
 //-------------------------------------------------------------------------
+static int dump_mac_cache(lua_State* L)
+{
+    RnaModule* mod = (RnaModule*) ModuleManager::get_module(RNA_NAME);
+    if ( mod )
+        mod->log_mac_cache( luaL_optstring(L, 1, nullptr) );
+    return 0;
+}
 
 static int reload_fingerprint(lua_State*)
 {
@@ -73,10 +89,27 @@ static int reload_fingerprint(lua_State*)
     return 0;
 }
 
+std::string format_dump_mac(uint8_t mac[MAC_SIZE])
+{
+    std::stringstream ss;
+    ss << std::hex;
+
+    for(int i=0; i < MAC_SIZE; i++)
+    {
+        ss << std::setfill('0') << std::setw(2) << static_cast<int>(mac[i]);
+        if (i != MAC_SIZE - 1)
+            ss << ":";
+    }
+
+    return ss.str();
+}
+
 static const Command rna_cmds[] =
 {
     { "reload_fingerprint", reload_fingerprint, nullptr,
       "reload rna database of fingerprint patterns/signatures" },
+    { "dump_macs", dump_mac_cache, nullptr,
+      "dump rna's internal MAC trackers" },
     { nullptr, nullptr, nullptr, nullptr }
 };
 
@@ -94,6 +127,9 @@ static const Parameter rna_params[] =
     { "log_when_idle", Parameter::PT_BOOL, nullptr, "false",
       "enable host update logging when snort is idle" },
 
+    { "dump_file", Parameter::PT_STRING, nullptr, nullptr,
+      "file name to dump RNA mac cache on shutdown; won't dump by default" },
+
     { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
 };
 
@@ -122,6 +158,12 @@ RnaModule::RnaModule() : Module(RNA_NAME, RNA_HELP, rna_params)
 
 RnaModule::~RnaModule()
 {
+    if ( dump_file )
+    {
+        log_mac_cache(dump_file);
+        snort_free((void*)dump_file);
+    }
+
     delete mod_conf;
 }
 
@@ -144,6 +186,12 @@ bool RnaModule::set(const char*, Value& v, SnortConfig*)
         mod_conf->enable_logger = v.get_bool();
     else if (v.is("log_when_idle"))
         mod_conf->log_when_idle = v.get_bool();
+    else if ( v.is("dump_file") )
+    {
+        if ( dump_file )
+            snort_free((void*)dump_file);
+        dump_file = snort_strdup(v.get_string());
+    }
     else
         return false;
 
@@ -187,6 +235,48 @@ const PegInfo* RnaModule::get_pegs() const
 ProfileStats* RnaModule::get_profile() const
 { return &rna_perf_stats; }
 
+bool RnaModule::log_mac_cache(const char* outfile)
+{
+    if ( !outfile )
+    {
+        LogMessage("File name is needed!\n");
+        return 0;
+    }
+
+    struct stat file_stat;
+    if ( stat(outfile, &file_stat) == 0 )
+    {
+        LogMessage("File %s already exists!\n", outfile);
+        return 0;
+    }
+
+    std::ofstream out_stream(outfile);
+    if ( !out_stream )
+    {
+        snort::LogMessage("Error opening %s for dumping MAC cache", outfile);
+    }
+
+    std::string str;
+    const auto&& lru_data = host_cache_mac.get_all_data();
+    out_stream << "Current mac cache size: " << host_cache_mac.mem_size() << " bytes, "
+        << lru_data.size() << " trackers" << std::endl << std::endl;
+    for ( const auto& elem : lru_data )
+    {
+        str = "MAC: ";
+
+        if (elem.second->data.size() > 0)
+            str += format_dump_mac(elem.second->data.front().mac);
+        else
+            str += "No MacHostTracker found for entry ";
+
+        str += "\n Key: " +  std::to_string(hash_mac(elem.second->data.front().mac));
+        out_stream << str << std::endl << std::endl;
+    }
+    out_stream.close();
+
+    return 0;
+}
+
 #ifdef UNIT_TEST
 TEST_CASE("RNA module", "[rna_module]")
 {
index 9fc20dd032b5abbb75c4d030880269c6e152a01e..ece385d07e67d0914148a9bc21df0b7feb4872c1 100644 (file)
@@ -29,6 +29,8 @@
 #define RNA_NAME "rna"
 #define RNA_HELP "Real-time network awareness and OS fingerprinting (experimental)"
 
+std::string format_dump_mac(uint8_t mac[6]);
+
 struct RnaStats
 {
     PegCount icmp_bidirectional;
@@ -56,6 +58,7 @@ public:
     bool begin(const char*, int, snort::SnortConfig*) override;
     bool set(const char*, snort::Value&, snort::SnortConfig*) override;
     bool end(const char*, int, snort::SnortConfig*) override;
+    bool log_mac_cache(const char* outfile);
 
     const snort::Command* get_commands() const override;
     RnaModuleConfig* get_config();
@@ -68,6 +71,8 @@ public:
 
 private:
     RnaModuleConfig* mod_conf = nullptr;
+    const char* dump_file = nullptr;
+
 };
 
 #endif
index 991f4069c6dd09404296555f8d62a3c91cab287c..fe217a47cc1bf78a298ddbc1dc91d2e959c2c50e 100644 (file)
 
 #include "rna_pnd.h"
 
-#include "protocols/eth.h"
+#include <algorithm>
+
+#include "protocols/arp.h"
+#include "protocols/bpdu.h"
 #include "protocols/icmp4.h"
 #include "protocols/packet.h"
+#include "protocols/protocol_ids.h"
 #include "protocols/tcp.h"
 
 #include "rna_logger_common.h"
@@ -38,6 +42,7 @@
 #endif
 
 using namespace snort;
+using namespace bpdu;
 
 static inline bool is_eligible_packet(const Packet* p)
 {
@@ -125,7 +130,7 @@ void RnaPnd::discover_network_ip(const Packet* p)
 void RnaPnd::discover_network_non_ip(const Packet* p)
 {
     // process rna discovery for non-ip in mac cache
-    UNUSED(p);
+    discover_network_ethernet(p);
 }
 
 void RnaPnd::discover_network_tcp(const Packet* p)
@@ -147,22 +152,104 @@ void RnaPnd::discover_network_udp(const Packet* p)
 void RnaPnd::discover_network(const Packet* p, uint8_t ttl)
 {
     bool new_host = false;
+    bool new_mac = 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);
+
+    new_mac = 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);
     }
-    else if ( update_timeout )
+
+    if ( new_mac and !new_host )
+        logger.log(RNA_EVENT_CHANGE, CHANGE_MAC_ADD, p, &ht,
+            (const struct in6_addr*) src_ip->get_ip6_ptr(), src_mac,
+            0, nullptr, ht->get_hostmac(src_mac));
+
+    if ( ht->update_mac_ttl(src_mac, ttl) )
+    {
+        logger.log(RNA_EVENT_CHANGE, CHANGE_MAC_INFO, p, &ht,
+            (const struct in6_addr*) src_ip->get_ip6_ptr(), src_mac,
+            0, nullptr, ht->get_hostmac(src_mac));
+
+        HostMac* hm = ht->get_max_ttl_hostmac();
+        if (hm and hm->primary and ht->get_hops())
+        {
+            ht->update_hops(0);
+            logger.log(RNA_EVENT_CHANGE, CHANGE_HOPS, p, &ht,
+                (const struct in6_addr*) src_ip->get_ip6_ptr(), src_mac);
+        }
+    }
+
+    if ( !new_host )
+    {
         generate_change_host_update(&ht, p, src_ip, src_mac, packet_time());
+    }
+}
+
+inline void RnaPnd::update_vlan(const Packet* p, HostMacIp& hm)
+{
+    if (!(p->proto_bits & PROTO_BIT__VLAN))
+        return;
 
+    const vlan::VlanTagHdr* vh = layer::get_vlan_layer(p);
+
+    if (vh)
+        hm.update_vlan(vh->vth_pri_cfi_vlan, vh->vth_proto);
+}
+
+void RnaPnd::generate_change_vlan_update(RnaTracker *rt, const Packet* p,
+    const uint8_t* src_mac, HostMacIp& hm, bool isnew)
+{
+    if (!(p->proto_bits & PROTO_BIT__VLAN))
+        return;
+
+    const vlan::VlanTagHdr* vh = layer::get_vlan_layer(p);
+
+    if (!vh)
+        return;
+
+    if (isnew or !hm.has_vlan() or hm.get_vlan() != vh->vth_pri_cfi_vlan)
+    {
+        time_t timestamp = packet_time() - update_timeout;
+
+        if (!isnew)
+            update_vlan(p, hm);
+
+        rt->get()->update_vlan(vh->vth_pri_cfi_vlan, vh->vth_proto);
+        logger.log(RNA_EVENT_CHANGE, CHANGE_VLAN_TAG, p, rt, nullptr,
+            src_mac, rt->get()->get_last_seen(), (void*) &timestamp);
+    }
+}
+
+void RnaPnd::generate_change_vlan_update(RnaTracker *rt, const Packet* p,
+    const uint8_t* src_mac, const SfIp* src_ip, bool isnew)
+{
+    if (!(p->proto_bits & PROTO_BIT__VLAN))
+        return;
+
+    const vlan::VlanTagHdr* vh = layer::get_vlan_layer(p);
+
+    if (!vh)
+        return;
+
+    if (isnew or !rt->get()->has_vlan() or rt->get()->get_vlan() != vh->vth_pri_cfi_vlan)
+    {
+        time_t timestamp = packet_time() - update_timeout;
+
+        rt->get()->update_vlan(vh->vth_pri_cfi_vlan, vh->vth_proto);
+        logger.log(RNA_EVENT_CHANGE, CHANGE_VLAN_TAG, p, rt,
+            (const struct in6_addr*) src_ip->get_ip6_ptr(),
+            src_mac, rt->get()->get_last_seen(), (void*) &timestamp);
+    }
 }
 
 void RnaPnd::generate_change_host_update(RnaTracker* ht, const Packet* p,
@@ -191,6 +278,220 @@ void RnaPnd::generate_change_host_update()
         generate_change_host_update(&h.second, nullptr, &h.first, nullptr, sec);
 }
 
+void RnaPnd::generate_new_host_mac(const Packet* p, RnaTracker ht)
+{
+    // In general, this is the default case for mac eventing.
+    // Ex. if BPDU dsap, ssap checks fail, we fallback here to
+    // generate a new_host event
+
+    bool new_host_mac = false;
+    MacKey mk(layer::get_eth_layer(p)->ether_src);
+
+    auto hm_ptr = host_cache_mac.find_else_create(mk, &new_host_mac);
+
+    if (new_host_mac)
+    {
+        HostMacIp hm(mk.mac_addr, 0, p->pkth->ts.tv_sec);
+
+        update_vlan(p, hm);
+        hm_ptr->insert(&hm);
+
+        ht.get()->update_last_seen();
+        ht.get()->add_mac(mk.mac_addr, 0, 0);
+
+        logger.log(RNA_EVENT_NEW, NEW_HOST, p, &ht,
+            (const struct in6_addr*) nullptr, mk.mac_addr);
+
+        generate_change_vlan_update(&ht, p, mk.mac_addr, hm, true);
+    }
+    else
+    {
+        if (hm_ptr and !hm_ptr->isempty())
+        {
+            generate_change_vlan_update(&ht, p, mk.mac_addr, hm_ptr->data.front(), false);
+        }
+    }
+}
+
+// RNA Flow Tracking for non-IP connections (ARP, BPDU, CDP)
+void RnaPnd::discover_network_ethernet(const Packet* p)
+{
+    #define BPDU_ID 0x42
+    #define SNAP_ID 0xAA
+    int retval = 1;
+    HostTracker* ht = new HostTracker();
+    RnaTracker rt = std::shared_ptr<snort::HostTracker>(ht);
+
+    if (!p->is_eth())
+        return;
+
+    if (layer::get_arp_layer(p))
+    {
+        retval = discover_network_arp(p, &rt);
+    }
+
+    // If we have an inner LLC layer, grab it
+    Layer lyr = p->layers[p->num_layers-1];
+    if (p->layers[p->num_layers-1].prot_id == ProtocolId::ETHERNET_LLC)
+    {
+        uint16_t etherType = rna_get_eth(p);
+
+        if (!etherType || etherType > static_cast<uint16_t>(ProtocolId::ETHERTYPE_MINIMUM))
+        {
+            generate_new_host_mac(p, rt);
+            return;
+        }
+
+        const RNA_LLC* llc = (const RNA_LLC*) lyr.start;
+
+        if (llc->s.s.DSAP != llc->s.s.SSAP)
+        {
+            generate_new_host_mac(p, rt);
+            return;
+        }
+
+        switch (llc->s.s.DSAP)
+        {
+            case BPDU_ID:
+            {
+                retval = discover_network_bpdu(p, ((const uint8_t*)llc + sizeof(RNA_LLC)), rt);
+                break;
+            }
+
+            default:
+                break;
+        }
+    }
+
+    if (retval)
+        generate_new_host_mac(p, rt);
+
+    return;
+}
+
+int RnaPnd::discover_network_arp(const Packet* p, RnaTracker* ht_ref)
+{
+    bool new_host_mac = false;
+    bool new_host = false;
+
+    MacKey mk(layer::get_eth_layer(p)->ether_src);
+    const auto& src_mac = mk.mac_addr;
+    HostMacIp hm(mk.mac_addr, 0, p->pkth->ts.tv_sec);
+
+    const snort::arp::EtherARP *ah = layer::get_arp_layer(p);
+
+    if (ntohs(ah->ea_hdr.ar_hrd) != 0x0001)
+        return 1;
+    if (ntohs(ah->ea_hdr.ar_pro) != 0x0800)
+        return 1;
+    if (ah->ea_hdr.ar_hln != 6 || ah->ea_hdr.ar_pln != 4)
+        return 1;
+    if ((ntohs(ah->ea_hdr.ar_op) != 0x0002))
+        return 1;
+    if (memcmp(src_mac, ah->arp_sha, MAC_SIZE))
+        return 1;
+    if (!ah->arp_spa32)
+        return 1;
+
+    SfIp spa(ah->arp_spa, AF_INET);
+
+    // In the case where SPA is not monitored, log as a generic "NEW MAC"
+    if ( !(filter.is_host_monitored(p, nullptr, &spa) ))
+        return 1;
+
+    auto ht = host_cache.find_else_create(spa, &new_host);
+
+    std::shared_ptr<LruMac> hm_ptr = host_cache_mac.find_else_create(mk, &new_host_mac);
+
+    if (new_host_mac)
+        hm_ptr->insert(&hm);
+
+    *ht_ref = ht;
+
+    if( new_host )
+    {
+        ht->update_hops(255);
+        ht->add_mac(src_mac, 0, 0);
+        logger.log(RNA_EVENT_NEW, NEW_HOST, p, &ht,
+            (const struct in6_addr*) spa.get_ip6_ptr(), src_mac);
+    }
+
+    if ( ht->add_mac(src_mac, 0, 0) )
+    {
+        logger.log(RNA_EVENT_CHANGE, CHANGE_MAC_ADD, p, ht_ref,
+            (const struct in6_addr*) spa.get_ip6_ptr(), src_mac,
+            0, nullptr, ht->get_hostmac(src_mac));
+    }
+    else if (ht->make_primary(src_mac))
+    {
+        logger.log(RNA_EVENT_CHANGE, CHANGE_MAC_INFO, p, ht_ref,
+            (const struct in6_addr*) spa.get_ip6_ptr(), src_mac,
+            0, nullptr, ht->get_hostmac(src_mac));
+    }
+
+    generate_change_vlan_update(&ht, p, src_mac, &spa, true);
+
+    if ( ht->get_hops() )
+    {
+        ht->update_hops(0);
+        logger.log(RNA_EVENT_CHANGE, CHANGE_HOPS, p, ht_ref,
+            (const struct in6_addr*) spa.get_ip6_ptr(), src_mac, 0, nullptr,
+             ht->get_hostmac(src_mac));
+    }
+
+    return 0;
+}
+
+int RnaPnd::discover_network_bpdu(const Packet* p, const uint8_t* data,
+    RnaTracker ht_ref)
+{
+    const uint8_t* dst_mac = layer::get_eth_layer(p)->ether_dst;
+
+    const BPDUData* stp;
+
+    if (!isBPDU(dst_mac))
+        return 1;
+    stp = reinterpret_cast<const BPDUData*>(data);
+    if (stp->id || stp->version)
+        return 1;
+    if (stp->type !=  BPDU_TYPE_TOPCHANGE)
+        return 1;
+
+    return discover_switch(p, ht_ref);
+}
+
+int RnaPnd::discover_switch(const Packet* p, RnaTracker ht_ref)
+{
+    bool new_host = false;
+    MacKey mk(layer::get_eth_layer(p)->ether_src);
+
+    std::shared_ptr<LruMac> hm_ptr = host_cache_mac.find_else_create(mk, &new_host);
+
+    if (new_host)
+    {
+        HostMacIp hm(mk.mac_addr, 0, p->pkth->ts.tv_sec);
+
+        hm.host_type = HOST_TYPE_BRIDGE;
+
+        update_vlan(p, hm);
+        hm_ptr->insert(&hm);
+
+        ht_ref.get()->update_last_seen();
+        ht_ref.get()->add_mac(mk.mac_addr, 0, 1);
+
+        logger.log(RNA_EVENT_NEW, NEW_HOST, p, &ht_ref,
+            (const struct in6_addr*) nullptr, mk.mac_addr);
+
+        generate_change_vlan_update(&ht_ref, p, mk.mac_addr, hm, true);
+    }
+    else
+    {
+        generate_change_vlan_update(&ht_ref, p, mk.mac_addr, hm_ptr->data.front(), false);
+    }
+
+    return 0;
+}
+
 #ifdef UNIT_TEST
 TEST_CASE("RNA pnd", "[non-ip]")
 {
index 1fbbe3bce0dae040e2bdbca4bafe3cfb17d69a17..47833ad4363d45f76e93aadb51648ccc1019d200 100644 (file)
 #ifndef RNA_PND_H
 #define RNA_PND_H
 
+#include <limits>
+
 #include "helpers/discovery_filter.h"
+#include "host_tracker/host_tracker.h"
+#include "protocols/eth.h"
+#include "protocols/layer.h"
+#include "protocols/vlan.h"
+#include "sfip/sf_ip.h"
 
+#include "rna_mac_cache.h"
 #include "rna_logger.h"
-#include "sfip/sf_ip.h"
+
+#define USHRT_MAX std::numeric_limits<unsigned short>::max()
 
 namespace snort
 {
@@ -35,6 +44,37 @@ enum class TcpPacketType
     SYN, SYN_ACK, MIDSTREAM
 };
 
+#pragma pack(1)
+struct RNA_LLC
+{
+    union
+    {
+        struct
+        {
+            uint8_t DSAP;
+            uint8_t SSAP;
+        } s;
+        uint16_t proto;
+    } s;
+    uint8_t flags;
+};
+#pragma pack()
+
+static inline unsigned short rna_get_eth(const snort::Packet* p)
+{
+    const snort::vlan::VlanTagHdr* vh = nullptr;
+    const snort::eth::EtherHdr* eh = nullptr;
+
+    if (p->proto_bits & PROTO_BIT__VLAN)
+        vh = snort::layer::get_vlan_layer(p);
+
+    if (vh)
+        return ntohs(vh->vth_proto);
+    else if ((eh = snort::layer::get_eth_layer(p)))
+        return ntohs(eh->ether_type);
+    return USHRT_MAX;
+}
+
 class RnaPnd
 {
 public:
@@ -55,6 +95,15 @@ public:
     // generate change event for all hosts in the ip cache
     void generate_change_host_update();
 
+    // Change vlan event related utilities
+    inline void update_vlan(const snort::Packet* p, HostMacIp& hm);
+    void generate_change_vlan_update(RnaTracker *rt, const snort::Packet* p,
+        const uint8_t* src_mac, HostMacIp& hm, bool isnew);
+    void generate_change_vlan_update(RnaTracker *rt, const snort::Packet* p,
+        const uint8_t* src_mac, const SfIp* src_ip, bool isnew);
+
+    void generate_new_host_mac(const snort::Packet* p, RnaTracker ht);
+
 private:
     // General rna utilities not associated with flow
     void discover_network_icmp(const snort::Packet* p);
@@ -64,6 +113,12 @@ private:
     void discover_network_udp(const snort::Packet* p);
     void discover_network(const snort::Packet* p, uint8_t ttl);
 
+    // RNA utilities for non-IP packets
+    void discover_network_ethernet(const snort::Packet* p);
+    int discover_network_arp(const snort::Packet* p, RnaTracker* ht_ref);
+    int discover_network_bpdu(const snort::Packet* p, const uint8_t* data, RnaTracker ht_ref);
+    int discover_switch(const snort::Packet* p, RnaTracker ht_ref);
+
     RnaLogger logger;
     DiscoveryFilter filter;
     time_t update_timeout;
index 0724eb009940e333c3091e4410b7faab1d46224f..d7baad34e29331915ffc089d63e7293976e75774 100644 (file)
 #ifndef RNA_MODULE_MOCK_H
 #define RNA_MODULE_MOCK_H
 
+#include "../rna_mac_cache.cc"
+
 bool Swapper::reload_in_progress = false;
 THREAD_LOCAL RnaStats rna_stats;
 THREAD_LOCAL ProfileStats rna_perf_stats;
 static std::string message;
 static Request mock_request;
 
+const char* luaL_optlstring(lua_State*, int, const char*, size_t*) { return nullptr; }
+
 void Request::respond(const char* msg, bool, bool)
 {
     message = msg;
@@ -38,6 +42,13 @@ namespace snort
 {
 Inspector* InspectorManager::get_inspector(const char*, bool, const SnortConfig*)
 { return nullptr; }
+
+Module* ModuleManager::get_module(const char*)
+{ return nullptr; }
+
+char* snort_strdup(const char* s)
+{ return strdup(s); }
+
 Module::Module(const char*, const char*, const Parameter*, bool) {}
 void Module::sum_stats(bool) {}
 void Module::show_stats() {}
index 20390609d51b55142305a6d9a26b471e7d1df5cf..82e0d01a33802dc8e8f197a3e692a4a46f5b59f8 100644 (file)
@@ -1,6 +1,7 @@
 
 set (PROTOCOL_HEADERS
     arp.h
+    bpdu.h
     cisco_meta_data.h
     eapol.h
     eth.h
diff --git a/src/protocols/bpdu.h b/src/protocols/bpdu.h
new file mode 100644 (file)
index 0000000..d6ed773
--- /dev/null
@@ -0,0 +1,52 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+// bpdu.h author Michael Matirko <mmatirko@cisco.com>
+
+// Represents the BPDU (bridge protocol data unit) payload used for Spanning Tree Protocol
+
+#ifndef PROTOCOLS_BPDU_H
+#define PROTOCOLS_BPDU_H
+
+namespace snort
+{
+namespace bpdu
+{
+
+#define BPDU_TYPE_TOPCHANGE  0x80    // Topology change type BPDU
+
+static const uint8_t BPDU_DEST[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x00};
+
+bool isBPDU(const uint8_t mac[6]);
+
+struct BPDUData
+{
+    uint16_t id;
+    uint8_t version;
+    uint8_t type;
+} __attribute__((__packed__));
+
+
+bool isBPDU(const uint8_t mac[6])
+{
+    return (memcmp(mac, BPDU_DEST, sizeof(BPDU_DEST)) == 0);
+}
+
+} // namespace bpdu
+} // namespace snort
+
+#endif