]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #2455 in SNORT/snort3 from ~SHASLAD/snort3:netflow_as_inspector...
authorSteve Chew (stechew) <stechew@cisco.com>
Tue, 8 Sep 2020 20:51:37 +0000 (20:51 +0000)
committerSteve Chew (stechew) <stechew@cisco.com>
Tue, 8 Sep 2020 20:51:37 +0000 (20:51 +0000)
Squashed commit of the following:

commit 08bc5e0c873d97912e9986c5f3ee57ab5eeb0831
Author: Shashi Lad <shaslad@cisco.com>
Date:   Fri Aug 28 08:18:29 2020 -0400

    netflow: introducing netflow as inspector

lua/snort.lua
lua/snort_defaults.lua
src/service_inspectors/CMakeLists.txt
src/service_inspectors/netflow/CMakeLists.txt [new file with mode: 0644]
src/service_inspectors/netflow/netflow.cc [new file with mode: 0644]
src/service_inspectors/netflow/netflow.h [new file with mode: 0644]
src/service_inspectors/netflow/netflow_module.cc [new file with mode: 0644]
src/service_inspectors/netflow/netflow_module.h [new file with mode: 0644]
src/service_inspectors/service_inspectors.cc

index e4eb1fa8e1e965fc6e1cd6a01cd9097b100f1d61..e3a23af2daf6bf4002649acb3798be202adc746c 100644 (file)
@@ -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' } },
index f956986991b98ad5e40e8705e75e95072f9d71f5..e3df6eaa7ed5f62e6c988b6e06dd518552d291ce 100644 (file)
@@ -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)
index f1a831eca3a3e4ea34118bc45c932a5cedcf7993..cc465800dcbcc315ed59fee695011160d29e5d89 100644 (file)
@@ -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)
         $<TARGET_OBJECTS:gtp_inspect>
         $<TARGET_OBJECTS:imap>
         $<TARGET_OBJECTS:modbus>
+        $<TARGET_OBJECTS:netflow>
         $<TARGET_OBJECTS:pop>
         $<TARGET_OBJECTS:rpc_decode>
+        $<TARGET_OBJECTS:s7commplus>
         $<TARGET_OBJECTS:smtp>
         $<TARGET_OBJECTS:ssh>
         $<TARGET_OBJECTS:wizard>
-        $<TARGET_OBJECTS:s7commplus>
     )
 endif()
 
diff --git a/src/service_inspectors/netflow/CMakeLists.txt b/src/service_inspectors/netflow/CMakeLists.txt
new file mode 100644 (file)
index 0000000..fe9cb90
--- /dev/null
@@ -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 (file)
index 0000000..0d9db15
--- /dev/null
@@ -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 <rdempste@cisco.com>
+//                   Shashikant Lad <shaslad@cisco.com>
+
+#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 (file)
index 0000000..29021fc
--- /dev/null
@@ -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 <rdempste@cisco.com>
+//                  Shashikant Lad <shaslad@cisco.com>
+
+#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 (file)
index 0000000..2ca8ea9
--- /dev/null
@@ -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 <shaslad@cisco.com>
+
+#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 (file)
index 0000000..f3ee8db
--- /dev/null
@@ -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 <shaslad@cisco.com>
+
+
+#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
index 05d8c582ad48e830613c2621dc369a7f845b8ff2..8d2038c76a6d178252c6f4e9ca79475f9c4a498b 100644 (file)
@@ -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
 }