static const unsigned ALL_FLOWS = 3;
static const unsigned WDT_MASK = 7; // kick watchdog once for every 8 flows deleted
-const uint8_t MAX_PROTOCOLS = (uint8_t)to_utype(PktType::MAX) - 1; //removing PktType::NONE from count
+constexpr uint8_t MAX_PROTOCOLS = (uint8_t)to_utype(PktType::MAX) - 1; //removing PktType::NONE from count
+constexpr uint64_t max_skip_protos = (1ULL << MAX_PROTOCOLS) - 1;
//-------------------------------------------------------------------------
// FlowCache stuff
}
flow->reset(do_cleanup);
- prune_stats.update(reason);
+ prune_stats.update(reason, flow->key->pkt_type);
remove(flow);
return true;
}
void FlowCache::retire(Flow* flow)
{
flow->reset(true);
- prune_stats.update(PruneReason::NONE);
+ prune_stats.update(PruneReason::NONE, flow->key->pkt_type);
remove(flow);
}
assert(MAX_PROTOCOLS < 8 * sizeof(skip_protos));
- const uint64_t max_skip_protos = (1ULL << MAX_PROTOCOLS) - 1;
-
{
PacketTracerSuspend pt_susp;
while ( pruned <= cleanup_flows and
if( pruned > cleanup_flows )
break;
- if ( skip_protos & (1ULL << proto_idx) )
+ const uint64_t proto_mask = 1ULL << proto_idx;
+
+ if ( skip_protos & proto_mask )
continue;
auto flow = static_cast<Flow*>(hash_table->lru_first(proto_idx));
if ( !flow )
{
- skip_protos |= (1ULL << proto_idx);
+ skip_protos |= proto_mask;
continue;
}
or flow->is_suspended()
or flow->last_data_seen + config.pruning_timeout >= thetime )
{
- skip_protos |= (1ULL << proto_idx);
+ skip_protos |= proto_mask;
continue;
}
assert(MAX_PROTOCOLS < 8 * sizeof(skip_protos));
- const uint64_t max_skip_protos = (1ULL << MAX_PROTOCOLS) - 1;
{
PacketTracerSuspend pt_susp;
num_nodes = hash_table->get_num_nodes();
if ( num_nodes <= max_cap or num_nodes <= blocks )
break;
-
- if ( skip_protos & (1ULL << proto_idx) )
+
+ const uint64_t proto_mask = 1ULL << proto_idx;
+
+ if ( skip_protos & proto_mask )
continue;
auto flow = static_cast<Flow*>(hash_table->lru_first(proto_idx));
if ( !flow )
{
- skip_protos |= (1ULL << proto_idx);
+ skip_protos |= proto_mask;
continue;
}
assert(MAX_PROTOCOLS < 8 * sizeof(skip_protos));
- const uint64_t max_skip_protos = (1ULL << MAX_PROTOCOLS) - 1;
-
+
while ( pruned < config.prune_flows )
{
- if ( (skip_protos & (1ULL << proto)) or !prune_one(reason, do_cleanup, proto) )
+ const uint64_t proto_mask = 1ULL << proto;
+ if ( (skip_protos & proto_mask) or !prune_one(reason, do_cleanup, proto) )
{
- skip_protos |= (1ULL << proto);
+ skip_protos |= proto_mask;
if ( skip_protos == max_skip_protos )
break;
}
assert(MAX_PROTOCOLS < 8 * sizeof(skip_protos));
- const uint64_t max_skip_protos = (1ULL << MAX_PROTOCOLS) - 1;
{
PacketTracerSuspend pt_susp;
{
if( retired >= num_flows )
break;
+
+ const uint64_t proto_mask = 1ULL << proto_idx;
- if ( skip_protos & (1ULL << proto_idx) )
+ if ( skip_protos & proto_mask )
continue;
auto flow = static_cast<Flow*>(hash_table->lru_current(proto_idx));
flow = static_cast<Flow*>(hash_table->lru_first(proto_idx));
if ( !flow )
{
- skip_protos |= (1ULL << proto_idx);
+ skip_protos |= proto_mask;
continue;
}
{
if ( flow->expire_time > static_cast<uint64_t>(thetime) )
{
- skip_protos |= (1ULL << proto_idx);
+ skip_protos |= proto_mask;
continue;
}
}
else if ( flow->last_data_seen + flow->idle_timeout > thetime )
{
- skip_protos |= (1ULL << proto_idx);
+ skip_protos |= proto_mask;
continue;
}
assert(MAX_PROTOCOLS < 8 * sizeof(skip_protos));
- const uint64_t max_skip_protos = (1ULL << MAX_PROTOCOLS) - 1;
while ( num_to_delete and skip_protos != max_skip_protos and
undeletable < hash_table->get_num_nodes() )
if( num_to_delete == 0)
break;
- if ( skip_protos & (1ULL << proto_idx) )
+ const uint64_t proto_mask = 1ULL << proto_idx;
+
+ if ( skip_protos & proto_mask )
continue;
auto flow = static_cast<Flow*>(hash_table->lru_first(proto_idx));
if ( !flow )
{
- skip_protos |= (1ULL << proto_idx);
+ skip_protos |= proto_mask;
continue;
}
PegCount get_prunes(PruneReason reason) const
{ return prune_stats.get(reason); }
+ PegCount get_proto_prune_count(PruneReason reason, PktType type) const
+ { return prune_stats.get_proto_prune_count(reason,type); }
+
PegCount get_total_deletes() const
{ return delete_stats.get_total(); }
PegCount FlowControl::get_prunes(PruneReason reason) const
{ return cache->get_prunes(reason); }
+PegCount FlowControl::get_proto_prune_count(PruneReason reason, PktType type) const
+{ return cache->get_proto_prune_count(reason,type); }
+
PegCount FlowControl::get_total_deletes() const
{ return cache->get_total_deletes(); }
PegCount get_total_prunes() const;
PegCount get_prunes(PruneReason) const;
+ PegCount get_proto_prune_count(PruneReason, PktType) const;
PegCount get_total_deletes() const;
PegCount get_deletes(FlowDeleteState state) const;
void clear_counts();
MAX
};
+struct ProtoPruneStats
+{
+ using proto_t = std::underlying_type_t<PktType>;
+ PegCount proto_counts[static_cast<proto_t>(PktType::MAX)] { };
+
+ PegCount get_total() const
+ {
+ PegCount total = 0;
+ for ( proto_t i = 0; i < static_cast<proto_t>(PktType::MAX); ++i )
+ total += proto_counts[i];
+
+ return total;
+ }
+
+ PegCount& get(PktType type)
+ { return proto_counts[static_cast<proto_t>(type)]; }
+
+ const PegCount& get(PktType type) const
+ { return proto_counts[static_cast<proto_t>(type)]; }
+
+ void update(PktType type)
+ { ++get(type); }
+};
+
struct PruneStats
{
using reason_t = std::underlying_type<PruneReason>::type;
PegCount prunes[static_cast<reason_t>(PruneReason::MAX)] { };
+ ProtoPruneStats protoPruneStats[static_cast<reason_t>(PruneReason::MAX)] { };
PegCount get_total() const
{
const PegCount& get(PruneReason reason) const
{ return prunes[static_cast<reason_t>(reason)]; }
- void update(PruneReason reason)
- { ++get(reason); }
+ void update(PruneReason reason, PktType type = PktType::NONE)
+ {
+ ++get(reason);
+ protoPruneStats[static_cast<reason_t>(reason)].update(type);
+ }
+
+ PegCount& get_proto_prune_count(PruneReason reason, PktType type)
+ { return protoPruneStats[static_cast<reason_t>(reason)].get(type); }
+
+ const PegCount& get_proto_prune_count(PruneReason reason, PktType type) const
+ { return protoPruneStats[static_cast<reason_t>(reason)].get(type); }
+
+ PegCount get_proto_prune_count(PktType type) const
+ {
+ PegCount total = 0;
+ for ( reason_t i = 0; i < static_cast<reason_t>(PruneReason::NONE); ++i )
+ total += protoPruneStats[i].get(type);
+
+ return total;
+ }
};
enum class FlowDeleteState : uint8_t
delete cache;
}
+TEST(flow_prune, prune_counts)
+{
+ PruneStats stats;
+
+ // Simulate a few prunes for different reasons and protocol types
+ stats.update(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::IP);
+ stats.update(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::TCP);
+ stats.update(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::UDP);
+ stats.update(PruneReason::MEMCAP, PktType::ICMP);
+ stats.update(PruneReason::MEMCAP, PktType::USER);
+
+ // Check the total prunes
+ CHECK_EQUAL(5, stats.get_total());
+
+ // Check individual protocol prunes
+ CHECK_EQUAL(1, stats.get_proto_prune_count(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::IP));
+ CHECK_EQUAL(1, stats.get_proto_prune_count(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::TCP));
+ CHECK_EQUAL(1, stats.get_proto_prune_count(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::UDP));
+ CHECK_EQUAL(1, stats.get_proto_prune_count(PruneReason::MEMCAP, PktType::ICMP));
+ CHECK_EQUAL(1, stats.get_proto_prune_count(PruneReason::MEMCAP, PktType::USER));
+
+ // Check prunes for a specific protocol across all reasons
+ CHECK_EQUAL(1, stats.get_proto_prune_count(PktType::IP));
+ CHECK_EQUAL(1, stats.get_proto_prune_count(PktType::TCP));
+ CHECK_EQUAL(1, stats.get_proto_prune_count(PktType::UDP));
+ CHECK_EQUAL(1, stats.get_proto_prune_count(PktType::ICMP));
+ CHECK_EQUAL(1, stats.get_proto_prune_count(PktType::USER));
+
+ // Reset the counts
+ stats = PruneStats();
+
+ // Ensure that the counts have been reset
+ CHECK_EQUAL(0, stats.get_total());
+ CHECK_EQUAL(0, stats.get_proto_prune_count(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::IP));
+ CHECK_EQUAL(0, stats.get_proto_prune_count(PruneReason::MEMCAP, PktType::TCP));
+
+ // Update the same protocol and reason multiple times
+ stats.update(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::IP);
+ stats.update(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::IP);
+ stats.update(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::IP);
+
+ CHECK_EQUAL(3, stats.get_proto_prune_count(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::IP));
+}
+
int main(int argc, char** argv)
{
return CommandLineTestRunner::RunAllTests(argc, argv);
{ CountType::SUM, "reload_allowed_deletes", "number of allowed flows deleted by config reloads" },
{ CountType::SUM, "reload_blocked_deletes", "number of blocked flows deleted by config reloads" },
{ CountType::SUM, "reload_offloaded_deletes", "number of offloaded flows deleted by config reloads" },
+ { CountType::SUM, "ip_timeout_prunes", "number of IP flows pruned due to timeout" },
+ { CountType::SUM, "tcp_timeout_prunes", "number of TCP flows pruned due to timeout" },
+ { CountType::SUM, "udp_timeout_prunes", "number of UDP flows pruned due to timeout" },
+ { CountType::SUM, "icmp_timeout_prunes", "number of ICMP flows pruned due to timeout" },
+ { CountType::SUM, "user_timeout_prunes", "number of USER flows pruned due to timeout" },
+ { CountType::SUM, "file_timeout_prunes", "number of FILE flows pruned due to timeout" },
+ { CountType::SUM, "pdu_timeout_prunes", "number of PDU flows pruned due to timeout" },
+ { CountType::SUM, "ip_memcap_prunes", "number of IP flows pruned due to memcap" },
+ { CountType::SUM, "tcp_memcap_prunes", "number of TCP flows pruned due to memcap" },
+ { CountType::SUM, "udp_memcap_prunes", "number of UDP flows pruned due to memcap" },
+ { CountType::SUM, "icmp_memcap_prunes", "number of ICMP flows pruned due to memcap" },
+ { CountType::SUM, "user_memcap_prunes", "number of USER flows pruned due to memcap" },
+ { 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" },
// Keep the NOW stats at the bottom as it requires special sum_stats logic
{ CountType::NOW, "current_flows", "current number of flows in cache" },
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.reload_blocked_flow_deletes= flow_con->get_deletes(FlowDeleteState::BLOCKED);
+ stream_base_stats.ip_timeout_prunes = flow_con->get_proto_prune_count(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::IP);
+ stream_base_stats.tcp_timeout_prunes = flow_con->get_proto_prune_count(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::TCP);
+ stream_base_stats.udp_timeout_prunes = flow_con->get_proto_prune_count(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::UDP);
+ stream_base_stats.icmp_timeout_prunes = flow_con->get_proto_prune_count(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::ICMP);
+ stream_base_stats.user_timeout_prunes = flow_con->get_proto_prune_count(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::USER);
+ stream_base_stats.file_timeout_prunes = flow_con->get_proto_prune_count(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::FILE);
+ stream_base_stats.pdu_timeout_prunes = flow_con->get_proto_prune_count(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::PDU);
+ stream_base_stats.ip_memcap_prunes = flow_con->get_proto_prune_count(PruneReason::MEMCAP, PktType::IP);
+ stream_base_stats.tcp_memcap_prunes = flow_con->get_proto_prune_count(PruneReason::MEMCAP, PktType::TCP);
+ stream_base_stats.udp_memcap_prunes = flow_con->get_proto_prune_count(PruneReason::MEMCAP, PktType::UDP);
+ stream_base_stats.icmp_memcap_prunes = flow_con->get_proto_prune_count(PruneReason::MEMCAP, PktType::ICMP);
+ stream_base_stats.user_memcap_prunes = flow_con->get_proto_prune_count(PruneReason::MEMCAP, PktType::USER);
+ 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.current_flows = flow_con->get_num_flows();
stream_base_stats.uni_flows = flow_con->get_uni_flows();
PegCount reload_allowed_flow_deletes;
PegCount reload_blocked_flow_deletes;
PegCount reload_offloaded_flow_deletes;
+ PegCount ip_timeout_prunes;
+ PegCount tcp_timeout_prunes;
+ PegCount udp_timeout_prunes;
+ PegCount icmp_timeout_prunes;
+ PegCount user_timeout_prunes;
+ PegCount file_timeout_prunes;
+ PegCount pdu_timeout_prunes;
+ PegCount ip_memcap_prunes;
+ PegCount tcp_memcap_prunes;
+ PegCount udp_memcap_prunes;
+ PegCount icmp_memcap_prunes;
+ PegCount user_memcap_prunes;
+ PegCount file_memcap_prunes;
+ PegCount pdu_memcap_prunes;
// Keep the NOW stats at the bottom as it requires special sum_stats logic
PegCount current_flows;