return result.second;
}
+bool HostTracker::add_udp_fingerprint(uint32_t fpid)
+{
+ lock_guard<mutex> lck(host_tracker_lock);
+ auto result = udp_fpids.emplace(fpid);
+ return result.second;
+}
+
bool HostTracker::set_visibility(bool v)
{
std::lock_guard<std::mutex> lck(host_tracker_lock);
str += string(")") + (--total ? ", " : "");
}
}
+
+ total = udp_fpids.size();
+ if ( total )
+ {
+ str += "\nudp fingerprint: ";
+ for ( const auto& fpid : udp_fpids )
+ str += to_string(fpid) + (--total ? ", " : "");
+ }
}
bool add_tcp_fingerprint(uint32_t fpid);
bool add_ua_fingerprint(uint32_t fpid, uint32_t fp_type, bool jail_broken,
const char* device_info, uint8_t max_devices);
+ bool add_udp_fingerprint(uint32_t fpid);
// This should be updated whenever HostTracker data members are changed
void stringify(std::string& str);
std::vector<HostApplication, HostAppAllocator> services;
std::vector<HostClient, HostClientAllocator> clients;
std::set<uint32_t, std::less<uint32_t>, HostCacheAllocIp<uint32_t>> tcp_fpids;
+ std::set<uint32_t, std::less<uint32_t>, HostCacheAllocIp<uint32_t>> udp_fpids;
std::vector<DeviceFingerprint, HostDeviceFpAllocator> ua_fps;
bool vlan_tag_present = false;
#define MAX_SFTP_PACKET_COUNT 55
#define APPID_SESSION_DATA_NONE 0
-#define APPID_SESSION_DATA_DHCP_FP_DATA 2
#define APPID_SESSION_DATA_SMB_DATA 4
-#define APPID_SESSION_DATA_DHCP_INFO 5
#define APPID_SESSION_DATA_SERVICE_MODSTATE_BIT 0x20000000
#define APPID_SESSION_DATA_CLIENT_MODSTATE_BIT 0x40000000
#define APPID_SESSION_DATA_DETECTOR_MODSTATE_BIT 0x80000000
#include "service_bootp.h"
+#include "detection/detection_engine.h"
+#include "protocols/eth.h"
+#include "protocols/packet.h"
+#include "pub_sub/dhcp_events.h"
#include "app_info_table.h"
#include "appid_config.h"
#include "appid_inspector.h"
#include "appid_utils/ip_funcs.h"
-#include "protocols/eth.h"
-#include "protocols/packet.h"
using namespace snort;
if (option53 && op55_len && (memcmp(eh->ether_src, bh->chaddr, 6) == 0))
{
- if (add_dhcp_info(args.asd, op55_len, op55, op60_len, op60, bh->chaddr))
- return APPID_ENOMEM;
+ add_dhcp_info(args.asd, op55_len, op55, op60_len, op60, bh->chaddr);
}
goto inprocess;
}
return APPID_NOT_COMPATIBLE;
}
-void BootpServiceDetector::AppIdFreeDhcpData(DHCPData* dd)
-{
- snort_free(dd);
-}
-
-void BootpServiceDetector::AppIdFreeDhcpInfo(DHCPInfo* dd)
-{
- snort_free(dd);
-}
-
-int BootpServiceDetector::add_dhcp_info(AppIdSession& asd, unsigned op55_len, const uint8_t* op55,
+void BootpServiceDetector::add_dhcp_info(AppIdSession& asd, unsigned op55_len, const uint8_t* op55,
unsigned op60_len, const uint8_t* op60, const uint8_t* mac)
{
if (op55_len && op55_len <= DHCP_OPTION55_LEN_MAX
&& !asd.get_session_flags(APPID_SESSION_HAS_DHCP_FP))
{
- DHCPData* rdd = (DHCPData*)snort_calloc(sizeof(*rdd));
- if (asd.add_flow_data(rdd, APPID_SESSION_DATA_DHCP_FP_DATA,
- (AppIdFreeFCN)BootpServiceDetector::AppIdFreeDhcpData))
- {
- BootpServiceDetector::AppIdFreeDhcpData(rdd);
- return -1;
- }
-
asd.set_session_flags(APPID_SESSION_HAS_DHCP_FP);
- rdd->op55_len = (op55_len > DHCP_OP55_MAX_SIZE) ? DHCP_OP55_MAX_SIZE : op55_len;
- memcpy(rdd->op55, op55, rdd->op55_len);
- rdd->op60_len = (op60_len > DHCP_OP60_MAX_SIZE) ? DHCP_OP60_MAX_SIZE : op60_len;
- if (op60_len)
- memcpy(rdd->op60, op60, rdd->op60_len);
- memcpy(rdd->eth_addr, mac, sizeof(rdd->eth_addr));
+ unsigned op55_length = (op55_len > DHCP_OP55_MAX_SIZE) ? DHCP_OP55_MAX_SIZE : op55_len;
+ unsigned op60_length = (op60_len > DHCP_OP60_MAX_SIZE) ? DHCP_OP60_MAX_SIZE : op60_len;
+ Packet* p = DetectionEngine::get_current_packet();
+ DHCPDataEvent event(p, op55_length, op60_length, op55, op60, mac);
+ DataBus::publish(DHCP_DATA_EVENT, event, p->flow);
}
- return 0;
}
static unsigned isIPv4HostMonitored(uint32_t, int32_t)
int32_t zone,
uint32_t subnetmask, uint32_t leaseSecs, uint32_t router)
{
- DHCPInfo* info;
-
if (memcmp(mac, zeromac, 6) == 0 || ip == 0)
return;
if (!(flags & IPFUNCS_HOSTS_IP))
return;
- info = (DHCPInfo*)snort_calloc(sizeof(DHCPInfo));
-
- if (asd.add_flow_data(info, APPID_SESSION_DATA_DHCP_INFO,
- (AppIdFreeFCN)BootpServiceDetector::AppIdFreeDhcpInfo))
- {
- BootpServiceDetector::AppIdFreeDhcpInfo(info);
- return;
- }
asd.set_session_flags(APPID_SESSION_HAS_DHCP_INFO);
- info->ipAddr = ip;
- memcpy(info->eth_addr, mac, sizeof(info->eth_addr));
- info->subnetmask = subnetmask;
- info->leaseSecs = leaseSecs;
- info->router = router;
+ Packet* p = DetectionEngine::get_current_packet();
+ DHCPInfoEvent event(p, ip, mac, subnetmask, leaseSecs, router);
+ DataBus::publish(DHCP_INFO_EVENT, event, p->flow);
}
#include "service_detector.h"
-#define DHCP_OP55_MAX_SIZE 64
-#define DHCP_OP60_MAX_SIZE 64
-
class AppIdSession;
class ServiceDiscovery;
-struct DHCPData
-{
- DHCPData* next;
- unsigned op55_len;
- unsigned op60_len;
- uint8_t op55[DHCP_OP55_MAX_SIZE];
- uint8_t op60[DHCP_OP60_MAX_SIZE];
- uint8_t eth_addr[6];
-};
-
-struct DHCPInfo
-{
- DHCPInfo* next;
- uint32_t ipAddr;
- uint8_t eth_addr[6];
- uint32_t subnetmask;
- uint32_t leaseSecs;
- uint32_t router;
-};
-
class BootpServiceDetector : public ServiceDetector
{
public:
int validate(AppIdDiscoveryArgs&) override;
- // FIXIT-L - move to service discovery class
- static void AppIdFreeDhcpData(DHCPData*);
- static void AppIdFreeDhcpInfo(DHCPInfo*);
-
private:
- int add_dhcp_info(AppIdSession&, unsigned op55_len, const uint8_t* op55, unsigned
+ void add_dhcp_info(AppIdSession&, unsigned op55_len, const uint8_t* op55, unsigned
op60_len, const uint8_t* op60, const uint8_t* mac);
void add_new_dhcp_lease(AppIdSession&, const uint8_t* mac, uint32_t ip, int32_t zone,
uint32_t subnetmask, uint32_t leaseSecs, uint32_t router);
rna_fingerprint.h
rna_fingerprint_tcp.h
rna_fingerprint_ua.h
+ rna_fingerprint_udp.h
rna_inspector.h
rna_logger.h
rna_name.h
rna_fingerprint.h
rna_fingerprint_tcp.cc
rna_fingerprint_ua.cc
+ rna_fingerprint_udp.cc
rna_inspector.cc
rna_inspector.h
rna_logger.cc
Fingerprints
Fingerprints are a sequence of features in network traffic used to identify a host's operating system.
-Currently only tcp fingerprints are supported, but future work may include udp fingerprints and
-user agent fingerprints.
Tcp fingerprints are specified in lua and read by snort at configure time.
Only the SYN and SYN-ACK packets are used for fingerprint matching. We refer to the SYN packet
user_agent = { { substring = "CPU" }, { substring = "OS 3_0" }, { substring = "My Company" } },
device = "My Mobile",
}
+
+UDP fingerprints are of two types, namely DHCP and SMB. Currently only DHCP fingerprints are
+supported. Similar to TCP and user-agent based fingerprints, fingerprint patterns can be specified
+in the lua configuration. DHCP fingerprint matching is based on option fields present in DHCP
+Request packet and the updated lease information is obtained from the DHCP ACK packet. Appid
+module processes the DHCP request packet, parses the option 55 and option 60 fields and publishes
+the DHCPDataEvent. This triggers RNA fingerprint matching and event generation. While processing
+DHCP ACK packet, appid extracts the leased IP, netmask and lease time and publishes the DHCPInfoEvent.
+RNA generates the CHANGE_FULL_DHCP_INFO event with updated lease information.
+A sample DHCP fingerprint is shown below:
+{
+ fpid = 111,
+ uuid = "12345678-1234-1234-1234-123456789111",
+ type = 4,
+ dhcp55 = "1 121 3 6 15 119 252",
+ dhcp60 = "dhcp 5.1.4",
+}
{
class TcpFpProcessor;
class UaFpProcessor;
+class UdpFpProcessor;
}
struct RnaModuleConfig
bool log_when_idle;
snort::TcpFpProcessor* tcp_processor = nullptr;
snort::UaFpProcessor* ua_processor = nullptr;
+ snort::UdpFpProcessor* udp_processor = nullptr;
};
// Give default values so that RNA can work even if rna_conf_path is not provided
#endif
#include "rna_event_handler.h"
+#include "pub_sub/dhcp_events.h"
using namespace snort;
++rna_stats.change_host_update;
pnd.generate_change_host_update();
}
+
+void RnaDHCPInfoEventHandler::handle(DataEvent& event, Flow*)
+{
+ Profile profile(rna_perf_stats);
+ ++rna_stats.dhcp_info;
+ pnd.add_dhcp_info(event);
+}
+
+void RnaDHCPDataEventHandler::handle(DataEvent& event, Flow*)
+{
+ Profile profile(rna_perf_stats);
+ ++rna_stats.dhcp_data;
+ pnd.analyze_dhcp_fingerprint(event);
+}
RnaPnd& pnd;
};
+class RnaDHCPInfoEventHandler : public snort::DataHandler
+{
+public:
+ RnaDHCPInfoEventHandler(RnaPnd& nd) : DataHandler(RNA_NAME), pnd(nd) { }
+ void handle(snort::DataEvent&, snort::Flow*) override;
+private:
+ RnaPnd& pnd;
+};
+
+class RnaDHCPDataEventHandler : public snort::DataHandler
+{
+public:
+ RnaDHCPDataEventHandler(RnaPnd& nd) : DataHandler(RNA_NAME), pnd(nd) { }
+ void handle(snort::DataEvent&, snort::Flow*) override;
+private:
+ RnaPnd& pnd;
+};
+
#endif
std::string host_name;
std::string device;
+ std::string dhcp55;
+ std::string dhcp60;
+
void clear()
{
fpid = 0;
user_agent.clear();
host_name.clear();
device.clear();
+ dhcp55.clear();
+ dhcp60.clear();
}
};
--- /dev/null
+//--------------------------------------------------------------------------
+// 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_fingerprint_udp.cc author Sreeja Athirkandathil Narayanan <sathirka@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "rna_fingerprint_udp.h"
+
+#include <sstream>
+
+#ifdef UNIT_TEST
+#include "catch/snort_catch.h"
+#endif
+
+#include "main/thread.h"
+#include "pub_sub/dhcp_events.h"
+
+using namespace snort;
+using namespace std;
+
+static THREAD_LOCAL UdpFpProcessor* udp_fp_processor = nullptr;
+
+void set_udp_fp_processor(UdpFpProcessor* processor)
+{
+ udp_fp_processor = processor;
+}
+
+UdpFpProcessor* get_udp_fp_processor()
+{
+ return udp_fp_processor;
+}
+
+static void parse_fp_element(const string& data, vector<FpElement>& fpe)
+{
+ istringstream in(data);
+ string tok;
+ while ( in >> tok )
+ fpe.emplace_back(tok);
+}
+
+DHCPFingerprint::DHCPFingerprint(const RawFingerprint& rfp)
+{
+ fpid = rfp.fpid;
+ fpuuid = rfp.fpuuid;
+ fp_type = rfp.fp_type;
+ if (!rfp.dhcp55.empty())
+ parse_fp_element(rfp.dhcp55, dhcp55);
+ if (!rfp.dhcp60.empty())
+ dhcp60 = rfp.dhcp60;
+}
+
+void UdpFpProcessor::push(const RawFingerprint& rfp)
+{
+ if (rfp.fp_type == FpFingerprint::FpType::FP_TYPE_DHCP)
+ {
+ DHCPFingerprint dhcp_fp(rfp);
+ if (dhcp_fp.dhcp55.size() > DHCP_OP55_MAX_SIZE)
+ return;
+ push_dhcp_fp(dhcp_fp);
+ }
+}
+
+static bool match_dhcp_options(const vector<FpElement>& options, const uint8_t* key_options)
+{
+ for (const auto& op : options)
+ {
+ if (op.d.value != *key_options++)
+ return false;
+ }
+ return true;
+}
+
+const DHCPFingerprint* UdpFpProcessor::match_dhcp_fingerprint(const FpDHCPKey& key) const
+{
+ if (key.dhcp55_len == 0 || key.dhcp55_len > DHCP_OP55_MAX_SIZE)
+ return nullptr;
+ uint32_t fptype = FpFingerprint::FpType::FP_TYPE_DHCP;
+ for (const auto& fp: dhcp_fps)
+ {
+ if (fptype == fp.fp_type && fp.dhcp55.size() == key.dhcp55_len and
+ match_dhcp_options(fp.dhcp55, key.dhcp55))
+ {
+ if (key.dhcp60 and !fp.dhcp60.empty())
+ {
+ if(fp.dhcp60.size() == key.dhcp60_len and
+ !fp.dhcp60.compare((const char*) (key.dhcp60)))
+ return &fp;
+ }
+ else if (fp.dhcp60.empty())
+ return &fp;
+ }
+ }
+ return nullptr;
+}
+
+#ifdef UNIT_TEST
+
+TEST_CASE("match_dhcp_fingerprint", "[rna_fingerprint_udp]")
+{
+ set_udp_fp_processor(new UdpFpProcessor);
+ UdpFpProcessor* processor = get_udp_fp_processor();
+
+ RawFingerprint rawfp;
+ rawfp.fpid = 111;
+ rawfp.fp_type = 4;
+ rawfp.fpuuid = "12345678-1234-1234-1234-123456789111";
+ rawfp.dhcp55 = "1 3 15 28 225";
+ rawfp.dhcp60 = "dhcp 5.1.2";
+ processor->push(rawfp);
+
+ FpDHCPKey key;
+ key.dhcp55_len = 0;
+ key.dhcp60 = (const uint8_t*) "dhcp 5.1.2";
+ key.dhcp60_len = 10;
+ // no dhcp55, only dhcp60 option, returns null
+ CHECK(processor->match_dhcp_fingerprint(key) == nullptr);
+
+ rawfp.fpid = 222;
+ rawfp.fp_type = 4;
+ rawfp.fpuuid = "12345678-1234-1234-1234-123456789222";
+ rawfp.dhcp55 = "1 2 45 121";
+ rawfp.dhcp60 = "dhcp 5.1.3";
+ processor->push(rawfp);
+
+ uint8_t op55[] = {1, 2, 45, 121};
+ key.dhcp55 = op55;
+ key.dhcp55_len = 4;
+ key.dhcp60 = (const uint8_t*) "dhcp 5.0";
+ key.dhcp60_len = 8;
+ //dhcp60 doesn't match, returns null
+ CHECK(processor->match_dhcp_fingerprint(key) == nullptr);
+}
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// 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_fingerprint_udp.h author Sreeja Athirkandathil Narayanan <sathirka@cisco.com>
+
+#ifndef RNA_FINGERPRINT_UDP_H
+#define RNA_FINGERPRINT_UDP_H
+
+#include <string>
+#include <vector>
+
+#include "rna_fingerprint.h"
+
+namespace snort
+{
+class SO_PUBLIC DHCPFingerprint : public FpFingerprint
+{
+public:
+ DHCPFingerprint() = default;
+ DHCPFingerprint(const RawFingerprint& rfp);
+ std::vector<FpElement> dhcp55;
+ std::string dhcp60;
+
+ void clear() override
+ {
+ FpFingerprint::clear();
+ dhcp55.clear();
+ dhcp60.clear();
+ }
+};
+
+struct FpDHCPKey
+{
+ unsigned dhcp55_len;
+ unsigned dhcp60_len;
+ const uint8_t* dhcp55;
+ const uint8_t* dhcp60;
+};
+
+class SO_PUBLIC UdpFpProcessor
+{
+public:
+ void push(const RawFingerprint& rfp);
+ const DHCPFingerprint* match_dhcp_fingerprint(const FpDHCPKey&) const;
+
+ void push_dhcp_fp(DHCPFingerprint& dhcp_fp)
+ {
+ dhcp_fps.emplace_back(dhcp_fp);
+ }
+private:
+ std::vector<DHCPFingerprint> dhcp_fps;
+};
+
+}
+
+snort::UdpFpProcessor* get_udp_fp_processor();
+SO_PUBLIC void set_udp_fp_processor(snort::UdpFpProcessor*);
+#endif
#include "main/snort.h"
#include "managers/inspector_manager.h"
#include "protocols/packet.h"
+#include "pub_sub/dhcp_events.h"
#include "rna_event_handler.h"
#include "rna_fingerprint_tcp.h"
#include "rna_fingerprint_ua.h"
+#include "rna_fingerprint_udp.h"
#include "rna_module.h"
#include "rna_pnd.h"
{
delete mod_conf->tcp_processor;
delete mod_conf->ua_processor;
+ delete mod_conf->udp_processor;
delete mod_conf;
}
}
bool RnaInspector::configure(SnortConfig* sc)
{
DataBus::subscribe_global( APPID_EVENT_ANY_CHANGE, new RnaAppidEventHandler(*pnd), sc );
+ DataBus::subscribe_global( DHCP_INFO_EVENT, new RnaDHCPInfoEventHandler(*pnd), sc);
+ DataBus::subscribe_global( DHCP_DATA_EVENT, new RnaDHCPDataEventHandler(*pnd), sc);
DataBus::subscribe_global( STREAM_ICMP_NEW_FLOW_EVENT, new RnaIcmpNewFlowEventHandler(*pnd), sc );
DataBus::subscribe_global( STREAM_ICMP_BIDIRECTIONAL_EVENT, new RnaIcmpBidirectionalEventHandler(*pnd), sc );
// thread local initialization
set_tcp_fp_processor(mod_conf->tcp_processor);
set_ua_fp_processor(mod_conf->ua_processor);
+ set_udp_fp_processor(mod_conf->udp_processor);
}
void RnaInspector::tterm()
in_stream.close();
}
-void RnaInspector::get_or_create_fp_processor(TcpFpProcessor*& tfp, UaFpProcessor*& uafp)
+void RnaInspector::get_or_create_fp_processor(TcpFpProcessor*& tfp, UaFpProcessor*& uafp,
+ UdpFpProcessor*& udpfp)
{
if ( !mod_conf )
return;
mod_conf->tcp_processor = new TcpFpProcessor;
if ( !mod_conf->ua_processor )
mod_conf->ua_processor = new UaFpProcessor;
+ if ( !mod_conf->udp_processor )
+ mod_conf->udp_processor = new UdpFpProcessor;
tfp = mod_conf->tcp_processor;
uafp = mod_conf->ua_processor;
+ udpfp = mod_conf->udp_processor;
}
-void RnaInspector::set_fp_processor(TcpFpProcessor* tfp, UaFpProcessor* uafp)
+void RnaInspector::set_fp_processor(TcpFpProcessor* tfp, UaFpProcessor* uafp, UdpFpProcessor* udpfp)
{
if ( !mod_conf )
return;
delete mod_conf->ua_processor;
mod_conf->ua_processor = uafp;
+
+ delete mod_conf->udp_processor;
+ mod_conf->udp_processor = udpfp;
}
//-------------------------------------------------------------------------
RnaInspector ins(&mod);
TcpFpProcessor* tfp = nullptr;
UaFpProcessor* uafp = nullptr;
- ins.set_fp_processor(tfp, uafp);
- ins.get_or_create_fp_processor(tfp, uafp);
+ UdpFpProcessor* udpfp = nullptr;
+ ins.set_fp_processor(tfp, uafp, udpfp);
+ ins.get_or_create_fp_processor(tfp, uafp, udpfp);
CHECK(tfp != nullptr);
CHECK(uafp != nullptr);
+ CHECK(udpfp != nullptr);
}
}
#endif
struct Packet;
class TcpFpProcessor;
class UaFpProcessor;
+class UdpFpProcessor;
}
struct RnaConfig;
void tinit() override;
void tterm() override;
- void get_or_create_fp_processor(snort::TcpFpProcessor*&, snort::UaFpProcessor*&);
- void set_fp_processor(snort::TcpFpProcessor*, snort::UaFpProcessor*);
+ void get_or_create_fp_processor(snort::TcpFpProcessor*&, snort::UaFpProcessor*&,
+ snort::UdpFpProcessor*&);
+ void set_fp_processor(snort::TcpFpProcessor*, snort::UaFpProcessor*, snort::UdpFpProcessor*);
private:
void load_rna_conf();
SfIpString ipbuf;
ip.set(rle.ip); // using this instead of packet's ip to support ARP
if ( rle.mac )
- debug_logf(rna_trace, p, "RNA log: type %u, subtype %u, mac %s, ip %s\n",
- rle.type, rle.subtype, macbuf, ip.ntop(ipbuf));
+ {
+ if (rle.type == RNA_EVENT_CHANGE and rle.subtype == CHANGE_FULL_DHCP_INFO)
+ {
+ SfIp router;
+ SfIpString routerbuf;
+ router.set(rle.router);
+ debug_logf(rna_trace, p, "RNA DHCP full information log: type %u, "
+ "subtype %u, mac %s, ip %s, lease time %u, netmask %x, router %s\n",
+ rle.type, rle.subtype, macbuf, ip.ntop(ipbuf), rle.lease,
+ rle.netmask, router.ntop(routerbuf));
+ }
+ else
+ debug_logf(rna_trace, p, "RNA log: type %u, subtype %u, mac %s, ip %s\n",
+ rle.type, rle.subtype, macbuf, ip.ntop(ipbuf));
+ }
else
debug_logf(rna_trace, p, "RNA log: type %u, subtype %u, ip %s\n",
rle.type, rle.subtype, ip.ntop(ipbuf));
nullptr, nullptr, nullptr, cond_var);
}
+void RnaLogger::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 lease, uint32_t netmask,
+ const struct in6_addr* router)
+{
+ log(type, subtype, src_ip, src_mac, ht, p, 0, 0, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, APP_ID_NONE, nullptr, false, lease, netmask, router);
+}
+
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,
- const char* user, AppId appid, const char* di, bool jb)
+ const char* user, AppId appid, const char* di, bool jb, uint32_t lease,
+ uint32_t netmask, const struct in6_addr* router)
{
if ( !enabled )
return false;
assert(ht);
RnaLoggerEvent rle(type, subtype, src_mac, ht, hm, proto, cond_var,
- ha, fp, hc, user, appid, di, jb, p);
+ ha, fp, hc, user, appid, di, jb, lease, netmask, router, p);
if ( src_ip and (!IN6_IS_ADDR_V4MAPPED(src_ip) or src_ip->s6_addr32[3]) )
rle.ip = src_ip;
else
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, const char* u,
- int32_t app, const char* di, bool jb, const snort::Packet* p) : type(t), subtype(st),
+ int32_t app, const char* di, bool jb, uint32_t ls, uint32_t nm,
+ const struct in6_addr* rtr, const snort::Packet* p) : type(t), subtype(st),
mac(mc), ht(rt), hm(hmp), proto(pr), cond_var(cv), ha(hap), fp(fpr), hc(hcp),
- user(u), appid(app), device_info(di), jail_broken(jb), pkt(p) { }
+ user(u), appid(app), device_info(di), jail_broken(jb), lease(ls), netmask(nm),
+ router(rtr), pkt(p) { }
uint32_t event_time = 0;
uint16_t type;
AppId appid;
const char* device_info;
bool jail_broken;
+ uint32_t lease;
+ uint32_t netmask;
+ const struct in6_addr* router;
const snort::Packet* pkt;
};
void log(uint16_t type, uint16_t subtype, const snort::Packet* p, const uint8_t* src_mac,
const struct in6_addr* src_ip, RnaTracker* ht, uint32_t event_time, void* cond_var);
+ // for dhcp info event
+ void 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 lease, uint32_t netmask,
+ const struct in6_addr* router);
+
// 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,
const snort::HostApplication* ha = nullptr, const snort::FpFingerprint* fp = nullptr,
void* cond_var = nullptr, const snort::HostClient* hc = nullptr,
const char* user = nullptr, AppId appid = APP_ID_NONE, const char* device_info = nullptr,
- bool jail_broken = false);
+ bool jail_broken = false, uint32_t lease = 0, uint32_t netmask = 0,
+ const struct in6_addr* router = nullptr);
private:
const bool enabled;
#define CHANGE_VLAN_TAG 18
#define CHANGE_BANNER_UPDATE 24
#define CHANGE_CLIENT_APP_UPDATE 32
+ #define CHANGE_FULL_DHCP_INFO 33
#define RUA_EVENT 1004
#define CHANGE_USER_LOGIN 2
#include "rna_fingerprint_tcp.h"
#include "rna_fingerprint_ua.h"
+#include "rna_fingerprint_udp.h"
#include "rna_mac_cache.h"
#ifdef UNIT_TEST
{
set_tcp_fp_processor(mod_conf.tcp_processor);
set_ua_fp_processor(mod_conf.ua_processor);
+ set_udp_fp_processor(mod_conf.udp_processor);
return false; // no work to do after this
}
{ "device", Parameter::PT_STRING, nullptr, nullptr,
"device information" },
+ { "dhcp55", Parameter::PT_STRING, nullptr, nullptr,
+ "dhcp option 55 values" },
+
+ { "dhcp60", Parameter::PT_STRING, nullptr, nullptr,
+ "dhcp option 60 values" },
+
{ nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
};
{ "ua_fingerprints", Parameter::PT_LIST, rna_fp_params, nullptr,
"list of user agent fingerprints" },
+ { "udp_fingerprints", Parameter::PT_LIST, rna_fp_params, nullptr,
+ "list of udp fingerprints" },
+
{ nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
};
{ CountType::SUM, "tcp_midstream", "count of TCP midstream packets received" },
{ CountType::SUM, "other_packets", "count of packets received without session tracking" },
{ CountType::SUM, "change_host_update", "count number of change host update events" },
+ { CountType::SUM, "dhcp_data", "count of DHCP data events received" },
+ { CountType::SUM, "dhcp_info", "count of new DHCP lease events received" },
{ CountType::END, nullptr, nullptr},
};
if (!mod_conf->ua_processor)
mod_conf->ua_processor = new UaFpProcessor;
}
+ else if (!strcmp(fqn, "rna.udp_fingerprints"))
+ {
+ fingerprint.clear();
+ if (!mod_conf->udp_processor)
+ mod_conf->udp_processor = new UdpFpProcessor;
+ }
return true;
}
dump_file = snort_strdup(v.get_string());
}
else if ( fqn and ( strstr(fqn, "rna.tcp_fingerprints") or
- strstr(fqn, "rna.ua_fingerprints") ) )
+ strstr(fqn, "rna.ua_fingerprints") or strstr(fqn, "rna.udp_fingerprints") ) )
{
if (v.is("fpid"))
fingerprint.fpid = v.get_uint32();
return false;
fingerprint.user_agent.emplace_back(ua_part);
}
+ else if (v.is("dhcp55"))
+ fingerprint.dhcp55 = v.get_string();
+ else if (v.is("dhcp60"))
+ fingerprint.dhcp60 = v.get_string();
else
return false;
}
mod_conf->ua_processor->push(fingerprint);
fingerprint.clear();
}
+ else if ( index > 0 and mod_conf->udp_processor and !strcmp(fqn, "rna.udp_fingerprints") )
+ {
+ mod_conf->udp_processor->push(fingerprint);
+ fingerprint.clear();
+ }
return true;
}
bool RnaModule::is_valid_fqn(const char* fqn) const
{
return !strcmp(fqn, RNA_NAME) or !strcmp(fqn, "rna.tcp_fingerprints") or
- !strcmp(fqn, "rna.ua_fingerprints") or !strcmp(fqn, "rna.ua_fingerprints.user_agent");
+ !strcmp(fqn, "rna.ua_fingerprints") or !strcmp(fqn, "rna.ua_fingerprints.user_agent") or
+ !strcmp(fqn, "rna.udp_fingerprints");
}
PegCount tcp_midstream;
PegCount other_packets;
PegCount change_host_update;
+ PegCount dhcp_data;
+ PegCount dhcp_info;
};
extern THREAD_LOCAL RnaStats rna_stats;
#include "rna_app_discovery.h"
#include "rna_fingerprint_tcp.h"
+#include "rna_fingerprint_udp.h"
#include "rna_logger_common.h"
#ifdef UNIT_TEST
}
}
+void RnaPnd::analyze_dhcp_fingerprint(DataEvent& event)
+{
+ const Packet* p = event.get_packet();
+ const DHCPDataEvent& dhcp_data_event = static_cast<DHCPDataEvent&>(event);
+ const uint8_t* src_mac = dhcp_data_event.get_eth_addr();
+ 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();
+
+ MacKey mk(src_mac);
+ auto hm_ptr = host_cache_mac.find_else_create(mk, &new_mac);
+ if (new_mac)
+ {
+ ht->add_mac(mk.mac_addr, p->ptrs.ip_api.ttl(), 0);
+ logger.log(RNA_EVENT_NEW, NEW_HOST, p, &ht, nullptr, mk.mac_addr);
+ hm_ptr->update_last_event(p->pkth->ts.tv_sec);
+ }
+ else
+ hm_ptr->update_last_seen(p->pkth->ts.tv_sec);
+
+ const UdpFpProcessor* processor = get_udp_fp_processor();
+ if (!processor)
+ return;
+
+ FpDHCPKey key;
+ key.dhcp55_len = dhcp_data_event.get_op55_len();
+ key.dhcp55 = dhcp_data_event.get_op55();
+ key.dhcp60_len = dhcp_data_event.get_op60_len();
+ key.dhcp60 = dhcp_data_event.get_op60();
+
+ const DHCPFingerprint* dhcp_fp = processor->match_dhcp_fingerprint(key);
+ if (dhcp_fp and ht->add_udp_fingerprint(dhcp_fp->fpid))
+ {
+ const auto& src_ip_ptr = (const struct in6_addr*) src_ip->get_ip6_ptr();
+ logger.log(RNA_EVENT_NEW, NEW_OS, p, &ht, src_ip_ptr, src_mac, dhcp_fp, packet_time());
+ }
+}
+
+/* called for processing information extracted from DHCP Ack.
+ It is called only for IPv4 since DHCPv6 is not implemented.*/
+void RnaPnd::add_dhcp_info(DataEvent& event)
+{
+ const DHCPInfoEvent& dhcp_info_event = static_cast<DHCPInfoEvent&>(event);
+ const uint8_t* src_mac = dhcp_info_event.get_eth_addr();
+ uint32_t ip_address = dhcp_info_event.get_ip_address();
+ uint32_t net_mask = dhcp_info_event.get_subnet_mask();
+ uint32_t lease = dhcp_info_event.get_lease_secs();
+ uint32_t router = dhcp_info_event.get_router();
+ const Packet* p = event.get_packet();
+
+ SfIp leased_ip = {(void*)&ip_address, AF_INET};
+ SfIp router_ip = {(void*)&router, AF_INET};
+ bool new_host = false;
+ bool new_mac = false;
+ auto ht = host_cache.find_else_create(leased_ip, &new_host);
+ if (!new_host)
+ ht->update_last_seen();
+
+ MacKey mk(src_mac);
+ auto hm_ptr = host_cache_mac.find_else_create(mk, &new_mac);
+ if (new_mac)
+ {
+ ht->add_mac(mk.mac_addr, p->ptrs.ip_api.ttl(), 0);
+ logger.log(RNA_EVENT_NEW, NEW_HOST, p, &ht, nullptr, mk.mac_addr);
+ hm_ptr->update_last_event(p->pkth->ts.tv_sec);
+ }
+ else
+ hm_ptr->update_last_seen(p->pkth->ts.tv_sec);
+
+ logger.log(RNA_EVENT_CHANGE, CHANGE_FULL_DHCP_INFO, p, &ht,
+ (const struct in6_addr*) leased_ip.get_ip6_ptr(), src_mac,
+ lease, net_mask, (const struct in6_addr*) router_ip.get_ip6_ptr());
+}
+
inline void RnaPnd::update_vlan(const Packet* p, HostTrackerMac& hm)
{
if ( !(p->proto_bits & PROTO_BIT__VLAN) )
#include "protocols/tcp.h"
#include "protocols/vlan.h"
#include "pub_sub/appid_events.h"
+#include "pub_sub/dhcp_events.h"
#include "sfip/sf_ip.h"
#include "rna_config.h"
void analyze_flow_non_ip(const snort::Packet*);
void analyze_flow_tcp(const snort::Packet*, TcpPacketType);
void analyze_flow_udp(const snort::Packet*);
+ void analyze_dhcp_fingerprint(snort::DataEvent&);
+ void add_dhcp_info(snort::DataEvent&);
// generate change event for all hosts in the ip cache
void generate_change_host_update();
void UaFpProcessor::make_mpse(SnortConfig*) { }
void UaFpProcessor::push(RawFingerprint const&) { }
+void UdpFpProcessor::push(RawFingerprint const&) { }
+
// inspector
class RnaInspector
{
void set_tcp_fp_processor(TcpFpProcessor*) { }
void set_ua_fp_processor(UaFpProcessor*) { }
+void set_udp_fp_processor(UdpFpProcessor*) { }
namespace snort
{
data_decrypt_event.h
daq_message_event.h
dcerpc_events.h
+ dhcp_events.h
expect_events.h
finalize_packet_event.h
http_events.h
--- /dev/null
+//--------------------------------------------------------------------------
+// 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.
+//--------------------------------------------------------------------------
+// dhcp_events.h author Sreeja Athirkandathil Narayanan <sathirka@cisco.com>
+
+#ifndef DHCP_EVENTS_H
+#define DHCP_EVENTS_H
+
+#include <cstring>
+#include "framework/data_bus.h"
+
+#define DHCP_DATA_EVENT "dhcp_data_event"
+#define DHCP_INFO_EVENT "dhcp_info_event"
+#define DHCP_OP55_MAX_SIZE 64
+#define DHCP_OP60_MAX_SIZE 64
+
+namespace snort
+{
+
+class DHCPInfoEvent : public snort::DataEvent
+{
+public:
+ DHCPInfoEvent(const snort::Packet* p, uint32_t ip_address, const uint8_t* eth,
+ uint32_t subnet_mask, uint32_t lease_secs, uint32_t router) :
+ pkt(p), ip_address(ip_address), subnet_mask(subnet_mask),
+ lease_secs(lease_secs), router(router)
+ {
+ memcpy(eth_addr, eth, sizeof(eth_addr));
+ }
+
+ const snort::Packet* get_packet() override
+ { return pkt; }
+
+ uint32_t get_ip_address() const
+ { return ip_address; }
+
+ const uint8_t* get_eth_addr() const
+ { return eth_addr; }
+
+ uint32_t get_subnet_mask() const
+ { return subnet_mask; }
+
+ uint32_t get_lease_secs() const
+ { return lease_secs; }
+
+ uint32_t get_router() const
+ { return router; }
+
+private:
+ const snort::Packet* pkt;
+ uint32_t ip_address;
+ uint8_t eth_addr[6];
+ uint32_t subnet_mask;
+ uint32_t lease_secs;
+ uint32_t router;
+};
+
+class DHCPDataEvent : public snort::DataEvent
+{
+public:
+ DHCPDataEvent(const snort::Packet* p, unsigned op55_len, unsigned op60_len,
+ const uint8_t* op55_val, const uint8_t* op60_val, const uint8_t* eth) : pkt(p),
+ op55_len(op55_len), op60_len(op60_len)
+ {
+ memcpy(op55, op55_val, op55_len);
+ if (op60_len)
+ memcpy(op60, op60_val, op60_len);
+ memcpy(eth_addr, eth, sizeof(eth_addr));
+ }
+
+ const snort::Packet* get_packet() override
+ { return pkt; }
+
+ unsigned get_op55_len() const
+ { return op55_len; }
+
+ unsigned get_op60_len() const
+ { return op60_len; }
+
+ const uint8_t* get_op55() const
+ { return op55; }
+
+ const uint8_t* get_op60() const
+ { return op60; }
+
+ const uint8_t* get_eth_addr() const
+ { return eth_addr; }
+
+private:
+ const snort::Packet* pkt;
+ unsigned op55_len;
+ unsigned op60_len;
+ uint8_t op55[DHCP_OP55_MAX_SIZE] = {0};
+ uint8_t op60[DHCP_OP60_MAX_SIZE] = {0};
+ uint8_t eth_addr[6];
+};
+
+}
+
+#endif // DHCP_EVENTS_H