--- /dev/null
+//--------------------------------------------------------------------------
+// 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
#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"
{ 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
// -----------------------------------------------------------------------------
for( auto const& rule : rules->exclude )
{
if ( rule.filter_match(address, zone) )
- {
return false;
- }
}
}
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 )
// 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:
// 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:
// 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:
// 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:
if( record.last_pkt_second > MAX_TIME )
return false;
+ record_status.last = true;
break;
case NETFLOW_FIRST_PKT:
if( record.first_pkt_second > MAX_TIME )
return 0;
+ record_status.first = true;
break;
case NETFLOW_IN_BYTES:
else
return false;
+ record_status.bytes_sent = true;
break;
case NETFLOW_IN_PKTS:
else
return false;
+ record_status.packets_sent = true;
break;
case NETFLOW_SRC_TOS:
return false;
record.nf_src_tos = (uint8_t)*data;
+ record_status.src_tos = true;
break;
case NETFLOW_DST_TOS:
return false;
record.nf_dst_tos = (uint8_t)*data;
+ record_status.dst_tos = true;
break;
case NETFLOW_SNMP_IN:
{
NetflowSessionRecord record = {};
+ RecordStatus record_status;
bool bad_field = false;
for ( auto t_field = tf.begin(); t_field != tf.end(); ++t_field )
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;
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);
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;
}
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());