From: ARUNKUMAR KAYAMBU -X (akayambu - XORIANT CORPORATION at Cisco) Date: Tue, 10 Sep 2024 14:13:00 +0000 (+0000) Subject: Pull request #4429: stream: fix to dump all flows X-Git-Tag: 3.3.7.0~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4f6bb1a7ec9fd73693be64770ad67222ad0a9e64;p=thirdparty%2Fsnort3.git Pull request #4429: stream: fix to dump all flows Merge in SNORT/snort3 from ~AKAYAMBU/snort3:dump_all_flows to master Squashed commit of the following: commit 5bdf40420c947eeb8490cab14a0632feded8905c Author: Arunkumar Kayambu Date: Tue Aug 20 12:21:14 2024 -0400 stream: fix to dump all flows --- diff --git a/src/flow/flow_cache.cc b/src/flow/flow_cache.cc index 8283acb43..b00b949ba 100644 --- a/src/flow/flow_cache.cc +++ b/src/flow/flow_cache.cc @@ -886,39 +886,39 @@ bool FlowCache::dump_flows(std::fstream& stream, unsigned count, const FilterFlo struct timeval now; packet_gettimeofday(&now); unsigned i; + bool has_more_flows = false; + Flow* walk_flow = nullptr; for(uint8_t proto_id = to_utype(PktType::NONE)+1; proto_id <= to_utype(PktType::ICMP); proto_id++) { if (first) { - Flow* walk_flow = static_cast(hash_table->get_walk_user_data(proto_id)); + walk_flow = static_cast(hash_table->get_walk_user_data(proto_id)); if (!walk_flow) { //Return only if all the protocol caches are processed. if (proto_id < to_utype(PktType::ICMP)) continue; - - return true; + return !has_more_flows; } walk_flow->dump_code = code; bool matched_filter = filter_flows(*walk_flow, ffc); if (matched_filter) output_flow(stream, *walk_flow, now); i = 1; - } else i = 0; while (i < count) { - Flow* walk_flow = static_cast(hash_table->get_next_walk_user_data(proto_id)); + walk_flow = static_cast(hash_table->get_next_walk_user_data(proto_id)); if (!walk_flow ) { //Return only if all the protocol caches are processed. if (proto_id < to_utype(PktType::ICMP)) break; - return true; + return !has_more_flows; } if (walk_flow->dump_code != code) { @@ -933,6 +933,8 @@ bool FlowCache::dump_flows(std::fstream& stream, unsigned count, const FilterFlo LogMessage("dump_flows skipping already dumped flow\n"); #endif } + if(walk_flow) // we have output 'count' flows, but the protocol cache still has more flows + has_more_flows = true; } return false; } diff --git a/src/flow/flow_cache.h b/src/flow/flow_cache.h index f45f32cc2..7756a16af 100644 --- a/src/flow/flow_cache.h +++ b/src/flow/flow_cache.h @@ -81,7 +81,7 @@ class FlowCache { public: FlowCache(const FlowCacheConfig&); - ~FlowCache(); + virtual ~FlowCache(); FlowCache(const FlowCache&) = delete; FlowCache& operator=(const FlowCache&) = delete; @@ -134,6 +134,9 @@ public: const FlowCacheConfig& get_flow_cache_config() const { return config; } + virtual bool filter_flows(const snort::Flow&, const FilterFlowCriteria&) const; + virtual void output_flow(std::fstream&, const snort::Flow&, const struct timeval&) const; + unsigned get_flows_allocated() const; size_t uni_flows_size() const; @@ -150,8 +153,6 @@ private: unsigned delete_active_flows(unsigned mode, unsigned num_to_delete, unsigned &deleted); static std::string timeout_to_str(time_t); bool is_ip_match(const snort::SfIp& flow_ip, const snort::SfIp& filter_ip, const snort::SfIp& subnet) const; - bool filter_flows(const snort::Flow&, const FilterFlowCriteria&) const; - void output_flow(std::fstream&, const snort::Flow&, const struct timeval&) const; private: uint8_t timeout_idx; diff --git a/src/flow/test/flow_cache_test.cc b/src/flow/test/flow_cache_test.cc index 4ebbd223f..2341b5d32 100644 --- a/src/flow/test/flow_cache_test.cc +++ b/src/flow/test/flow_cache_test.cc @@ -131,6 +131,15 @@ int ExpectCache::add_flow(const Packet*, PktType, IpProtocol, const SfIp*, uint1 unsigned int get_random_seed() { return 3193; } +class DummyCache : public FlowCache +{ + 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; }; +}; + TEST_GROUP(flow_prune) { }; // No flows in the flow cache, pruning should not happen @@ -384,6 +393,105 @@ TEST(flow_prune, prune_counts) CHECK_EQUAL(3, stats.get_proto_prune_count(PruneReason::IDLE_PROTOCOL_TIMEOUT, PktType::IP)); } +TEST_GROUP(dump_flows) { }; + +TEST(dump_flows, dump_flows_with_all_empty_caches) +{ + FlowCacheConfig fcg; + FilterFlowCriteria ffc; + std::fstream dump_stream; + DummyCache *cache = new DummyCache(fcg); + CHECK(cache->dump_flows(dump_stream, 100, ffc, true, 1 ) == true); + CHECK(cache->get_flows_allocated() == 0); + delete cache; +} + +TEST(dump_flows, dump_flows_with_one_tcp_flow) +{ + FlowCacheConfig fcg; + fcg.max_flows = 5; + FilterFlowCriteria ffc; + std::fstream dump_stream; + DummyCache *cache = new DummyCache(fcg); + + FlowKey flow_key; + flow_key.port_l = 1; + flow_key.pkt_type = PktType::TCP; + cache->allocate(&flow_key); + CHECK(cache->dump_flows(dump_stream, 100, ffc, true, 1 ) == true); + CHECK (cache->get_count() == 1); + + cache->purge(); + CHECK(cache->get_flows_allocated() == 0); + delete cache; +} + +TEST(dump_flows, dump_flows_with_102_tcp_flows) +{ + FlowCacheConfig fcg; + fcg.max_flows = 500; + FilterFlowCriteria ffc; + std::fstream dump_stream; + DummyCache *cache = new DummyCache(fcg); + int port = 1; + + for ( unsigned i = 0; i < 102; i++ ) + { + FlowKey flow_key; + flow_key.port_l = port++; + flow_key.pkt_type = PktType::TCP; + cache->allocate(&flow_key); + } + CHECK (cache->get_count() == 102); + //since we only dump 100 flows at a time. The first call will return false + //second time when it is called , it dumps the remaining 2 flows and returns true + CHECK(cache->dump_flows(dump_stream, 100, ffc, true, 1 ) == false); + CHECK(cache->dump_flows(dump_stream, 100, ffc, false, 1 ) == true); + + cache->purge(); + CHECK(cache->get_flows_allocated() == 0); + CHECK (cache->get_count() == 0); + delete cache; +} + +TEST(dump_flows, dump_flows_with_102_tcp_flows_and_202_udp_flows) +{ + FlowCacheConfig fcg; + fcg.max_flows = 500; + FilterFlowCriteria ffc; + std::fstream dump_stream; + DummyCache *cache = new DummyCache(fcg); + int port = 1; + + for ( unsigned i = 0; i < 102; i++ ) + { + FlowKey flow_key; + flow_key.port_l = port++; + flow_key.pkt_type = PktType::TCP; + cache->allocate(&flow_key); + } + + for ( unsigned i = 0; i < 202; i++ ) + { + FlowKey flow_key; + flow_key.port_l = port++; + flow_key.pkt_type = PktType::UDP; + cache->allocate(&flow_key); + } + + CHECK (cache->get_count() == 304); + //since we only dump 100 flows at a time. The first 2 calls will return false + //third time when it is called , it dumps the remaining 2 UDP flows and returns true + CHECK(cache->dump_flows(dump_stream, 100, ffc, true, 1 ) == false); + CHECK(cache->dump_flows(dump_stream, 100, ffc, false, 1 ) == false); + CHECK(cache->dump_flows(dump_stream, 100, ffc, false, 1 ) == true); + + cache->purge(); + CHECK(cache->get_flows_allocated() == 0); + CHECK (cache->get_count() == 0); + delete cache; +} + int main(int argc, char** argv) { return CommandLineTestRunner::RunAllTests(argc, argv); diff --git a/src/flow/test/flow_control_test.cc b/src/flow/test/flow_control_test.cc index a0037668d..234001d24 100644 --- a/src/flow/test/flow_control_test.cc +++ b/src/flow/test/flow_control_test.cc @@ -75,6 +75,8 @@ void Flow::init(PktType) { } const SnortConfig* SnortConfig::get_conf() { return nullptr; } void FlowCache::unlink_uni(Flow*) { } bool FlowCache::dump_flows(std::fstream&, unsigned, const FilterFlowCriteria&, bool, uint8_t) 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 Flow::set_mpls_layer_per_dir(Packet*) { }