]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #3385: netflow: Framework for netflow V5 and V9 events
authorMasud Hasan (mashasan) <mashasan@cisco.com>
Thu, 21 Apr 2022 19:20:11 +0000 (19:20 +0000)
committerMasud Hasan (mashasan) <mashasan@cisco.com>
Thu, 21 Apr 2022 19:20:11 +0000 (19:20 +0000)
Merge in SNORT/snort3 from ~MASHASAN/snort3:netflow_event2 to master

Squashed commit of the following:

commit 9320cdc01f9ace6dec235274b38e3115381e5a19
Author: Masud Hasan <mashasan@cisco.com>
Date:   Mon Apr 18 08:22:53 2022 -0400

    netflow: Framework for netflow V5 and V9 events

src/network_inspectors/rna/rna_event_handler.cc
src/network_inspectors/rna/rna_event_handler.h
src/network_inspectors/rna/rna_inspector.cc
src/network_inspectors/rna/rna_module.cc
src/network_inspectors/rna/rna_module.h
src/network_inspectors/rna/rna_pnd.cc
src/network_inspectors/rna/rna_pnd.h
src/pub_sub/CMakeLists.txt
src/pub_sub/netflow_event.h [new file with mode: 0644]
src/service_inspectors/netflow/CMakeLists.txt
src/service_inspectors/netflow/netflow.cc

index dd47d5e41ab05182a169f0e116d5b64da54ba1a6..cc7768ea15bf25a2fc0b1c48a0d801036448ecb9 100644 (file)
@@ -133,3 +133,10 @@ void RnaCPEOSInfoEventHandler::handle(DataEvent& event, Flow*)
     ++rna_stats.cpe_os;
     pnd.analyze_cpe_os_info(event);
 }
+
+void RnaNetflowEventHandler::handle(DataEvent& event, Flow*)
+{
+    Profile profile(rna_perf_stats);
+    ++rna_stats.netflow_record;
+    pnd.analyze_netflow(event);
+}
index 621abdd6935216b02cd1045d53a125402313e627..b0ce76d50109722ea1dddeed57dce81b200d9095 100644 (file)
@@ -162,4 +162,13 @@ private:
     RnaPnd& pnd;
 };
 
+class RnaNetflowEventHandler : public snort::DataHandler
+{
+public:
+    RnaNetflowEventHandler(RnaPnd& nd) : DataHandler(RNA_NAME), pnd(nd) { }
+    void handle(snort::DataEvent&, snort::Flow*) override;
+private:
+    RnaPnd& pnd;
+};
+
 #endif
index 15fb65cd8dafc6ee06a142753260fc3713c4f8e8..ac15390b7846b4973fb130dc402841067f14f103 100644 (file)
@@ -105,7 +105,9 @@ bool RnaInspector::configure(SnortConfig*)
     DataBus::subscribe_network( STREAM_TCP_SYN_EVENT, new RnaTcpSynEventHandler(*pnd) );
     DataBus::subscribe_network( STREAM_TCP_SYN_ACK_EVENT, new RnaTcpSynAckEventHandler(*pnd) );
     DataBus::subscribe_network( STREAM_TCP_MIDSTREAM_EVENT, new RnaTcpMidstreamEventHandler(*pnd) );
+
     DataBus::subscribe_network( CPE_OS_INFO_EVENT, new RnaCPEOSInfoEventHandler(*pnd) );
+    DataBus::subscribe_network( NETFLOW_EVENT, new RnaNetflowEventHandler(*pnd) );
 
     if (rna_conf && rna_conf->log_when_idle)
         DataBus::subscribe_network( THREAD_IDLE_EVENT, new RnaIdleEventHandler(*pnd) );
index 9561d0392504a05ff613947e34a048a19db6c0a7..78e863df5bc3e67e9fd465030b57567285b7eefc 100644 (file)
@@ -387,6 +387,7 @@ static const PegInfo rna_pegs[] =
     { CountType::SUM, "dhcp_data", "count of DHCP data events received" },
     { CountType::SUM, "dhcp_info", "count of new DHCP lease events received" },
     { CountType::SUM, "smb", "count of new SMB events received" },
+    { CountType::SUM, "netflow_record", "count of netflow record events received" },
     { CountType::END, nullptr, nullptr},
 };
 
index d5c98d80e0a1181e1f11abce0249b28a0e0e9838..8d327711dc6e40630495bbb06c32cb6d0c9af219 100644 (file)
@@ -54,6 +54,7 @@ struct RnaStats
     PegCount dhcp_data;
     PegCount dhcp_info;
     PegCount smb;
+    PegCount netflow_record;
 };
 
 extern THREAD_LOCAL RnaStats rna_stats;
index edf078170e305abe7a6f9d3529b3a42a50a4c9b7..3fe45744d4f42e3c49c58961c821f26fccefd610 100644 (file)
@@ -176,6 +176,26 @@ bool RnaPnd::analyze_cpe_os_info(snort::DataEvent& event)
     return true;
 }
 
+bool RnaPnd::analyze_netflow(snort::DataEvent& event)
+{
+    const Packet* p = event.get_packet();
+    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);
+
+    return true;
+}
+
 void RnaPnd::discover_network_icmp(const Packet* p)
 {
     discover_network(p, 0);
index b810cc46728c7b9394a26734667d996ec0a81675..326eadf6d3ea57bd49ca4eb56b25f2baa2d0e73e 100644 (file)
@@ -31,6 +31,7 @@
 #include "protocols/vlan.h"
 #include "pub_sub/appid_events.h"
 #include "pub_sub/dhcp_events.h"
+#include "pub_sub/netflow_event.h"
 #include "pub_sub/smb_events.h"
 #include "sfip/sf_ip.h"
 
@@ -131,6 +132,7 @@ public:
     void add_dhcp_info(snort::DataEvent&);
     void analyze_smb_fingerprint(snort::DataEvent&);
     bool analyze_cpe_os_info(snort::DataEvent&);
+    bool analyze_netflow(snort::DataEvent&);
 
     // generate change event for all hosts in the ip cache
     void generate_change_host_update();
index 648d736c2b600d092d22f8db63e76f17d221de69..d00049a668f9dc62af96ddc4dedcad6bbe459106 100644 (file)
@@ -12,6 +12,7 @@ set (PUB_SUB_INCLUDES
     finalize_packet_event.h
     http_events.h
     http_request_body_event.h
+    netflow_event.h
     opportunistic_tls_event.h
     sip_events.h
     smb_events.h
diff --git a/src/pub_sub/netflow_event.h b/src/pub_sub/netflow_event.h
new file mode 100644 (file)
index 0000000..dd44ebe
--- /dev/null
@@ -0,0 +1,50 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2022-2022 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_event.h author Masud Hasan <mashasan@cisco.com>
+
+#ifndef NETFLOW_EVENT_H
+#define NETFLOW_EVENT_H
+
+#include "framework/data_bus.h"
+#include "service_inspectors/netflow/netflow_headers.h"
+
+#define NETFLOW_EVENT "service_inspector.netflow"
+
+namespace snort
+{
+
+class NetflowEvent : public DataEvent
+{
+public:
+    NetflowEvent(const snort::Packet* p, const NetflowSessionRecord* rec)
+        : pkt(p), record(rec) { }
+
+    const Packet* get_packet() override
+    { return pkt; }
+
+    const NetflowSessionRecord* get_record()
+    { return record; }
+
+private:
+    const Packet* pkt;
+    const NetflowSessionRecord* record;
+};
+
+}
+
+#endif
index 1017d4057dc3f793b4800b70340709b55add6f4c..11e6c5fb2dcea48ad0da46ee0ac0b085d73af5a5 100644 (file)
@@ -12,4 +12,4 @@ if (STATIC_INSPECTORS)
 else (STATIC_INSPECTORS)
     add_dynamic_module(netflow inspectors ${FILE_LIST})
 
-endif (STATIC_INSPECTORS)
+endif (STATIC_INSPECTORS)
\ No newline at end of file
index fa23b689eec60596cf14e1e33a5cd1c81d5ebd44..de7e1cfb1f31259dc8eda658f73cda1ff514388c 100644 (file)
@@ -35,6 +35,7 @@
 #include "log/messages.h"
 #include "profiler/profiler.h"
 #include "protocols/packet.h"
+#include "pub_sub/netflow_event.h"
 #include "sfip/sf_ip.h"
 #include "src/utils/endian.h"
 #include "utils/util.h"
@@ -51,6 +52,19 @@ struct IpCompare
     { return a.less_than(b); }
 };
 
+// Used to ensure we fully populate the record; can't rely on the actual values being zero
+struct RecordStatus
+{
+    bool src = false;
+    bool dst = false;
+    bool first = false;
+    bool last = false;
+    bool src_tos = false;
+    bool dst_tos = false;
+    bool bytes_sent = false;
+    bool packets_sent = false;
+};
+
 // -----------------------------------------------------------------------------
 // static variables
 // -----------------------------------------------------------------------------
@@ -81,9 +95,7 @@ static bool filter_record(const NetflowRules* rules, const int zone,
         for( auto const& rule : rules->exclude )
         {
             if ( rule.filter_match(address, zone) )
-            {
                 return false;
-            }
         }
     }
 
@@ -92,18 +104,15 @@ static bool filter_record(const NetflowRules* rules, const int zone,
         for( auto const& rule : rules->include )
         {
             if ( rule.filter_match(address, zone) )
-            {
-                // check i.create_host i.create_service
-                // and publish events
                 return true;
-            }
         }
     }
     return false;
 }
 
 static bool version_9_record_update(const unsigned char* data, uint32_t unix_secs,
-        std::vector<Netflow9TemplateField>::iterator field, NetflowSessionRecord &record)
+    std::vector<Netflow9TemplateField>::iterator field, NetflowSessionRecord &record,
+    RecordStatus& record_status)
 {
 
     switch ( field->field_type )
@@ -144,6 +153,8 @@ static bool version_9_record_update(const unsigned char* data, uint32_t unix_sec
             // Invalid source IP address provided
             if ( record.initiator_ip.set((const uint32_t *)data, AF_INET) != SFIP_SUCCESS )
                 return false;
+
+            record_status.src = true;
             break;
 
         case NETFLOW_SRC_IPV6:
@@ -151,6 +162,8 @@ static bool version_9_record_update(const unsigned char* data, uint32_t unix_sec
             // Invalid source IP address provided
             if ( record.initiator_ip.set((const uint32_t *)data, AF_INET6) != SFIP_SUCCESS )
                 return false;
+
+            record_status.src = true;
             break;
 
         case NETFLOW_DST_PORT:
@@ -171,6 +184,8 @@ static bool version_9_record_update(const unsigned char* data, uint32_t unix_sec
             // Invalid destination IP address
             if ( record.responder_ip.set((const uint32_t *)data, AF_INET) != SFIP_SUCCESS )
                 return false;
+
+            record_status.dst = true;
             break;
 
         case NETFLOW_DST_IPV6:
@@ -178,6 +193,8 @@ static bool version_9_record_update(const unsigned char* data, uint32_t unix_sec
             // Invalid destination IP address
             if ( record.responder_ip.set((const uint32_t *)data, AF_INET6) != SFIP_SUCCESS )
                 return false;
+
+            record_status.dst = true;
             break;
 
         case NETFLOW_IPV4_NEXT_HOP:
@@ -202,6 +219,7 @@ static bool version_9_record_update(const unsigned char* data, uint32_t unix_sec
             if( record.last_pkt_second > MAX_TIME )
                 return false;
 
+            record_status.last = true;
             break;
 
         case NETFLOW_FIRST_PKT:
@@ -215,6 +233,7 @@ static bool version_9_record_update(const unsigned char* data, uint32_t unix_sec
             if( record.first_pkt_second > MAX_TIME )
                 return 0;
 
+            record_status.first = true;
             break;
 
         case NETFLOW_IN_BYTES:
@@ -228,6 +247,7 @@ static bool version_9_record_update(const unsigned char* data, uint32_t unix_sec
             else
                 return false;
 
+            record_status.bytes_sent = true;
             break;
 
         case NETFLOW_IN_PKTS:
@@ -241,6 +261,7 @@ static bool version_9_record_update(const unsigned char* data, uint32_t unix_sec
             else
                 return false;
 
+            record_status.packets_sent = true;
             break;
 
         case NETFLOW_SRC_TOS:
@@ -249,6 +270,7 @@ static bool version_9_record_update(const unsigned char* data, uint32_t unix_sec
                 return false;
 
             record.nf_src_tos = (uint8_t)*data;
+            record_status.src_tos = true;
             break;
 
         case NETFLOW_DST_TOS:
@@ -257,6 +279,7 @@ static bool version_9_record_update(const unsigned char* data, uint32_t unix_sec
                 return false;
 
             record.nf_dst_tos = (uint8_t)*data;
+            record_status.dst_tos = true;
             break;
 
         case NETFLOW_SNMP_IN:
@@ -397,6 +420,7 @@ static bool decode_netflow_v9(const unsigned char* data, uint16_t size,
             {
 
                 NetflowSessionRecord record = {};
+                RecordStatus record_status;
                 bool bad_field = false;
 
                 for ( auto t_field = tf.begin(); t_field != tf.end(); ++t_field )
@@ -407,7 +431,8 @@ static bool decode_netflow_v9(const unsigned char* data, uint16_t size,
 
                     if ( !bad_field )
                     {
-                        bool status = version_9_record_update(data, header.unix_secs, t_field, record);
+                        bool status = version_9_record_update(data, header.unix_secs,
+                            t_field, record, record_status);
 
                         if ( !status )
                             bad_field = true;
@@ -430,7 +455,24 @@ static bool decode_netflow_v9(const unsigned char* data, uint16_t size,
                     continue;
                 }
 
-                // create flow event here
+                if ( record_status.bytes_sent and record_status.packets_sent and
+                    record_status.src and record_status.dst and record_status.first and
+                    record_status.last and record.first_pkt_second <= record.last_pkt_second )
+                {
+                    if ( record_status.src_tos )
+                    {
+                        if ( !record_status.dst_tos )
+                            record.nf_dst_tos = record.nf_src_tos;
+                    }
+                    else if ( record_status.dst_tos )
+                    {
+                        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);
+                }
 
                 // check if record exists
                 auto result = netflow_cache->find(record.initiator_ip);
@@ -625,6 +667,9 @@ static bool decode_netflow_v5(const unsigned char* data, uint16_t size,
         if ( result.second )
             ++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);
     }
     return true;
 }
@@ -896,9 +941,8 @@ NetflowInspector::~NetflowInspector()
 
 void NetflowInspector::eval(Packet* p)
 {
-    // precondition - what we registered for
-    assert((p->is_udp() and p->dsize and p->data));
-    assert(netflow_cache);
+    if ( !p->is_udp() or !p->dsize or !p->data or !netflow_cache )
+        return;
 
     auto d = config->device_rule_map.find(*p->ptrs.ip_api.get_src());