]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #2513 in SNORT/snort3 from ~DAVMCPHE/snort3:rna_host_type_discover...
authorMasud Hasan (mashasan) <mashasan@cisco.com>
Fri, 2 Oct 2020 17:18:59 +0000 (17:18 +0000)
committerMasud Hasan (mashasan) <mashasan@cisco.com>
Fri, 2 Oct 2020 17:18:59 +0000 (17:18 +0000)
Squashed commit of the following:

commit 52c06b3d7bc98f14eddab2d70efa5fe8df3a486a
Author: davis mcpherson <davmcphe@cisco.com>
Date:   Wed Sep 16 15:50:43 2020 -0400

    rna: port host type discovery logic

src/host_tracker/host_tracker.cc
src/host_tracker/host_tracker.h
src/host_tracker/test/host_tracker_test.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_pnd.cc
src/network_inspectors/rna/rna_pnd.h
src/protocols/CMakeLists.txt
src/protocols/cdp.h [new file with mode: 0644]

index 1c818a555f11b29f38602c36f16b7fc41c3a8482..98fc6a659d5a2d4a269609a4ab83107386fa9365 100644 (file)
@@ -85,18 +85,21 @@ bool HostTracker::add_mac(const uint8_t* mac, uint8_t ttl, uint8_t primary)
     return true;
 }
 
-const HostMac* HostTracker::get_hostmac(const uint8_t* mac)
+bool HostTracker::get_hostmac(const uint8_t* mac, HostMac& hm)
 {
     if ( !mac or !memcmp(mac, zero_mac, MAC_SIZE) )
-        return nullptr;
+        return false;
 
     lock_guard<mutex> lck(host_tracker_lock);
 
-    for ( const auto& hm : macs )
-        if ( !memcmp(mac, hm.mac, MAC_SIZE) )
-            return &hm;
+    for ( auto& ahm : macs )
+        if ( !memcmp(mac, ahm.mac, MAC_SIZE) )
+        {
+            hm = ahm;
+            return true;
+        }
 
-    return nullptr;
+    return false;
 }
 
 const uint8_t* HostTracker::get_last_seen_mac()
@@ -155,7 +158,8 @@ bool HostTracker::make_primary(const uint8_t* mac)
     if ( !hm )
         return false;
 
-    if (!hm->primary)
+    hm->last_seen = last_seen;
+    if ( !hm->primary )
     {
         hm->primary = true;
         return true;
@@ -176,7 +180,7 @@ HostMac* HostTracker::get_max_ttl_hostmac()
         if (hm.primary)
             return &hm;
 
-        if (hm.ttl > max_ttl)
+        if ( hm.ttl > max_ttl )
         {
             max_ttl = hm.ttl;
             max_ttl_hm = &hm;
@@ -479,9 +483,9 @@ HostClient HostTracker::get_client(AppId id, const char* version, AppId service,
 
     for ( const auto& c : clients )
     {
-        if (c.id != APP_ID_NONE and c.id == id and c.service == service
+        if ( c.id != APP_ID_NONE and c.id == id and c.service == service
             and ((c.version[0] == '\0' and !version) or
-            (version and strncmp(c.version, version, INFO_SIZE) == 0)))
+            (version and strncmp(c.version, version, INFO_SIZE) == 0)) )
         {
             return c;
         }
@@ -523,11 +527,19 @@ static inline string to_mac_string(const uint8_t* mac)
     return mac_addr;
 }
 
+static std::vector<std::string> host_types = { "Host", "Router", "Bridge", "NAT", "Load Balancer" };
+
+static inline string& to_host_type_string(HostType type)
+{
+    return host_types[type];
+}
+
 void HostTracker::stringify(string& str)
 {
     lock_guard<mutex> lck(host_tracker_lock);
 
-    str += "\n    hops: " + to_string(hops) + ", time: " + to_time_string(last_seen);
+    str += "\n    type: " + to_host_type_string(host_type) + ", ttl: " + to_string(ip_ttl)
+        + ", hops: " + to_string(hops) + ", time: " + to_time_string(last_seen);
 
     if ( !macs.empty() )
     {
index 4302e9d68d17244c66dd575c4d546d5200eaf2e8..1904e6cfa568eb020aaf70f1623ee10208702160 100644 (file)
@@ -56,6 +56,9 @@ extern const uint8_t zero_mac[MAC_SIZE];
 
 struct HostMac
 {
+    HostMac() : ttl(0), primary(0), last_seen(0)
+    { memset(mac, 0, MAC_SIZE); }
+
     HostMac(uint8_t p_ttl, const uint8_t* p_mac, uint8_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); }
 
@@ -110,15 +113,18 @@ struct DeviceFingerprint
     char device[INFO_SIZE] = { 0 };
 };
 
-enum HostType
+enum HostType : std::uint32_t
 {
-    HOST_TYPE_HOST=0,
+    HOST_TYPE_HOST = 0,
     HOST_TYPE_ROUTER,
     HOST_TYPE_BRIDGE,
     HOST_TYPE_NAT,
     HOST_TYPE_LB
 };
 
+#define MIN_BOOT_TIME    10
+#define MIN_TTL_DIFF     16
+
 typedef HostCacheAllocIp<HostMac> HostMacAllocator;
 typedef HostCacheAllocIp<HostApplication> HostAppAllocator;
 typedef HostCacheAllocIp<HostClient> HostClientAllocator;
@@ -129,7 +135,7 @@ class SO_PUBLIC HostTracker
 public:
     HostTracker() : hops(-1)
     {
-        last_seen = (uint32_t) packet_time();
+        last_seen = nat_count_start = (uint32_t) packet_time();
         last_event = -1;
     }
 
@@ -165,6 +171,12 @@ public:
         host_type = rht;
     }
 
+    HostType get_host_type() const
+    {
+        std::lock_guard<std::mutex> lck(host_tracker_lock);
+        return host_type;
+    }
+
     uint8_t get_hops()
     {
         std::lock_guard<std::mutex> lck(host_tracker_lock);
@@ -189,8 +201,8 @@ public:
     // 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);
+    // Returns true and copy of the matching HostMac, false if no match...
+    bool get_hostmac(const uint8_t* mac, HostMac& hm);
 
     const uint8_t* get_last_seen_mac();
 
@@ -234,6 +246,48 @@ public:
     //  This should be updated whenever HostTracker data members are changed
     void stringify(std::string& str);
 
+    uint8_t get_ip_ttl() const
+    {
+        std::lock_guard<std::mutex> lck(host_tracker_lock);
+        return ip_ttl;
+    }
+
+    void set_ip_ttl(uint8_t ttl)
+    {
+        std::lock_guard<std::mutex> lck(host_tracker_lock);
+        ip_ttl = ttl;
+    }
+
+    uint32_t get_nat_count_start() const
+    {
+        std::lock_guard<std::mutex> lck(host_tracker_lock);
+        return nat_count_start;
+    }
+
+    void set_nat_count_start(uint32_t natCountStart)
+    {
+        std::lock_guard<std::mutex> lck(host_tracker_lock);
+        nat_count_start = natCountStart;
+    }
+
+    uint32_t get_nat_count() const
+    {
+        std::lock_guard<std::mutex> lck(host_tracker_lock);
+        return nat_count;
+    }
+
+    void set_nat_count(uint32_t v = 0)
+    {
+        std::lock_guard<std::mutex> lck(host_tracker_lock);
+        nat_count = v;
+    }
+
+    uint32_t inc_nat_count()
+    {
+        std::lock_guard<std::mutex> lck(host_tracker_lock);
+        return ++nat_count;
+    }
+
 private:
     mutable 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
@@ -249,7 +303,10 @@ private:
 
     bool vlan_tag_present = false;
     vlan::VlanTagHdr vlan_tag;
-    HostType host_type;
+    HostType host_type = HOST_TYPE_HOST;
+    uint8_t ip_ttl = 0;
+    uint32_t nat_count = 0;
+    uint32_t nat_count_start;     // the time nat counting start for this host
 
     // 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 50116f87e0a1410bb144f22459b455ae291ed9a9..1e7dd10a54227945ddf557e4900d7672acc6b8c5 100644 (file)
@@ -125,7 +125,7 @@ TEST(host_tracker, stringify)
     ht.stringify(host_tracker_string);
 
     STRCMP_EQUAL(host_tracker_string.c_str(),
-        "\n    hops: 255, time: 2019-07-04 00:00:09"
+        "\n    type: Host, ttl: 0, hops: 255, time: 2019-07-04 00:00:09"
         "\nmacs size: 2"
         "\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"
index c288d075c5db4e1b8dfd2cf237cb0a88eaedcdd1..242527fab5e0ddd25ebaba3e67d387e8b7d367ba 100644 (file)
@@ -144,8 +144,8 @@ void RnaLogger::log(uint16_t type, uint16_t subtype, const Packet* p, const uint
 
 bool RnaLogger::log(uint16_t type, uint16_t subtype, const struct in6_addr* src_ip,
     const uint8_t* src_mac, RnaTracker* ht, const Packet* p, uint32_t event_time,
-    uint16_t proto, const HostMac* hm, const HostApplication* ha, const FpFingerprint* fp,
-    void* cond_var, const HostClient* hc)
+    uint16_t proto, const HostMac* hm, const HostApplication* ha,
+    const FpFingerprint* fp, void* cond_var, const HostClient* hc)
 {
     if ( !enabled )
         return false;
@@ -181,11 +181,11 @@ TEST_CASE("RNA logger", "[rna_logger]")
         uint8_t mac[6] = {0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6};
         RnaLogger logger1(false);
         CHECK(logger1.log(0, 0, nullptr, mac, &ht, nullptr, 0, 0,
-            nullptr, nullptr, nullptr, nullptr) == false);
+            nullptr, nullptr, nullptr, nullptr, nullptr) == false);
 
         RnaLogger logger2(true);
         CHECK(logger2.log(0, 0, nullptr, mac, &ht, nullptr, 0, 0,
-            nullptr, nullptr, nullptr, nullptr) == true);
+            nullptr, nullptr, nullptr, nullptr, nullptr) == true);
     }
 }
 #endif
index 89ab74002c7058580def9ebaa6adb9177195746f..86f98edd49c5892b8f7bf9904fa6c31a5da457f6 100644 (file)
@@ -38,7 +38,8 @@ struct RnaLoggerEvent : public Event
     RnaLoggerEvent (uint16_t t, uint16_t st, const uint8_t* mc, const RnaTracker* rt,
         const snort::HostMac* hmp, uint16_t pr, void* cv, const snort::HostApplication* hap,
         const snort::FpFingerprint* fpr, const snort::HostClient* hcp) : type(t), subtype(st),
-            mac(mc), ht(rt), hm(hmp), proto(pr), cond_var(cv), ha(hap), fp(fpr), hc(hcp) { }
+            mac(mc), ht(rt), hm(hmp), proto(pr), cond_var(cv), ha(hap), fp(fpr), hc(hcp)
+    { }
 
     uint32_t event_time = 0;
     uint16_t type;
@@ -92,10 +93,10 @@ public:
 
     // for all
     bool log(uint16_t type, uint16_t subtype, const struct in6_addr* src_ip,
-        const uint8_t* src_mac, RnaTracker* ht, const snort::Packet* p = nullptr,
-        uint32_t event_time = 0, uint16_t proto = 0, const snort::HostMac* hm = nullptr,
-        const snort::HostApplication* ha = nullptr, const snort::FpFingerprint* fp = nullptr,
-        void* cond_var = nullptr, const snort::HostClient* hc = nullptr);
+        const uint8_t* src_mac, RnaTracker* ht, const snort::Packet* p,
+        uint32_t event_time, uint16_t proto, const snort::HostMac* hm,
+        const snort::HostApplication* ha, const snort::FpFingerprint* fp,
+        void* cond_var, const snort::HostClient* hc);
 
 private:
     const bool enabled;
index 56174674c3894a03fbffd188395953676bff26a6..9f6a9757c0da809d954ca92b9b915f21969fc4ba 100644 (file)
@@ -37,6 +37,7 @@
     #define CHANGE_MAC_INFO            13
     #define CHANGE_MAC_ADD             14
     #define CHANGE_HOST_UPDATE         15
+    #define CHANGE_HOST_TYPE           16
     #define CHANGE_VLAN_TAG            18
 
 #endif
index 422cf7d6dfee19d1174d99695429cb99facc9cff..8bb41ed8c4279cbdca65eb4bdbb521c62aaf1ca4 100644 (file)
@@ -30,7 +30,9 @@
 
 #include "protocols/arp.h"
 #include "protocols/bpdu.h"
+#include "protocols/cdp.h"
 #include "protocols/icmp4.h"
+#include "protocols/icmp6.h"
 #include "protocols/protocol_ids.h"
 
 #include "rna_app_discovery.h"
 
 using namespace snort;
 using namespace snort::bpdu;
+using namespace snort::cdp;
 using namespace std;
 
+#define RNA_NAT_COUNT_THRESHOLD 10
+#define RNA_NAT_TIMEOUT_THRESHOLD 10    // timeout in seconds
+
 void RnaPnd::analyze_appid_changes(DataEvent& event)
 {
     RnaAppDiscovery::process(static_cast<AppidEvent*>(&event), filter, conf, logger);
@@ -75,7 +81,7 @@ void RnaPnd::analyze_flow_tcp(const Packet* p, TcpPacketType type)
         // If it's a tcp SYN packet, create a fingerprint state for
         // the SYN-ACK, but only if we're monitoring the destination (server)
         const auto& dst_ip = p->ptrs.ip_api.get_dst();
-        if ( type == TcpPacketType::SYN && filter.is_host_monitored(p, nullptr, dst_ip) )
+        if ( type == TcpPacketType::SYN and filter.is_host_monitored(p, nullptr, dst_ip) )
         {
             RNAFlow* rna_flow = new RNAFlow();
             p->flow->set_flow_data(rna_flow);
@@ -134,6 +140,7 @@ void RnaPnd::discover_network(const Packet* p, uint8_t ttl)
 
     auto ht = host_cache.find_else_create(*src_ip, &new_host);
 
+    uint32_t last_seen = ht->get_last_seen();
     if ( !new_host )
         ht->update_last_seen(); // this should be done always and foremost
 
@@ -145,22 +152,30 @@ void RnaPnd::discover_network(const Packet* p, uint8_t ttl)
         logger.log(RNA_EVENT_NEW, NEW_HOST, p, &ht, src_ip_ptr, src_mac);
 
     if ( new_mac and !new_host )
-        logger.log(RNA_EVENT_CHANGE, CHANGE_MAC_ADD, p, &ht,
-            src_ip_ptr, src_mac, ht->get_hostmac(src_mac), packet_time());
+    {
+        HostMac hm;
+
+        logger.log(RNA_EVENT_CHANGE, CHANGE_MAC_ADD, p, &ht, src_ip_ptr, src_mac,
+            ht->get_hostmac(src_mac, hm) ? &hm : nullptr, packet_time());
+    }
 
     if ( ht->update_mac_ttl(src_mac, ttl) )
     {
-        logger.log(RNA_EVENT_CHANGE, CHANGE_MAC_INFO, p, &ht,
-            src_ip_ptr, src_mac, ht->get_hostmac(src_mac), packet_time());
+        HostMac hm;
+        logger.log(RNA_EVENT_CHANGE, CHANGE_MAC_INFO, p, &ht, src_ip_ptr, src_mac,
+            ht->get_hostmac(src_mac, hm) ? &hm : nullptr, packet_time());
 
-        HostMac* hm = ht->get_max_ttl_hostmac();
-        if (hm and hm->primary and ht->get_hops())
+        HostMac* hm_max_ttl = ht->get_max_ttl_hostmac();
+        if (hm_max_ttl and hm_max_ttl->primary and ht->get_hops())
         {
             ht->update_hops(0);
             logger.log(RNA_EVENT_CHANGE, CHANGE_HOPS, p, &ht, src_ip_ptr, src_mac, packet_time());
         }
     }
 
+    if ( ht->get_host_type() == HOST_TYPE_HOST and p->is_tcp() )
+        discover_host_types_ttl(ht, p, ttl, last_seen, src_ip_ptr, src_mac);
+
     uint16_t ptype = rna_get_eth(p);
     if ( ptype > to_utype(ProtocolId::ETHERTYPE_MINIMUM) )
     {
@@ -183,49 +198,49 @@ void RnaPnd::discover_network(const Packet* p, uint8_t ttl)
     }
 
     if ( !new_host )
-    {
         generate_change_host_update(&ht, p, src_ip, src_mac, packet_time());
-    }
+
+    discover_host_types_icmpv6_ndp(ht, p, last_seen, src_ip_ptr, src_mac);
 
     // Fingerprint stuff
     const TcpFpProcessor* processor;
-    if ( p->is_tcp() && (processor = get_tcp_fp_processor()) != nullptr )
+    if ( p->is_tcp() and (processor = get_tcp_fp_processor()) != nullptr )
     {
         RNAFlow* rna_flow = nullptr;
         if ( p->ptrs.tcph->is_syn_ack() )
             rna_flow = (RNAFlow*) p->flow->get_flow_data(RNAFlow::inspector_id);
         const TcpFingerprint* tfp = processor->get(p, rna_flow);
 
-        if (tfp && ht->add_tcp_fingerprint(tfp->fpid))
+        if (tfp and ht->add_tcp_fingerprint(tfp->fpid))
             logger.log(RNA_EVENT_NEW, NEW_OS, p, &ht, src_ip_ptr, src_mac, tfp, packet_time());
     }
 }
 
 inline void RnaPnd::update_vlan(const Packet* p, HostTrackerMac& hm)
 {
-    if (!(p->proto_bits & PROTO_BIT__VLAN))
+    if ( !(p->proto_bits & PROTO_BIT__VLAN) )
         return;
 
     const vlan::VlanTagHdr* vh = layer::get_vlan_layer(p);
 
-    if (vh)
+    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, HostTrackerMac& hm, bool isnew)
 {
-    if (!(p->proto_bits & PROTO_BIT__VLAN))
+    if ( !(p->proto_bits & PROTO_BIT__VLAN) )
         return;
 
     const vlan::VlanTagHdr* vh = layer::get_vlan_layer(p);
 
-    if (!vh)
+    if ( !vh )
         return;
 
-    if (isnew or !hm.has_vlan() or hm.get_vlan() != vh->vth_pri_cfi_vlan)
+    if ( isnew or !hm.has_vlan() or (hm.get_vlan() != vh->vth_pri_cfi_vlan) )
     {
-        if (!isnew)
+        if ( !isnew )
             update_vlan(p, hm);
 
         rt->get()->update_vlan(vh->vth_pri_cfi_vlan, vh->vth_proto);
@@ -236,15 +251,14 @@ void RnaPnd::generate_change_vlan_update(RnaTracker *rt, const Packet* p,
 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))
+    if ( !(p->proto_bits & PROTO_BIT__VLAN) )
         return;
 
     const vlan::VlanTagHdr* vh = layer::get_vlan_layer(p);
-
-    if (!vh)
+    if ( !vh )
         return;
 
-    if (isnew or !rt->get()->has_vlan() or rt->get()->get_vlan() != vh->vth_pri_cfi_vlan)
+    if ( isnew or !rt->get()->has_vlan() or (rt->get()->get_vlan() != vh->vth_pri_cfi_vlan) )
     {
         rt->get()->update_vlan(vh->vth_pri_cfi_vlan, vh->vth_proto);
         logger.log(RNA_EVENT_CHANGE, CHANGE_VLAN_TAG, p, rt,
@@ -255,13 +269,13 @@ void RnaPnd::generate_change_vlan_update(RnaTracker *rt, const Packet* p,
 void RnaPnd::generate_change_host_update(RnaTracker* ht, const Packet* p,
     const SfIp* src_ip, const uint8_t* src_mac, const time_t& sec)
 {
-    if ( !ht || !update_timeout)
+    if ( !ht or !update_timeout )
         return;
 
     uint32_t last_seen = (*ht)->get_last_seen();
     uint32_t last_event = (*ht)->get_last_event();
     time_t timestamp = sec - update_timeout;
-    if ( last_seen > last_event && (time_t) last_event + update_timeout <= sec )
+    if ( last_seen > last_event and (time_t) last_event + update_timeout <= sec )
         logger.log(RNA_EVENT_CHANGE, CHANGE_HOST_UPDATE, p, src_mac,
             (const struct in6_addr*) src_ip->get_ip6_ptr(), ht, last_seen, (void*) &timestamp);
     // FIXIT-M: deal with host service hits.
@@ -271,7 +285,7 @@ void RnaPnd::generate_change_host_update(RnaTracker* ht, const Packet* p,
 void RnaPnd::generate_change_host_update_eth(HostTrackerMac* mt, const Packet* p,
     const uint8_t* src_mac, const time_t& sec)
 {
-    if ( !mt || !update_timeout)
+    if ( !mt or !update_timeout)
         return;
 
     // Create and populate a new HostTracker solely for event logging
@@ -288,7 +302,7 @@ void RnaPnd::generate_change_host_update_eth(HostTrackerMac* mt, const Packet* p
     uint32_t last_event = mt->get_last_event();
     time_t timestamp = sec - update_timeout;
 
-    if ( last_seen > last_event && (time_t) last_event + update_timeout <= sec )
+    if ( last_seen > last_event and (time_t) last_event + update_timeout <= sec )
     {
         logger.log(RNA_EVENT_CHANGE, CHANGE_HOST_UPDATE, p, src_mac, nullptr,
             &rt, last_seen, (void*) &timestamp);
@@ -326,7 +340,7 @@ void RnaPnd::generate_new_host_mac(const Packet* p, RnaTracker ht, bool discover
 
     auto hm_ptr = host_cache_mac.find_else_create(mk, &new_host_mac);
 
-    if (new_host_mac)
+    if ( new_host_mac )
     {
         update_vlan(p, *hm_ptr);
 
@@ -344,7 +358,7 @@ void RnaPnd::generate_new_host_mac(const Packet* p, RnaTracker ht, bool discover
         generate_change_vlan_update(&ht, p, mk.mac_addr, *hm_ptr, false);
     }
 
-    if (discover_proto)
+    if ( discover_proto )
     {
         uint16_t ntype = rna_get_eth(p);
         if ( ntype > to_utype(ProtocolId::ETHERTYPE_MINIMUM) )
@@ -377,22 +391,23 @@ void RnaPnd::discover_network_ethernet(const Packet* p)
     #define BPDU_ID 0x42
     #define SNAP_ID 0xAA
     int retval = 1;
-    RnaTracker rt = shared_ptr<snort::HostTracker>(new HostTracker());
 
-    if (!p->is_eth())
+    if ( !p->is_eth() )
         return;
 
-    if (layer::get_arp_layer(p))
+    RnaTracker rt = shared_ptr<snort::HostTracker>(new HostTracker());
+
+    if ( layer::get_arp_layer(p) )
         retval = discover_network_arp(p, &rt);
     else
     {
         // If we have an inner LLC layer, grab it
         const Layer& lyr = p->layers[p->num_layers-1];
-        if (lyr.prot_id == ProtocolId::ETHERNET_LLC)
+        if ( lyr.prot_id == ProtocolId::ETHERNET_LLC )
         {
             uint16_t etherType = rna_get_eth(p);
 
-            if (!etherType || etherType > static_cast<uint16_t>(ProtocolId::ETHERTYPE_MINIMUM))
+            if ( !etherType or etherType > static_cast<uint16_t>(ProtocolId::ETHERTYPE_MINIMUM) )
             {
                 generate_new_host_mac(p, rt);
                 return;
@@ -400,27 +415,30 @@ void RnaPnd::discover_network_ethernet(const Packet* p)
 
             const RNA_LLC* llc = (const RNA_LLC*) lyr.start;
 
-            if (llc->s.s.DSAP != llc->s.s.SSAP)
+            if ( llc->s.s.DSAP != llc->s.s.SSAP )
             {
                 generate_new_host_mac(p, rt);
                 return;
             }
 
-            switch (llc->s.s.DSAP)
+            switch ( llc->s.s.DSAP )
             {
-                case BPDU_ID:
-                {
-                    retval = discover_network_bpdu(p, ((const uint8_t*)llc + sizeof(RNA_LLC)), rt);
-                    break;
-                }
+            case BPDU_ID:
+                retval = discover_network_bpdu(p, ((const uint8_t*)llc + sizeof(RNA_LLC)), rt);
+                break;
 
-                default:
-                    break;
+            case SNAP_ID:
+                retval = discover_host_types_cdp(p, (const uint8_t*)llc + sizeof(RNA_LLC),
+                    p->dsize - sizeof(RNA_LLC));
+                break;
+
+            default:
+                break;
             }
         }
     }
 
-    if (retval)
+    if ( retval )
         generate_new_host_mac(p, rt, true);
 
     return;
@@ -433,23 +451,23 @@ int RnaPnd::discover_network_arp(const Packet* p, RnaTracker* ht_ref)
 
     const snort::arp::EtherARP *ah = layer::get_arp_layer(p);
 
-    if (ntohs(ah->ea_hdr.ar_hrd) != 0x0001)
+    if ( ntohs(ah->ea_hdr.ar_hrd) != 0x0001 )
         return 1;
-    if (ntohs(ah->ea_hdr.ar_pro) != 0x0800)
+    if ( ntohs(ah->ea_hdr.ar_pro) != 0x0800 )
         return 1;
-    if (ah->ea_hdr.ar_hln != 6 || ah->ea_hdr.ar_pln != 4)
+    if ( ah->ea_hdr.ar_hln != 6 or ah->ea_hdr.ar_pln != 4 )
         return 1;
-    if ((ntohs(ah->ea_hdr.ar_op) != 0x0002))
+    if ( (ntohs(ah->ea_hdr.ar_op) != 0x0002) )
         return 1;
-    if (memcmp(src_mac, ah->arp_sha, MAC_SIZE))
+    if ( memcmp(src_mac, ah->arp_sha, MAC_SIZE) )
         return 1;
-    if (!ah->arp_spa32)
+    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) ))
+    if ( !filter.is_host_monitored(p, nullptr, &spa) )
         return 1;
 
     bool new_host = false;
@@ -457,7 +475,7 @@ int RnaPnd::discover_network_arp(const Packet* p, RnaTracker* ht_ref)
     auto ht = host_cache.find_else_create(spa, &new_host);
     auto hm_ptr = host_cache_mac.find_else_create(mk, &new_host_mac);
 
-    if (!new_host_mac)
+    if ( !new_host_mac )
         hm_ptr->update_last_seen(p->pkth->ts.tv_sec);
 
     *ht_ref = ht;
@@ -473,14 +491,21 @@ int RnaPnd::discover_network_arp(const Packet* p, RnaTracker* ht_ref)
 
     if ( ht->add_mac(src_mac, 0, 0) )
     {
+        HostMac hm;
+        HostMac* phm = nullptr;
+        if ( ht->get_hostmac(src_mac, hm) )
+            phm = &hm;
+
         logger.log(RNA_EVENT_CHANGE, CHANGE_MAC_ADD, p, ht_ref,
-            (const struct in6_addr*) spa.get_ip6_ptr(), src_mac, ht->get_hostmac(src_mac));
+            (const struct in6_addr*) spa.get_ip6_ptr(), src_mac, phm);
         hm_ptr->update_last_event(p->pkth->ts.tv_sec);
     }
-    else if (ht->make_primary(src_mac))
+    else if ( ht->make_primary(src_mac) )
     {
+        HostMac hm;
         logger.log(RNA_EVENT_CHANGE, CHANGE_MAC_INFO, p, ht_ref,
-            (const struct in6_addr*) spa.get_ip6_ptr(), src_mac, ht->get_hostmac(src_mac));
+            (const struct in6_addr*) spa.get_ip6_ptr(), src_mac,
+            ht->get_hostmac(src_mac, hm) ? &hm : nullptr);
         hm_ptr->update_last_event(p->pkth->ts.tv_sec);
     }
 
@@ -495,9 +520,11 @@ int RnaPnd::discover_network_arp(const Packet* p, RnaTracker* ht_ref)
 
     if ( ht->get_hops() )
     {
+        HostMac hm;
+
         ht->update_hops(0);
         logger.log(RNA_EVENT_CHANGE, CHANGE_HOPS, p, ht_ref,
-            (const struct in6_addr*) spa.get_ip6_ptr(), src_mac, ht->get_hostmac(src_mac));
+            (const struct in6_addr*) spa.get_ip6_ptr(), src_mac, ht->get_hostmac(src_mac, hm) ? &hm : nullptr);
         hm_ptr->update_last_event(p->pkth->ts.tv_sec);
     }
 
@@ -507,19 +534,18 @@ int RnaPnd::discover_network_arp(const Packet* p, RnaTracker* ht_ref)
     return 0;
 }
 
-int RnaPnd::discover_network_bpdu(const Packet* p, const uint8_t* data,
-    RnaTracker ht_ref)
+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))
+    if ( !isBPDU(dst_mac) )
         return 1;
     stp = reinterpret_cast<const BPDUData*>(data);
-    if (stp->id || stp->version)
+    if ( stp->id or stp->version )
         return 1;
-    if (stp->type !=  BPDU_TYPE_TOPCHANGE)
+    if ( stp->type !=  BPDU_TYPE_TOPCHANGE )
         return 1;
 
     return discover_switch(p, ht_ref);
@@ -532,7 +558,7 @@ int RnaPnd::discover_switch(const Packet* p, RnaTracker ht_ref)
 
     auto hm_ptr = host_cache_mac.find_else_create(mk, &new_host_mac);
 
-    if (new_host_mac)
+    if ( new_host_mac )
     {
         hm_ptr->host_type = HOST_TYPE_BRIDGE;
         update_vlan(p, *hm_ptr);
@@ -557,6 +583,240 @@ int RnaPnd::discover_switch(const Packet* p, RnaTracker ht_ref)
     return 0;
 }
 
+void RnaPnd::discover_host_types_ttl(RnaTracker& ht, const Packet *p, uint8_t pkt_ttl,
+    uint32_t last_seen, const struct in6_addr* src_ip, const uint8_t* src_mac)
+{
+    uint8_t ht_ttl = ht->get_ip_ttl();
+    if ( pkt_ttl and ht_ttl and (pkt_ttl != ht_ttl) )
+    {
+        if ( (abs(ht_ttl - pkt_ttl) > MIN_TTL_DIFF) )
+        {
+            uint32_t ht_last_seen = ht->get_last_seen();
+            if ( ht_last_seen < (last_seen + MIN_BOOT_TIME) )
+            {
+                uint32_t nc = ht->inc_nat_count();
+                if ( nc >= RNA_NAT_COUNT_THRESHOLD )
+                {
+                    ht->set_nat_count(0);
+                    if ( ht_last_seen - ht->get_nat_count_start() <= RNA_NAT_TIMEOUT_THRESHOLD )
+                    {
+                        ht->set_host_type(p->is_from_application_client() ? HOST_TYPE_NAT : HOST_TYPE_LB);
+                        logger.log(RNA_EVENT_CHANGE, CHANGE_HOST_TYPE, p, &ht, src_ip, src_mac);
+                    }
+
+                    ht->set_nat_count_start(ht_last_seen);
+                }
+            }
+        }
+    }
+
+    ht->set_ip_ttl(pkt_ttl);
+}
+
+int RnaPnd::discover_host_types_cdp(const Packet* p, const uint8_t* data, uint16_t rlen)
+{
+    if ( !is_cdp(layer::get_eth_layer(p)->ether_dst) or rlen < sizeof(RNA_CDP) )
+        return 1;
+
+    if ( ntohs(((const RNA_CDP *)data)->pid) != CDP_HDLC_PROTOCOL_TYPE )
+        return 1;
+
+    data += sizeof(RNA_CDP);
+    const uint8_t* end = data + rlen - sizeof(RNA_CDP);
+    std::vector<uint32_t> ip_address;
+    uint32_t cap = 0;
+    while ( data < end )
+    {
+        uint16_t len;
+        uint16_t type;
+        const RNA_CDP_DATA* tlv;
+
+        tlv = (const RNA_CDP_DATA *)data;
+        len = ntohs(tlv->length);
+        if ( len < sizeof(RNA_CDP_DATA) or data + len > end )
+            return 1;
+
+        type = ntohs(tlv->type);
+        if ( type == RNA_CDP_ADDRESS_TYPE )
+        {
+            uint16_t addr_len = len - sizeof(RNA_CDP_DATA);
+            uint32_t num_addrs;
+
+            data += sizeof(RNA_CDP_DATA);
+            num_addrs = ntohl(*((const uint32_t *)data));
+            data += sizeof(uint32_t);
+            addr_len -= sizeof(uint32_t);
+            for (unsigned i = 0; i < num_addrs; i++)
+            {
+                uint16_t tmp_len;
+                bool ip;
+
+                if (addr_len < 5)
+                    return 1;
+
+                ip = ( *data == 0x01 ) ? true : false;
+                data++;
+                addr_len--;
+                ip = ( ip and (*data == 0x01) ) ? true : false;
+                tmp_len = *data;
+                data++;
+                addr_len--;
+                ip = ( ip and (*data == 0xcc) ) ? true : false;
+                data += tmp_len;
+                addr_len -= tmp_len;
+
+                if ( addr_len < 2 )
+                    return 1;
+
+                tmp_len = ntohs(*((const uint16_t *)data));
+                data += sizeof(uint16_t);
+                addr_len -= sizeof(uint16_t);
+
+                if ( addr_len < tmp_len )
+                    return 1;
+
+                if (ip and tmp_len == 0x0004)
+                    ip_address.push_back(*((const uint32_t *)data));
+
+                data += tmp_len;
+                addr_len -= tmp_len;
+            }
+
+            if ( addr_len )
+                return 1;
+        }
+        else if ( type == RNA_CDP_CAPABILITIES_TYPE )
+        {
+            data += sizeof(RNA_CDP_DATA);
+            if ( len != 8 )
+                return 1;
+            cap = ntohl(*((const uint32_t *)data));
+            data += sizeof(uint32_t);
+        }
+        else
+            data += len;
+    }
+
+    if ( !(cap & RNA_CDP_CAPABILITIES_MASK) )
+        return 0;
+
+    for ( uint32_t a : ip_address )
+    {
+        SfIp cdp_ip = {(void*)&a, AF_INET};
+        auto ht = host_cache.find(cdp_ip);
+
+        if ( ht and (ht->get_host_type() == HOST_TYPE_HOST) )
+        {
+            if ( cap & RNA_CDP_CAPABILITIES_ROUTER )
+                ht->set_host_type(HOST_TYPE_ROUTER);
+            else
+                ht->set_host_type(HOST_TYPE_BRIDGE);
+
+            logger.log(RNA_EVENT_CHANGE, CHANGE_HOST_TYPE, p, &ht,
+                (const struct in6_addr*)cdp_ip.get_ip6_ptr(), zero_mac);
+        }
+    }
+
+    return 0;
+}
+
+#define ICMPv6_NS_MIN_LEN 24
+#define ICMPv6_NA_MIN_LEN 24
+#define ICMPv6_RS_MIN_LEN 24
+#define ICMPv6_RA_MIN_LEN 16
+
+#define ICMPV6_OPION_SOURCE_LINKLAYER_ADDRESS 1
+#define ICMPV6_OPION_TARGET_LINKLAYER_ADDRESS 2
+#define ICMPV6_OPION_PREFIX_INFO              3
+#define ICMPV6_OPION_REDIRECT_HEADER          4
+#define ICMPV6_OPION_MTU                      5
+
+int RnaPnd::discover_host_types_icmpv6_ndp(RnaTracker& ht, const Packet* p, uint32_t last_seen,
+    const struct in6_addr* src_ip, const uint8_t* src_mac)
+{
+    const uint8_t* neighbor_src_mac = nullptr;
+    bool is_router = false;
+
+    if ( !p->is_icmp() or !p->is_ip6() )
+        return 1;
+
+    const uint8_t* data = (const uint8_t*)p->ptrs.icmph;
+    int32_t data_len = p->ptrs.ip_api.pay_len();
+
+    switch ( ((const icmp::Icmp6Hdr*)p->ptrs.icmph)->type )
+    {
+        case snort::icmp::NEIGHBOR_ADVERTISEMENT:
+            if ( (p->ptrs.icmph->code) or (data_len <= ICMPv6_NA_MIN_LEN) )
+                return 1;
+
+            data += ICMPv6_NA_MIN_LEN;
+            data_len -= ICMPv6_NA_MIN_LEN;
+
+            while ( data_len >= 2 )
+            {
+                uint8_t opt_type, opt_len;
+
+                opt_type = *data;
+                opt_len = *(data + 1);
+                if ( opt_type == ICMPV6_OPION_TARGET_LINKLAYER_ADDRESS )
+                    neighbor_src_mac = data + 2;
+
+                data += opt_len * 8;
+                data_len -= opt_len * 8;
+            }
+            break;
+
+        case snort::icmp::ROUTER_ADVERTISEMENT:
+            if ( p->ptrs.icmph->code or (data_len <= ICMPv6_RA_MIN_LEN) )
+                return 1;
+
+            is_router = true;
+            data += ICMPv6_RA_MIN_LEN;
+            data_len -= ICMPv6_RA_MIN_LEN;
+
+            while ( data_len >= 2 )
+            {
+                uint8_t opt_type, opt_len;
+
+                opt_type = *data;
+                opt_len = *(data + 1);
+                if ( opt_type == ICMPV6_OPION_SOURCE_LINKLAYER_ADDRESS )
+                    neighbor_src_mac = data + 2;
+
+                data += opt_len * 8;
+                data_len -= opt_len * 8;
+            }
+            break;
+
+        case snort::icmp::ROUTER_SOLICITATION:
+        case snort::icmp::NEIGHBOR_SOLICITATION:
+        default:
+            return 1;
+    }
+
+    if ( data_len or !neighbor_src_mac )
+        return 1;
+
+    // discarding packets through arp proxy.
+    if ( memcmp(src_mac, neighbor_src_mac, MAC_SIZE) )
+        return 1;
+
+    if ( is_router and ((ht->get_host_type() != HOST_TYPE_ROUTER) and (ht->get_host_type() != HOST_TYPE_BRIDGE)) )
+    {
+        ht->set_host_type(HOST_TYPE_ROUTER);
+        logger.log(RNA_EVENT_CHANGE, CHANGE_HOST_TYPE, p, &ht, src_ip, neighbor_src_mac);
+    }
+
+    if ( ht->make_primary(src_mac) )
+    {
+        HostMac hm;
+        logger.log(RNA_EVENT_CHANGE, CHANGE_MAC_INFO, p, &ht,
+            src_ip, src_mac, ht->get_hostmac(src_mac, hm) ? &hm : nullptr, last_seen);
+    }
+
+    return 1;
+}
+
 #ifdef UNIT_TEST
 TEST_CASE("RNA pnd", "[non-ip]")
 {
index 101af99ce28aabb18912a458bea8a1aa65e9b2df..591ddd44067641a69eb8be0e8d11cca328c8f637 100644 (file)
@@ -120,45 +120,52 @@ public:
         logger(RnaLogger(en)), filter(DiscoveryFilter(cp)), conf(rc)
         { update_timeout = (rc ? rc->update_timeout : 0); }
 
-    void analyze_appid_changes(snort::DataEvent& event);
-    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, TcpPacketType type);
-    void analyze_flow_udp(const snort::Packet* p);
+    void analyze_appid_changes(snort::DataEvent&);
+    void analyze_flow_icmp(const snort::Packet*);
+    void analyze_flow_ip(const snort::Packet*);
+    void analyze_flow_non_ip(const snort::Packet*);
+    void analyze_flow_tcp(const snort::Packet*, TcpPacketType);
+    void analyze_flow_udp(const snort::Packet*);
 
     // generate change event for all hosts in the ip cache
     void generate_change_host_update();
 
 private:
     // generate change event for single host
-    void generate_change_host_update(RnaTracker* ht, const snort::Packet* p,
-        const snort::SfIp* src_ip, const uint8_t* src_mac, const time_t& sec);
-    void generate_change_host_update_eth(HostTrackerMac* mt, const snort::Packet* p,
-        const uint8_t* src_mac, const time_t& sec);
+    void generate_change_host_update(RnaTracker*, const snort::Packet*,
+        const snort::SfIp*, const uint8_t* src_mac, const time_t&);
+    void generate_change_host_update_eth(HostTrackerMac*, const snort::Packet*,
+        const uint8_t* src_mac, const time_t&);
+
+    void discover_host_types_ttl(RnaTracker&, const snort::Packet*, uint8_t pkt_ttl,
+        uint32_t last_seen, const struct in6_addr*, const uint8_t* src_mac);
+    int discover_host_types_icmpv6_ndp(RnaTracker& ht, const snort::Packet*, uint32_t last_seen,
+        const struct in6_addr* src_ip, const uint8_t* src_mac);
 
     // Change vlan event related utilities
-    inline void update_vlan(const snort::Packet* p, HostTrackerMac& hm);
-    void generate_change_vlan_update(RnaTracker *rt, const snort::Packet* p,
-        const uint8_t* src_mac, HostTrackerMac& hm, bool isnew);
-    void generate_change_vlan_update(RnaTracker *rt, const snort::Packet* p,
-        const uint8_t* src_mac, const snort::SfIp* src_ip, bool isnew);
+    inline void update_vlan(const snort::Packet*, HostTrackerMac&);
+    void generate_change_vlan_update(RnaTracker*, const snort::Packet*,
+        const uint8_t* src_mac, HostTrackerMac&, bool isnew);
+    void generate_change_vlan_update(RnaTracker*, const snort::Packet*,
+        const uint8_t* src_mac, const snort::SfIp*, bool isnew);
 
-    void generate_new_host_mac(const snort::Packet* p, RnaTracker ht, bool discover_proto = false);
+    void generate_new_host_mac(const snort::Packet*, RnaTracker, bool discover_proto = false);
 
     // General rna utilities not associated with flow
-    void discover_network_icmp(const snort::Packet* p);
-    void discover_network_ip(const snort::Packet* p);
-    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, uint8_t ttl);
+    void discover_network_icmp(const snort::Packet*);
+    void discover_network_ip(const snort::Packet*);
+    void discover_network_non_ip(const snort::Packet*);
+    void discover_network_tcp(const snort::Packet*);
+    void discover_network_udp(const snort::Packet*);
+    void discover_network(const snort::Packet*, 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);
+    void discover_network_ethernet(const snort::Packet*);
+    int discover_network_arp(const snort::Packet*, RnaTracker*);
+    int discover_network_bpdu(const snort::Packet*, const uint8_t* data, RnaTracker);
+    int discover_host_types_cdp(const snort::Packet*, const uint8_t* data, uint16_t rlen);
+
+    int discover_switch(const snort::Packet*, RnaTracker);
 
     RnaLogger logger;
     DiscoveryFilter filter;
index 82e0d01a33802dc8e8f197a3e692a4a46f5b59f8..0fdf263af7e01b397f75d456802c2a7043faa3f7 100644 (file)
@@ -2,6 +2,7 @@
 set (PROTOCOL_HEADERS
     arp.h
     bpdu.h
+    cdp.h
     cisco_meta_data.h
     eapol.h
     eth.h
diff --git a/src/protocols/cdp.h b/src/protocols/cdp.h
new file mode 100644 (file)
index 0000000..b85247e
--- /dev/null
@@ -0,0 +1,67 @@
+//--------------------------------------------------------------------------
+// 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.
+//--------------------------------------------------------------------------
+// cdp.h author davis mcpherson <davmcphe@cisco.com>
+
+// Represents the CDP (Cisco Discovery Protocol) frame format
+
+#ifndef PROTOCOLS_CDP_H
+#define PROTOCOLS_CDP_H
+
+#include <cstdint>
+#include <cstring>
+
+namespace snort
+{
+namespace cdp
+{
+#define CDP_HDLC_PROTOCOL_TYPE 0x2000
+
+// CDP data type values
+#define RNA_CDP_ADDRESS_TYPE 0x0002
+#define RNA_CDP_CAPABILITIES_TYPE 0x0004
+
+#define RNA_CDP_CAPABILITIES_ROUTER 0x0001
+#define RNA_CDP_CAPABILITIES_SWITCH 0x000A
+#define RNA_CDP_CAPABILITIES_MASK (RNA_CDP_CAPABILITIES_ROUTER | RNA_CDP_CAPABILITIES_SWITCH)
+
+static const uint8_t CDP_DEST[6] = {0x01, 0x00, 0x0C, 0xCC, 0xCC, 0xCC};
+
+bool is_cdp(const uint8_t mac[6]);
+
+struct RNA_CDP
+{
+    uint8_t org_code[3];
+    uint16_t pid;
+    uint8_t version;
+    uint8_t ttl;
+    uint16_t checksum;
+}  __attribute__((__packed__));
+
+struct RNA_CDP_DATA
+{
+    uint16_t type;
+    uint16_t length;
+}  __attribute__((__packed__));
+
+bool is_cdp(const uint8_t mac[6])
+{ return (memcmp(mac, CDP_DEST, sizeof(CDP_DEST)) == 0); }
+
+} // namespace cdp
+} // namespace snort
+
+#endif