if ( !p )
return false;
- const auto& src_ip = p->ptrs.ip_api.get_src();
- const auto& src_ip_ptr = (const struct in6_addr*) src_ip->get_ip6_ptr();
- const auto& src_mac = layer::get_eth_layer(p)->ether_src;
NetflowEvent* nfe = static_cast<NetflowEvent*>(&event);
- const NetflowSessionRecord* nf_record = nfe->get_record();
- // process host and service log events
- UNUSED(src_ip_ptr);
- UNUSED(src_mac);
- UNUSED(nf_record);
+ if (nfe->get_create_host())
+ analyze_netflow_host(nfe);
+
+ if (nfe->get_create_service())
+ analyze_netflow_service(nfe);
return true;
}
+void RnaPnd::analyze_netflow_host(NetflowEvent* nfe)
+{
+ const Packet* p = nfe->get_packet();
+ if ( !p )
+ return;
+
+ bool new_host = false;
+ const auto& src_ip = nfe->get_record()->initiator_ip;
+ const auto& src_ip_ptr = (const struct in6_addr*) src_ip.get_ip6_ptr();
+
+ auto ht = find_or_create_host_tracker(src_ip, new_host);
+
+ if ( !new_host )
+ ht->update_last_seen();
+
+ const uint8_t src_mac[6] = {0};
+
+ if ( new_host )
+ logger.log(RNA_EVENT_NEW, NEW_HOST, p, &ht, src_ip_ptr, src_mac);
+
+ uint16_t ptype = rna_get_eth(p);
+ if ( ptype > to_utype(ProtocolId::ETHERTYPE_MINIMUM) )
+ {
+ if ( ht->add_network_proto(ptype) )
+ logger.log(RNA_EVENT_NEW, NEW_NET_PROTOCOL, p, &ht, ptype, src_mac, src_ip_ptr,
+ packet_time());
+ }
+
+ ptype = to_utype(p->get_ip_proto_next());
+ if ( ht->add_xport_proto(ptype) )
+ logger.log(RNA_EVENT_NEW, NEW_XPORT_PROTOCOL, p, &ht, ptype, src_mac, src_ip_ptr,
+ packet_time());
+
+ if ( !new_host )
+ generate_change_host_update(&ht, p, &src_ip, src_mac, packet_time());
+}
+
+void RnaPnd::analyze_netflow_service(NetflowEvent* nfe)
+{
+
+ const Packet* p = nfe->get_packet();
+ if ( !p )
+ return;
+
+ bool new_host = false;
+ const auto& src_ip = nfe->get_record()->initiator_ip;
+ const auto& mac_addr = layer::get_eth_layer(p)->ether_src;
+ uint32_t service = nfe->get_service_id();
+ uint16_t port = nfe->get_record()->responder_port;
+ IpProtocol proto = (IpProtocol) nfe->get_record()->proto;
+
+ auto ht = find_or_create_host_tracker(src_ip, new_host);
+ ht->update_last_seen();
+
+ bool is_new = false;
+ auto ha = ht->add_service(port, proto, (uint32_t) packet_time(), is_new, service);
+ if ( is_new )
+ {
+ if ( proto == IpProtocol::TCP )
+ logger.log(RNA_EVENT_NEW, NEW_TCP_SERVICE, p, &ht,
+ (const struct in6_addr*) src_ip.get_ip6_ptr(), mac_addr, &ha);
+ else
+ logger.log(RNA_EVENT_NEW, NEW_UDP_SERVICE, p, &ht,
+ (const struct in6_addr*) src_ip.get_ip6_ptr(), mac_addr, &ha);
+
+ ha.hits = 0;
+ ht->update_service(ha);
+ }
+}
void RnaPnd::discover_network_icmp(const Packet* p)
{
discover_network(p, 0);
void analyze_smb_fingerprint(snort::DataEvent&);
bool analyze_cpe_os_info(snort::DataEvent&);
bool analyze_netflow(snort::DataEvent&);
+ void analyze_netflow_host(snort::NetflowEvent*);
+ void analyze_netflow_service(snort::NetflowEvent*);
// generate change event for all hosts in the ip cache
void generate_change_host_update();
class NetflowEvent : public DataEvent
{
public:
- NetflowEvent(const snort::Packet* p, const NetflowSessionRecord* rec)
- : pkt(p), record(rec) { }
+ NetflowEvent(const snort::Packet* p, const NetflowSessionRecord* rec,
+ bool cre_host, bool cre_serv, uint32_t s_id)
+ : pkt(p), record(rec), create_host(cre_host),
+ create_service(cre_serv), serviceID(s_id) { }
const Packet* get_packet() override
{ return pkt; }
const NetflowSessionRecord* get_record()
{ return record; }
+ bool get_create_host()
+ { return create_host; }
+
+ bool get_create_service()
+ { return create_service; }
+
+ uint32_t get_service_id()
+ { return serviceID; }
+
private:
const Packet* pkt;
const NetflowSessionRecord* record;
+ bool create_host;
+ bool create_service;
+ uint32_t serviceID = 0;
};
}
#include <vector>
#include "log/messages.h"
+#include "managers/module_manager.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "pub_sub/netflow_event.h"
{ return a.first.less_than(b.first); }
};
+static std::unordered_map<int, int>* udp_srv_map = nullptr;
+static std::unordered_map<int, int>* tcp_srv_map = nullptr;
+
// -----------------------------------------------------------------------------
// static functions
// -----------------------------------------------------------------------------
-
-static bool filter_record(const NetflowRules* rules, const int zone,
+static const NetflowRule* filter_record(const NetflowRules* rules, const int zone,
const SfIp* src, const SfIp* dst)
{
const SfIp* addr[2] = {src, dst};
for( auto const& rule : rules->exclude )
{
if ( rule.filter_match(address, zone) )
- return false;
+ return nullptr;
}
}
for( auto const& rule : rules->include )
{
if ( rule.filter_match(address, zone) )
- return true;
+ return &rule;
}
}
- return false;
+ return nullptr;
+}
+
+static void publish_service_event(const Packet* p, const NetflowRule* match, NetflowSessionRecord& record)
+{
+ uint32_t serviceID = 0;
+
+ if (record.proto == (int) ProtocolId::TCP and tcp_srv_map)
+ serviceID = (*tcp_srv_map)[record.responder_port];
+ else if (record.proto == (int) ProtocolId::UDP and udp_srv_map)
+ serviceID = (*udp_srv_map)[record.responder_port];
+
+ NetflowEvent event(p, &record, match->create_host, match->create_service, serviceID);
+ DataBus::publish(NETFLOW_EVENT, event);
}
static bool version_9_record_update(const unsigned char* data, uint32_t unix_secs,
}
// filter based on configuration
- if ( !filter_record(p_rules, zone, &record.initiator_ip, &record.responder_ip) )
+ const NetflowRule* match = filter_record(p_rules, zone, &record.initiator_ip, &record.responder_ip);
+ if ( !match )
{
records--;
continue;
if ( !record_status.src_tos )
record.nf_src_tos = record.nf_dst_tos;
}
- // send create_host and create_service flags too so that rna event handler can log those
- NetflowEvent event(p, &record);
- DataBus::publish(NETFLOW_EVENT, event);
+
+ if (match->create_service)
+ publish_service_event(p, match, record);
+
}
if ( netflow_cache->add(record.initiator_ip, record, true) )
if ( record.next_hop_ip.set(&precord->next_hop_addr, AF_INET) != SFIP_SUCCESS )
return false;
- if ( !filter_record(p_rules, zone, &record.initiator_ip, &record.responder_ip) )
+ const NetflowRule* match = filter_record(p_rules, zone, &record.initiator_ip, &record.responder_ip);
+ if ( !match )
continue;
record.initiator_port = ntohs(precord->src_port);
if ( netflow_cache->add(record.initiator_ip, record, false) )
++netflow_stats.unique_flows;
- // send create_host and create_service flags too so that rna event handler can log those
- NetflowEvent event(p, &record);
- DataBus::publish(NETFLOW_EVENT, event);
+ if (match->create_service)
+ publish_service_event(p, match, record);
}
return true;
}
return retval;
}
-
//-------------------------------------------------------------------------
// inspector stuff
//-------------------------------------------------------------------------
if ( ! dump_cache )
dump_cache = new DumpCache;
}
+
+ NetflowModule* mod = (NetflowModule*) ModuleManager::get_module(NETFLOW_NAME);
+
+ if (mod)
+ {
+ udp_srv_map = &mod->udp_service_mappings;
+ tcp_srv_map = &mod->tcp_service_mappings;
+ }
}
NetflowInspector::~NetflowInspector()
NETFLOW_DST_IPV6 = 28,
NETFLOW_SRC_MASK_IPV6 = 29,
NETFLOW_DST_MASK_IPV6 = 30,
- NETFLOW_DST_TOS = 55
+ NETFLOW_DST_TOS = 55,
};
struct NetflowSessionRecord
#include "config.h"
#endif
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+
#include "netflow_module.h"
#include "utils/util.h"
{ "template_memcap", Parameter::PT_INT, "0:maxSZ", "0",
"maximum memory for template cache in bytes, 0 = unlimited" },
+ { "netflow_service_id_path", Parameter::PT_STRING, nullptr, nullptr,
+ "path to file containing service IDs for NetFlow" },
+
{ nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
};
{
rule_cfg.create_service = v.get_bool();
}
+ else if ( v.is("netflow_service_id_path") )
+ {
+ parse_service_id_file(v.get_string());
+ }
return true;
}
+void NetflowModule::parse_service_id_file(const std::string& serv_id_file_path)
+{
+ std::string serv_line;
+ std::ifstream serv_id_file;
+ serv_id_file.open(serv_id_file_path);
+
+ if ( serv_id_file.is_open() )
+ {
+ while ( std::getline(serv_id_file, serv_line) )
+ {
+ std::stringstream ss(serv_line);
+ std::vector<std::string> tokens;
+
+ std::string tmp_str;
+
+ while( std::getline(ss, tmp_str, '\t') )
+ tokens.push_back(tmp_str);
+
+ // Format is <port> <tcp/udp> <internal ID>
+ uint16_t srv_port = std::stoi(tokens[0]);
+ std::string proto_str = tokens[1];
+ uint16_t id = std::stoi(tokens[2]);
+
+ if ( proto_str == "tcp" )
+ tcp_service_mappings[srv_port] = id;
+ else if ( proto_str == "udp" )
+ udp_service_mappings[srv_port] = id;
+ }
+ }
+}
+
PegCount* NetflowModule::get_counts() const
{ return (PegCount*)&netflow_stats; }
snort::ProfileStats* get_profile() const override;
NetflowConfig* get_data();
+ void parse_service_id_file(const std::string& serv_id_file_path);
+
+ std::unordered_map<int, int> udp_service_mappings;
+ std::unordered_map<int, int> tcp_service_mappings;
+
Usage get_usage() const override
{ return INSPECT; }