From: Steve Chew (stechew) Date: Tue, 8 Sep 2020 20:51:37 +0000 (+0000) Subject: Merge pull request #2455 in SNORT/snort3 from ~SHASLAD/snort3:netflow_as_inspector... X-Git-Tag: 3.0.2-6~10 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1246a1f84846c4e7156e4d02a826786073094004;p=thirdparty%2Fsnort3.git Merge pull request #2455 in SNORT/snort3 from ~SHASLAD/snort3:netflow_as_inspector to master Squashed commit of the following: commit 08bc5e0c873d97912e9986c5f3ee57ab5eeb0831 Author: Shashi Lad Date: Fri Aug 28 08:18:29 2020 -0400 netflow: introducing netflow as inspector --- diff --git a/lua/snort.lua b/lua/snort.lua index e4eb1fa8e..e3a23af2d 100644 --- a/lua/snort.lua +++ b/lua/snort.lua @@ -58,6 +58,7 @@ http_inspect = { } http2_inspect = { } imap = { } modbus = { } +netflow = {} normalizer = { } pop = { } rpc_decode = { } @@ -118,6 +119,7 @@ binder = { when = { proto = 'tcp', service = 'dcerpc' }, use = { type = 'dce_tcp' } }, { when = { proto = 'udp', service = 'dcerpc' }, use = { type = 'dce_udp' } }, + { when = { proto = 'udp', service = 'netflow' }, use = { type = 'netflow' } }, { when = { service = 'netbios-ssn' }, use = { type = 'dce_smb' } }, { when = { service = 'dce_http_server' }, use = { type = 'dce_http_server' } }, diff --git a/lua/snort_defaults.lua b/lua/snort_defaults.lua index f95698699..e3df6eaa7 100644 --- a/lua/snort_defaults.lua +++ b/lua/snort_defaults.lua @@ -354,6 +354,12 @@ telnet_commands = '|FF FC|', '|FF FD|', '|FF FE|', '|FF FF|' } + +netflow_versions = +{ + '|00 05|', '|00 09|' +} + default_wizard = { spells = @@ -392,8 +398,12 @@ default_wizard = { service = 'dnp3', proto = 'tcp', client_first = true, to_server = { '|05 64|' }, to_client = { '|05 64|' } }, + { service = 'netflow', proto = 'udp', client_first = true, + to_server = netflow_versions }, + { service = 'http2', proto = 'tcp', client_first = true, to_server = { '|50 52 49 20 2a 20 48 54 54 50 2f 32 2e 30 0d 0a 0d 0a 53 4d 0d 0a 0d 0a|' } }, + --[[ { service = 'modbus', proto = 'tcp', client_first = true, to_server = { '??|0 0|' } }, @@ -1188,7 +1198,7 @@ default_whitelist = ip_med_sweep ip_med_dist ip_hi_proto ip_hi_decoy ip_hi_sweep ip_hi_dist icmp_low_sweep icmp_med_sweep icmp_hi_sweep default_hi_port_scan default_med_port_scan default_low_port_scan - default_variables + default_variables netflow_versions ]] snort_whitelist_append(default_whitelist) diff --git a/src/service_inspectors/CMakeLists.txt b/src/service_inspectors/CMakeLists.txt index f1a831eca..cc465800d 100644 --- a/src/service_inspectors/CMakeLists.txt +++ b/src/service_inspectors/CMakeLists.txt @@ -6,18 +6,19 @@ add_subdirectory(dnp3) add_subdirectory(dns) add_subdirectory(ftp_telnet) add_subdirectory(gtp) -add_subdirectory(imap) -add_subdirectory(modbus) add_subdirectory(http_inspect) add_subdirectory(http2_inspect) +add_subdirectory(imap) +add_subdirectory(modbus) +add_subdirectory(netflow) add_subdirectory(pop) add_subdirectory(rpc_decode) +add_subdirectory(s7commplus) add_subdirectory(sip) add_subdirectory(smtp) add_subdirectory(ssh) add_subdirectory(ssl) add_subdirectory(wizard) -add_subdirectory(s7commplus) if (STATIC_INSPECTORS) set (STATIC_INSPECTOR_OBJS @@ -30,12 +31,13 @@ if (STATIC_INSPECTORS) $ $ $ + $ $ $ + $ $ $ $ - $ ) endif() diff --git a/src/service_inspectors/netflow/CMakeLists.txt b/src/service_inspectors/netflow/CMakeLists.txt new file mode 100644 index 000000000..fe9cb90f4 --- /dev/null +++ b/src/service_inspectors/netflow/CMakeLists.txt @@ -0,0 +1,15 @@ + +set ( FILE_LIST + netflow.cc + netflow.h + netflow_module.cc + netflow_module.h +) + +if (STATIC_INSPECTORS) + add_library( netflow OBJECT ${FILE_LIST}) + +else (STATIC_INSPECTORS) + add_dynamic_module(netflow inspectors ${FILE_LIST}) + +endif (STATIC_INSPECTORS) diff --git a/src/service_inspectors/netflow/netflow.cc b/src/service_inspectors/netflow/netflow.cc new file mode 100644 index 000000000..0d9db1565 --- /dev/null +++ b/src/service_inspectors/netflow/netflow.cc @@ -0,0 +1,206 @@ +//-------------------------------------------------------------------------- +// 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. +//-------------------------------------------------------------------------- + +// netflow.cc author Ron Dempster +// Shashikant Lad + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "netflow.h" +#include "netflow_module.h" + +#include "host_tracker/host_cache.h" +#include "profiler/profiler.h" +#include "protocols/packet.h" + +using namespace snort; +using namespace std; + +THREAD_LOCAL NetflowStats netflow_stats; +THREAD_LOCAL ProfileStats netflow_perf_stats; + +// ----------------------------------------------------------------------------- +// static functions +// ----------------------------------------------------------------------------- +// FIXIT-M - keeping only few checks right now +static bool decode_netflow_v9(const unsigned char* data, uint16_t size) +{ + Netflow9Hdr header; + const Netflow9Hdr *pheader; + + if( size < sizeof(Netflow9Hdr) ) + return false; + + pheader = (const Netflow9Hdr *)data; + header.flow_count = ntohs(pheader->flow_count); + + // Invalid header flow count + if( header.flow_count < NETFLOW_MIN_COUNT or header.flow_count > NETFLOW_MAX_COUNT) + return false; + + return true; +} + +static bool decode_netflow_v5(const unsigned char* data, uint16_t size) +{ + Netflow5Hdr header; + const Netflow5Hdr *pheader; + const Netflow5RecordHdr *precord; + const Netflow5RecordHdr *end; + + end = (const Netflow5RecordHdr *)(data + size); + + pheader = (const Netflow5Hdr *)data; + header.flow_count = ntohs(pheader->flow_count); + + // invalid header flow count + if( header.flow_count < NETFLOW_MIN_COUNT or header.flow_count > NETFLOW_MAX_COUNT ) + return false; + + data += sizeof(Netflow5Hdr); + precord = (const Netflow5RecordHdr *)data; + + // Invalid flow count + if ( (precord + header.flow_count) > end ) + return false; + + header.sys_uptime = ntohl(pheader->sys_uptime) / 1000; + header.unix_secs = ntohl(pheader->unix_secs); + header.unix_secs -= header.sys_uptime; + + // update total records + netflow_stats.records += header.flow_count; + + unsigned i; + for ( i=0; i < header.flow_count; i++, precord++ ) + { + + uint32_t first_packet = header.unix_secs + (ntohl(precord->flow_first)/1000); + uint32_t last_packet = header.unix_secs + (ntohl(precord->flow_last)/1000); + + // invalid flow time values + if ( first_packet > MAX_TIME or last_packet > MAX_TIME or first_packet > last_packet ) + return false; + + } + return true; +} + +static bool validate_netflow(const Packet* p) +{ + uint16_t size = p->dsize; + const unsigned char* data = p->data; + uint16_t version; + bool retval = false; + + // invalid packet size + if( size < sizeof(Netflow5Hdr)) + return false; + + version = ntohs(*((const uint16_t *)data)); + + if( version == 5 ) + { + retval = decode_netflow_v5(data, size); + if ( retval ) + { + ++netflow_stats.packets; + ++netflow_stats.version_5; + } + } + else if (version == 9) + { + retval = decode_netflow_v9(data, size); + if ( retval ) + { + ++netflow_stats.packets; + ++netflow_stats.version_9; + } + } + + return retval; +} + +// ----------------------------------------------------------------------------- +// non-static functions +// ----------------------------------------------------------------------------- + +void NetflowInspector::eval(Packet* p) +{ + // precondition - what we registered for + assert((p->is_udp() and p->dsize and p->data)); + + if ( ! validate_netflow(p) ) + ++netflow_stats.invalid_netflow_pkts; +} + +//------------------------------------------------------------------------- +// api stuff +//------------------------------------------------------------------------- + +static Module* netflow_mod_ctor() +{ return new NetflowModule; } + +static void netflow_mod_dtor(Module* m) +{ delete m; } + +static Inspector* netflow_ctor(Module* m) +{ return new NetflowInspector((NetflowModule*)m); } + +static void netflow_dtor(Inspector* p) +{ delete p; } + +static const InspectApi netflow_api = +{ + { + PT_INSPECTOR, + sizeof(InspectApi), + INSAPI_VERSION, + 0, + API_RESERVED, + API_OPTIONS, + NETFLOW_NAME, + NETFLOW_HELP, + netflow_mod_ctor, + netflow_mod_dtor + }, + IT_SERVICE, + PROTO_BIT__UDP, + nullptr, // buffers + "netflow", // service + nullptr, + nullptr, //pterm + nullptr, // pre-config tinit + nullptr, // pre-config tterm + netflow_ctor, + netflow_dtor, + nullptr, // ssn + nullptr // reset +}; + +#ifdef BUILDING_SO +SO_PUBLIC const BaseApi* snort_plugins[] = +#else +const BaseApi* sin_netflow[] = +#endif +{ + &netflow_api.base, + nullptr +}; diff --git a/src/service_inspectors/netflow/netflow.h b/src/service_inspectors/netflow/netflow.h new file mode 100644 index 000000000..29021fce0 --- /dev/null +++ b/src/service_inspectors/netflow/netflow.h @@ -0,0 +1,92 @@ +//-------------------------------------------------------------------------- +// 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. +//-------------------------------------------------------------------------- + +// netflow.h author Ron Dempster +// Shashikant Lad + +#ifndef NETFLOW_H +#define NETFLOW_H + +#include "flow/flow.h" + +namespace snort +{ +struct Packet; +} + +class NetflowModule; + +#define NETFLOW_MIN_COUNT 1 +#define NETFLOW_MAX_COUNT 256 +#define MAX_TIME 2145916799 + +struct Netflow5Hdr +{ + uint16_t version; // Netflow export format version number + uint16_t flow_count; // Number of flows exported in this packet(1-30) + uint32_t sys_uptime; // Current time in milliseconds since the export device booted + uint32_t unix_secs; // Current count of seconds since 0000 UTC 1970 + uint32_t unix_nsecs; // Residual nanoseconds since 0000 UTC 1970 + uint32_t flow_sequence; // Sequence counter of total flows seen + uint8_t engine_type; // Type of flow-switching engine + uint8_t engine_id; // Slot number of the flow-switching engine + uint16_t sampling_interval; // First two bits hold the sampling mode; remaining 14 bits hold value of sampling interval +}; + +struct Netflow5RecordHdr +{ + uint32_t flow_src_addr; // Source IP address + uint32_t flow_dst_addr; // Destination IP address + uint32_t next_hop_addr; // IP address of next hop router + uint16_t snmp_if_in; // SNMP index of input interface + uint16_t snmp_if_out; // SNMP index of output interface + uint32_t pkt_count; // Packets in the flow + uint32_t bytes_sent; // Total number of Layer 3 bytes in the packets of the flow + uint32_t flow_first; // System uptime at start of flow + uint32_t flow_last; // System uptime at the time the last packet of the flow was received + uint16_t src_port; // TCP/UDP source port number or equivalent + uint16_t dst_port; // TCP/UDP destination port number or equivalent + uint8_t pad1; // Unused (zero) bytes + uint8_t tcp_flags; // Cumulative OR of TCP flags + uint8_t flow_protocol; // IP protocol type (for example, TCP = 6; UDP = 17) + uint8_t tos; // IP type of service + uint16_t src_as; // Autonomous system number of the source, either origin or peer + uint16_t dst_as; // Autonomous system number of the destination, either origin or peer + uint8_t src_mask; // Source address prefix mask bits + uint8_t dst_mask; // Destination address prefix mask bits + uint16_t pad2; // Unused (zero) bytes +}; + +struct Netflow9Hdr +{ + uint16_t version; // The version of netflow records exported in this packet; + uint16_t flow_count; // Number of FlowSet records (both template and data) contained within this packet + uint32_t sys_uptime; // Time in milliseconds since this device was first booted + uint32_t unix_secs; // Seconds since 0000 Coordinated Universal Time (UTC) 1970 + uint32_t sequence_num; // Incremental sequence counter of all export packets sent by this export device; + uint32_t source_id; // A 32-bit value that identifies the Exporter Observation Domain +}; + +class NetflowInspector : public snort::Inspector +{ +public: + NetflowInspector(NetflowModule*) {} + void eval(snort::Packet*) override; +}; + +#endif diff --git a/src/service_inspectors/netflow/netflow_module.cc b/src/service_inspectors/netflow/netflow_module.cc new file mode 100644 index 000000000..2ca8ea959 --- /dev/null +++ b/src/service_inspectors/netflow/netflow_module.cc @@ -0,0 +1,63 @@ +//-------------------------------------------------------------------------- +// 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. +//-------------------------------------------------------------------------- + +// netflow_module.cc author Shashikant Lad + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "netflow_module.h" + +using namespace snort; + +// ----------------------------------------------------------------------------- +// static variables +// ----------------------------------------------------------------------------- + +static const Parameter netflow_params[] = +{ + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +static const PegInfo netflow_pegs[] = +{ + { CountType::SUM, "packets", "total packets processed" }, + { CountType::SUM, "records", "total records found in netflow data" }, + { CountType::SUM, "version_5", "count of netflow version 5 packets received" }, + { CountType::SUM, "version_9", "count of netflow version 9 packets received" }, + { CountType::SUM, "invalid_netflow_pkts", "count of invalid netflow packets" }, + { CountType::END, nullptr, nullptr}, +}; + +//------------------------------------------------------------------------- +// netflow module +//------------------------------------------------------------------------- + +NetflowModule::NetflowModule() : Module(NETFLOW_NAME, NETFLOW_HELP, netflow_params) +{ } + +PegCount* NetflowModule::get_counts() const +{ return (PegCount*)&netflow_stats; } + +const PegInfo* NetflowModule::get_pegs() const +{ return netflow_pegs; } + +ProfileStats* NetflowModule::get_profile() const +{ return &netflow_perf_stats; } + diff --git a/src/service_inspectors/netflow/netflow_module.h b/src/service_inspectors/netflow/netflow_module.h new file mode 100644 index 000000000..f3ee8dbeb --- /dev/null +++ b/src/service_inspectors/netflow/netflow_module.h @@ -0,0 +1,66 @@ +//-------------------------------------------------------------------------- +// 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. +//-------------------------------------------------------------------------- + +// netflow_module.h author Shashikant Lad + + +#ifndef NETFLOW_MODULE_H +#define NETFLOW_MODULE_H + +#include "framework/module.h" + +#define NETFLOW_NAME "netflow" +#define NETFLOW_HELP "netflow inspection" + +namespace snort +{ +struct SnortConfig; +} + +struct NetflowStats +{ + PegCount packets; + PegCount records; + PegCount version_5; + PegCount version_9; + PegCount invalid_netflow_pkts; +}; + +extern THREAD_LOCAL NetflowStats netflow_stats; +extern THREAD_LOCAL snort::ProfileStats netflow_perf_stats; + +class NetflowModule : public snort::Module +{ +public: + NetflowModule(); + + bool set(const char*, snort::Value&, snort::SnortConfig*) override + {return false; } + + const PegInfo* get_pegs() const override; + PegCount* get_counts() const override; + snort::ProfileStats* get_profile() const override; + + Usage get_usage() const override + { return INSPECT; } + + bool is_bindable() const override + { return true; } +}; + +#endif diff --git a/src/service_inspectors/service_inspectors.cc b/src/service_inspectors/service_inspectors.cc index 05d8c582a..8d2038c76 100644 --- a/src/service_inspectors/service_inspectors.cc +++ b/src/service_inspectors/service_inspectors.cc @@ -53,6 +53,7 @@ extern const BaseApi* sin_dce[]; extern const BaseApi* sin_dnp3[]; extern const BaseApi* sin_gtp[]; extern const BaseApi* sin_modbus[]; +extern const BaseApi* sin_netflow[]; extern const BaseApi* sin_s7commplus[]; #endif @@ -92,6 +93,7 @@ void load_service_inspectors() PluginManager::load_plugins(sin_dnp3); PluginManager::load_plugins(sin_gtp); PluginManager::load_plugins(sin_modbus); + PluginManager::load_plugins(sin_netflow); PluginManager::load_plugins(sin_s7commplus); #endif }