From: Masud Hasan (mashasan) Date: Fri, 2 Oct 2020 17:18:59 +0000 (+0000) Subject: Merge pull request #2513 in SNORT/snort3 from ~DAVMCPHE/snort3:rna_host_type_discover... X-Git-Tag: 3.0.3-2~16 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5c95c4b622a902e381760bf99722322c211695b3;p=thirdparty%2Fsnort3.git Merge pull request #2513 in SNORT/snort3 from ~DAVMCPHE/snort3:rna_host_type_discovery to master Squashed commit of the following: commit 52c06b3d7bc98f14eddab2d70efa5fe8df3a486a Author: davis mcpherson Date: Wed Sep 16 15:50:43 2020 -0400 rna: port host type discovery logic --- diff --git a/src/host_tracker/host_tracker.cc b/src/host_tracker/host_tracker.cc index 1c818a555..98fc6a659 100644 --- a/src/host_tracker/host_tracker.cc +++ b/src/host_tracker/host_tracker.cc @@ -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 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 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 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() ) { diff --git a/src/host_tracker/host_tracker.h b/src/host_tracker/host_tracker.h index 4302e9d68..1904e6cfa 100644 --- a/src/host_tracker/host_tracker.h +++ b/src/host_tracker/host_tracker.h @@ -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 HostMacAllocator; typedef HostCacheAllocIp HostAppAllocator; typedef HostCacheAllocIp 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 lck(host_tracker_lock); + return host_type; + } + uint8_t get_hops() { std::lock_guard 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 lck(host_tracker_lock); + return ip_ttl; + } + + void set_ip_ttl(uint8_t ttl) + { + std::lock_guard lck(host_tracker_lock); + ip_ttl = ttl; + } + + uint32_t get_nat_count_start() const + { + std::lock_guard lck(host_tracker_lock); + return nat_count_start; + } + + void set_nat_count_start(uint32_t natCountStart) + { + std::lock_guard lck(host_tracker_lock); + nat_count_start = natCountStart; + } + + uint32_t get_nat_count() const + { + std::lock_guard lck(host_tracker_lock); + return nat_count; + } + + void set_nat_count(uint32_t v = 0) + { + std::lock_guard lck(host_tracker_lock); + nat_count = v; + } + + uint32_t inc_nat_count() + { + std::lock_guard 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 diff --git a/src/host_tracker/test/host_tracker_test.cc b/src/host_tracker/test/host_tracker_test.cc index 50116f87e..1e7dd10a5 100644 --- a/src/host_tracker/test/host_tracker_test.cc +++ b/src/host_tracker/test/host_tracker_test.cc @@ -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" diff --git a/src/network_inspectors/rna/rna_logger.cc b/src/network_inspectors/rna/rna_logger.cc index c288d075c..242527fab 100644 --- a/src/network_inspectors/rna/rna_logger.cc +++ b/src/network_inspectors/rna/rna_logger.cc @@ -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 diff --git a/src/network_inspectors/rna/rna_logger.h b/src/network_inspectors/rna/rna_logger.h index 89ab74002..86f98edd4 100644 --- a/src/network_inspectors/rna/rna_logger.h +++ b/src/network_inspectors/rna/rna_logger.h @@ -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; diff --git a/src/network_inspectors/rna/rna_logger_common.h b/src/network_inspectors/rna/rna_logger_common.h index 56174674c..9f6a9757c 100644 --- a/src/network_inspectors/rna/rna_logger_common.h +++ b/src/network_inspectors/rna/rna_logger_common.h @@ -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 diff --git a/src/network_inspectors/rna/rna_pnd.cc b/src/network_inspectors/rna/rna_pnd.cc index 422cf7d6d..8bb41ed8c 100644 --- a/src/network_inspectors/rna/rna_pnd.cc +++ b/src/network_inspectors/rna/rna_pnd.cc @@ -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" @@ -43,8 +45,12 @@ 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(&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*) ×tamp); // 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*) ×tamp); @@ -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(new HostTracker()); - if (!p->is_eth()) + if ( !p->is_eth() ) return; - if (layer::get_arp_layer(p)) + RnaTracker rt = shared_ptr(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(ProtocolId::ETHERTYPE_MINIMUM)) + if ( !etherType or etherType > static_cast(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(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 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]") { diff --git a/src/network_inspectors/rna/rna_pnd.h b/src/network_inspectors/rna/rna_pnd.h index 101af99ce..591ddd440 100644 --- a/src/network_inspectors/rna/rna_pnd.h +++ b/src/network_inspectors/rna/rna_pnd.h @@ -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; diff --git a/src/protocols/CMakeLists.txt b/src/protocols/CMakeLists.txt index 82e0d01a3..0fdf263af 100644 --- a/src/protocols/CMakeLists.txt +++ b/src/protocols/CMakeLists.txt @@ -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 index 000000000..b85247e01 --- /dev/null +++ b/src/protocols/cdp.h @@ -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 + +// Represents the CDP (Cisco Discovery Protocol) frame format + +#ifndef PROTOCOLS_CDP_H +#define PROTOCOLS_CDP_H + +#include +#include + +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