flow_key.h
flow_stash.h
ha.h
+ prune_stats.h
session.h
stream_flow.h
)
{
if ( flow_con->move_to_allowlist(this) )
{
- PacketTracer::log("Flow: flow has been moved to allowlist cache\n");
+ if ( PacketTracer::is_active() )
+ PacketTracer::log("Flow: flow has been moved to allowlist cache\n");
return true;
}
}
}
}
+ if ( UNLIKELY(PacketTracer::is_active()) )
+ log_flow_release(flow, reason);
+
uint8_t in_allowlist = flow->flags.in_allowlist;
flow->reset(do_cleanup);
prune_stats.update(reason, ( in_allowlist ? static_cast<PktType>(allowlist_lru_index) : flow->key->pkt_type ));
return true;
}
-void FlowCache::output_flow(std::fstream& stream, const Flow& flow, const struct timeval& now) const
+template<typename StreamType>
+void FlowCache::output_flow(StreamType& stream, const Flow& flow, const struct timeval& now) const
{
char src_ip[INET6_ADDRSTRLEN];
src_ip[0] = 0;
}
#endif
+inline void FlowCache::log_flow_release(const snort::Flow* flow, PruneReason reason) const
+{
+ PacketTracerUnsuspend pt_unsusp;
+
+ std::stringstream temp_stream;
+ struct timeval now;
+ packet_gettimeofday(&now);
+ output_flow(temp_stream, *flow, now);
+ std::string flow_info = temp_stream.str();
+
+ PacketTracer::log("Flow: Releasing flow due to %s: %s", prune_reason_to_string(reason), flow_info.c_str());
+}
bool move_to_allowlist(snort::Flow* f);
virtual bool filter_flows(const snort::Flow&, const FilterFlowCriteria&) const;
- virtual void output_flow(std::fstream&, const snort::Flow&, const struct timeval&) const;
+ template<typename StreamType>
+ void output_flow(StreamType&, const snort::Flow&, const struct timeval&) const;
unsigned get_flows_allocated() const;
empty_lru_masks |= lru_mask;
}
+ inline void log_flow_release(const snort::Flow* flow, PruneReason reason) const;
+
private:
uint8_t timeout_idx;
static const unsigned cleanup_flows = 1;
#include <cstdint>
#include <type_traits>
+#include <array>
#include "framework/counts.h"
MEMCAP,
HA,
STALE,
- IDLE_MAX_FLOWS,
- IDLE_PROTOCOL_TIMEOUT,
+ IDLE_MAX_FLOWS,
+ IDLE_PROTOCOL_TIMEOUT,
+ STREAM_CLOSED,
+ END_OF_FLOW,
NONE,
MAX
};
+inline const char* prune_reason_to_string(PruneReason reason)
+{
+ static constexpr const char* names[] = {
+ "EXCESS", "UNI", "MEMCAP", "HA", "STALE",
+ "IDLE_MAX_FLOWS", "IDLE_PROTOCOL_TIMEOUT",
+ "STREAM_CLOSED", "EOF", "NONE"
+ };
+
+ auto idx = static_cast<uint8_t>(reason);
+ return (idx < static_cast<uint8_t>(PruneReason::MAX)) ? names[idx] : "UNKNOWN";
+}
+
struct LRUPruneStats
{
using lru_t = std::underlying_type_t<LRUType>;
public:
DummyCache(const FlowCacheConfig& cfg) : FlowCache(cfg) {}
~DummyCache() = default;
- void output_flow(std::fstream& stream, const Flow& flow, const struct timeval& now) const override { (void)stream, (void)flow, (void)now; };
bool filter_flows(const Flow& flow, const FilterFlowCriteria& ffc) const override { (void)flow; (void)ffc; return true; };
};
public:
DummyCacheWithFilter(const FlowCacheConfig& cfg) : FlowCache(cfg) {}
~DummyCacheWithFilter() = default;
- void output_flow(std::fstream& stream, const Flow& flow, const struct timeval& now) const override { (void)stream, (void)flow, (void)now; };
};
TEST_GROUP(flow_prune) { };
void FlowCache::unlink_uni(Flow*) { }
bool FlowCache::dump_flows(std::fstream&, unsigned, const FilterFlowCriteria&, bool, uint8_t) const { return false; }
bool FlowCache::dump_flows_summary(FlowsSummary&, const FilterFlowCriteria&) const { return false; }
-void FlowCache::output_flow(std::fstream&, const Flow&, const struct timeval& ) const { }
bool FlowCache::filter_flows(const Flow&, const FilterFlowCriteria&) const { return true; };
void Flow::set_client_initiate(Packet*) { }
void Flow::set_direction(Packet*) { }
void PacketTracer::reset(bool) { }
void PacketTracer::pause() { }
void PacketTracer::unpause() { }
+bool PacketTracer::is_paused() { return true; }
bool PacketTracer::is_active() { return false; }
namespace layer
void PacketTracer::thread_term()
{
+ if ( s_pkt_trace and s_pkt_trace->buff_len > 0
+ and ( s_pkt_trace->user_enabled or s_pkt_trace->shell_enabled ) )
+ {
+ if ( !snort::SnortConfig::get_conf()->use_log_buffered() )
+ LogMessage(s_pkt_trace->log_fh, "%s\n", s_pkt_trace->buffer);
+ else
+ {
+ if ( s_pkt_trace->log_fh and s_pkt_trace->log_fh != stdout )
+ {
+ fprintf(s_pkt_trace->log_fh, "%.*s\n", s_pkt_trace->buff_len, s_pkt_trace->buffer);
+ fflush(s_pkt_trace->log_fh);
+ }
+ else
+ {
+ BatchedLogger::BatchedLogManager::log(s_pkt_trace->log_fh, SnortConfig::log_syslog(),
+ s_pkt_trace->buffer, s_pkt_trace->buff_len);
+ }
+ }
+ }
+
BatchedLogger::BatchedLogManager::flush_thread_buffers();
delete s_pkt_trace;
s_pkt_trace = nullptr;
{ PacketTracer::unpause(); }
};
+struct PacketTracerUnsuspend
+{
+ unsigned saved_pause_count = 0;
+
+ PacketTracerUnsuspend()
+ {
+ while (PacketTracer::is_paused())
+ {
+ PacketTracer::unpause();
+ saved_pause_count++;
+ }
+ }
+
+ ~PacketTracerUnsuspend() noexcept
+ {
+ for (unsigned i = 0; i < saved_pause_count; i++)
+ PacketTracer::pause();
+ }
+};
+
}
#endif
{ CountType::SUM, "memcap_prunes", "sessions pruned due to memcap" },
{ CountType::SUM, "ha_prunes", "sessions pruned by high availability sync" },
{ CountType::SUM, "stale_prunes", "sessions pruned due to stale connection" },
+ { CountType::SUM, "closed_prunes", "sessions pruned due to stream closed" },
{ CountType::SUM, "expected_flows", "total expected flows created within snort" },
{ CountType::SUM, "expected_realized", "number of expected flows realized" },
{ CountType::SUM, "expected_pruned", "number of expected flows pruned" },
{ CountType::SUM, "file_memcap_prunes", "number of FILE flows pruned due to memcap" },
{ CountType::SUM, "pdu_memcap_prunes", "number of PDU flows pruned due to memcap" },
{ CountType::SUM, "allowlist_memcap_prunes", "number of allowlist flows pruned due to memcap" },
+ { CountType::SUM, "ip_eof_prunes", "number of IP flows pruned due to EOF" },
+ { CountType::SUM, "tcp_eof_prunes", "number of TCP flows pruned due to EOF" },
+ { CountType::SUM, "udp_eof_prunes", "number of UDP flows pruned due to EOF" },
+ { CountType::SUM, "icmp_eof_prunes", "number of ICMP flows pruned due to EOF" },
+ { CountType::SUM, "user_eof_prunes", "number of USER flows pruned due to EOF" },
+ { CountType::SUM, "file_eof_prunes", "number of FILE flows pruned due to EOF" },
+ { CountType::SUM, "pdu_eof_prunes", "number of PDU flows pruned due to EOF" },
+ { CountType::SUM, "allowlist_eof_prunes", "number of allowlist flows pruned due to EOF" },
{ CountType::SUM, "excess_to_allowlist", "number of flows moved to the allowlist due to excess" },
// Keep the NOW stats at the bottom as it requires special sum_stats logic
stream_base_stats.memcap_prunes = flow_con->get_prunes(PruneReason::MEMCAP);
stream_base_stats.ha_prunes = flow_con->get_prunes(PruneReason::HA);
stream_base_stats.stale_prunes = flow_con->get_prunes(PruneReason::STALE);
+ stream_base_stats.closed_prunes = flow_con->get_prunes(PruneReason::STREAM_CLOSED);
stream_base_stats.reload_freelist_flow_deletes = flow_con->get_deletes(FlowDeleteState::FREELIST);
stream_base_stats.reload_allowed_flow_deletes = flow_con->get_deletes(FlowDeleteState::ALLOWED);
stream_base_stats.reload_offloaded_flow_deletes= flow_con->get_deletes(FlowDeleteState::OFFLOADED);
stream_base_stats.file_memcap_prunes = flow_con->get_proto_prune_count(PruneReason::MEMCAP, PktType::FILE);
stream_base_stats.pdu_memcap_prunes = flow_con->get_proto_prune_count(PruneReason::MEMCAP, PktType::PDU);
stream_base_stats.allowlist_memcap_prunes = flow_con->get_proto_prune_count(PruneReason::MEMCAP, static_cast<PktType>(allowlist_lru_index));
+ stream_base_stats.ip_eof_prunes = flow_con->get_proto_prune_count(PruneReason::END_OF_FLOW, PktType::IP);
+ stream_base_stats.tcp_eof_prunes = flow_con->get_proto_prune_count(PruneReason::END_OF_FLOW, PktType::TCP);
+ stream_base_stats.udp_eof_prunes = flow_con->get_proto_prune_count(PruneReason::END_OF_FLOW, PktType::UDP);
+ stream_base_stats.icmp_eof_prunes = flow_con->get_proto_prune_count(PruneReason::END_OF_FLOW, PktType::ICMP);
+ stream_base_stats.user_eof_prunes = flow_con->get_proto_prune_count(PruneReason::END_OF_FLOW, PktType::USER);
+ stream_base_stats.file_eof_prunes = flow_con->get_proto_prune_count(PruneReason::END_OF_FLOW, PktType::FILE);
+ stream_base_stats.pdu_eof_prunes = flow_con->get_proto_prune_count(PruneReason::END_OF_FLOW, PktType::PDU);
+ stream_base_stats.allowlist_eof_prunes = flow_con->get_proto_prune_count(PruneReason::END_OF_FLOW, static_cast<PktType>(allowlist_lru_index));
stream_base_stats.excess_to_allowlist = flow_con->get_excess_to_allowlist_count();
stream_base_stats.allowlist_flows = flow_con->get_allowlist_flow_count();
PegCount memcap_prunes;
PegCount ha_prunes;
PegCount stale_prunes;
+ PegCount closed_prunes;
PegCount expected_flows;
PegCount expected_realized;
PegCount expected_pruned;
PegCount file_memcap_prunes;
PegCount pdu_memcap_prunes;
PegCount allowlist_memcap_prunes;
+ PegCount ip_eof_prunes;
+ PegCount tcp_eof_prunes;
+ PegCount udp_eof_prunes;
+ PegCount icmp_eof_prunes;
+ PegCount user_eof_prunes;
+ PegCount file_eof_prunes;
+ PegCount pdu_eof_prunes;
+ PegCount allowlist_eof_prunes;
PegCount excess_to_allowlist;
// Keep the NOW stats at the bottom as it requires special sum_stats logic
void Stream::delete_flow(const FlowKey* key)
{ flow_con->release_flow(key); }
-void Stream::delete_flow(Flow* flow)
-{ flow_con->release_flow(flow, PruneReason::NONE); }
+void Stream::delete_flow(Flow* flow, PruneReason reason)
+{ flow_con->release_flow(flow, reason); }
//-------------------------------------------------------------------------
// key foo
// eventually all onloads will occur and delete will be called
if ( not flow->is_suspended() )
{
- flow_con->release_flow(flow, PruneReason::NONE);
+ flow_con->release_flow(flow, PruneReason::STREAM_CLOSED);
return;
}
}
#include <daq_common.h>
#include "flow/flow.h"
+#include "flow/prune_stats.h"
#include "main/policy.h"
#include "protocols/packet.h"
#include "time/packet_time.h"
// the resources allocated to that flow to the free list.
static void delete_flow(const FlowKey*);
static void delete_flow(Flow*);
+ static void delete_flow(Flow*, PruneReason reason = PruneReason::NONE);
// Examines the source and destination ip addresses and ports to determine if the
// packet is from the client or server side of the flow and sets bits in the
#endif
+#ifdef __GNUC__
+#define UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define UNLIKELY(x) (x)
+#endif
+
+
#define TIMEBUF_SIZE 27
#define SECONDS_PER_DAY 86400 /* number of seconds in a day */