]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #1539 in SNORT/snort3 from ~RUCOMBS/snort3:memory_misery to master
authorRuss Combs (rucombs) <rucombs@cisco.com>
Sun, 10 Mar 2019 00:47:51 +0000 (19:47 -0500)
committerRuss Combs (rucombs) <rucombs@cisco.com>
Sun, 10 Mar 2019 00:47:51 +0000 (19:47 -0500)
Squashed commit of the following:

commit 29f8a2c133f0aa5726c2d7a53f164bc840c069a5
Author: russ <rucombs@cisco.com>
Date:   Sat Mar 9 19:45:19 2019 -0500

    build: fix override warning

commit 322dac9242dc6b1a0c1c1cfd0289899fdca9e158
Author: russ <rucombs@cisco.com>
Date:   Sat Mar 9 12:52:28 2019 -0500

    memory: add configurable L3/L4 specific weights for better estimation against cap

commit 30826c6c6d425a24aedd49d7b1375580a449b027
Author: russ <rucombs@cisco.com>
Date:   Fri Mar 8 21:26:05 2019 -0500

    stream_tcp: patch around premature application of delayed actions that yoink the seglist

commit a6b3a0f313ad2f6911cc0167cca0e0179aedba4f
Author: russ <rucombs@cisco.com>
Date:   Thu Mar 7 03:37:08 2019 -0500

    stream: purge remaining flows before shutdown counts

commit a22cb207099c52b9bb0b3af7c2b2c45798f15213
Author: russ <rucombs@cisco.com>
Date:   Thu Mar 7 03:12:09 2019 -0500

    stream_tcp: implement reserve seglist

commit fcad14fd2875f9b5f3c6c792882617529dea67f3
Author: russ <rucombs@cisco.com>
Date:   Thu Mar 7 01:53:07 2019 -0500

    stream_tcp: consolidate segment node and data

commit 38d2075e51809c564057dde52b9ea47913b29f9d
Author: russ <rucombs@cisco.com>
Date:   Wed Mar 6 16:28:26 2019 -0500

    memory: require subclass implementation of FlowData::size_of()

commit 5cd42d4fa5a3f30d1e2f1a0008134403998e8779
Author: russ <rucombs@cisco.com>
Date:   Wed Mar 6 16:05:38 2019 -0500

    memory: add size_of to various FlowData subclasses

commit 3b82fc157d789d993eb8d7d1c77c05898956da6c
Author: russ <rucombs@cisco.com>
Date:   Wed Mar 6 16:04:45 2019 -0500

    memory: apply fudge factor to tracking to better align with RSS

commit 2deb67a92ddc1f8143d3e3768d74f3d99f7ba137
Author: russ <rucombs@cisco.com>
Date:   Wed Mar 6 18:44:56 2019 -0500

    stream_tcp: fixup allocation tracking for overlapped segmenets

commit a9539d086d3956f2346b2bb04137b71b427464c6
Author: russ <rucombs@cisco.com>
Date:   Wed Mar 6 13:17:42 2019 -0500

    memory: track session allocations

commit e18575a5e608ea598b41175f10923b1061ea65ad
Author: russ <rucombs@cisco.com>
Date:   Wed Mar 6 10:53:10 2019 -0500

    memory: basic flow pruning

commit 5da1c556989cd267c2718a4068b0e12edb7aea20
Author: russ <rucombs@cisco.com>
Date:   Wed Mar 6 10:52:38 2019 -0500

    memory: refactor stats

commit e6bfcd81fe52f018148b7c53ca3ce0520eadf532
Author: russ <rucombs@cisco.com>
Date:   Tue Mar 5 20:04:48 2019 -0500

    memory: basic flow data allocation tracking

commit 77f6ae93f8c5eb8f19df3b9d17736bb2655dcebc
Author: russ <rucombs@cisco.com>
Date:   Tue Mar 5 10:20:02 2019 -0500

    Revert "Merge pull request #1524 in SNORT/snort3 from ~PSHINDE2/snort3:memory_tracker_simplified to master"

    This reverts commit 0bb8323f6aae61501aaaaa6a9e904448ddf35ceb.
    Done to restore tracking of total allocations.
    Will fix differently.

commit feb9b3707d1fc8c9b13fe236eb55433944704c7c
Author: russ <rucombs@cisco.com>
Date:   Tue Mar 5 10:19:04 2019 -0500

    memory: initial preemptive pruning based on flow data

commit 118e0b21c8d2f4bc42d287bba7867d0ede1e728e
Author: russ <rucombs@cisco.com>
Date:   Mon Mar 4 18:01:42 2019 -0500

    memory: remove overloading manager to make way for new implementation

56 files changed:
src/detection/regex_offload.h
src/file_api/file_flows.h
src/flow/flow.cc
src/flow/flow.h
src/flow/flow_cache.cc
src/flow/flow_cache.h
src/flow/flow_config.h
src/main/snort.cc
src/memory/CMakeLists.txt
src/memory/memory_allocator.cc [deleted file]
src/memory/memory_allocator.h [deleted file]
src/memory/memory_cap.cc
src/memory/memory_cap.h
src/memory/memory_config.h
src/memory/memory_manager.cc [deleted file]
src/memory/memory_module.cc
src/memory/memory_module.h
src/network_inspectors/appid/appid_session.h
src/service_inspectors/dce_rpc/dce_smb.h
src/service_inspectors/dce_rpc/dce_tcp.h
src/service_inspectors/dce_rpc/dce_udp.h
src/service_inspectors/dnp3/dnp3.h
src/service_inspectors/dns/dns.h
src/service_inspectors/ftp_telnet/ftpp_si.h
src/service_inspectors/gtp/gtp_inspect.h
src/service_inspectors/http2_inspect/http2_flow_data.h
src/service_inspectors/http_inspect/http_flow_data.h
src/service_inspectors/imap/imap.h
src/service_inspectors/modbus/modbus.h
src/service_inspectors/pop/pop.h
src/service_inspectors/rpc_decode/rpc_decode.cc
src/service_inspectors/sip/sip.h
src/service_inspectors/smtp/smtp.h
src/service_inspectors/ssh/ssh.h
src/service_inspectors/ssl/ssl_inspector.h
src/stream/base/stream_base.cc
src/stream/base/stream_module.cc
src/stream/file/file_session.cc
src/stream/file/file_session.h
src/stream/icmp/icmp_session.cc
src/stream/icmp/icmp_session.h
src/stream/ip/ip_defrag.cc
src/stream/ip/ip_session.cc
src/stream/ip/ip_session.h
src/stream/libtcp/tcp_stream_session.cc
src/stream/libtcp/tcp_stream_session.h
src/stream/tcp/stream_tcp.cc
src/stream/tcp/tcp_reassembler.cc
src/stream/tcp/tcp_segment_node.cc
src/stream/tcp/tcp_segment_node.h
src/stream/tcp/tcp_session.cc
src/stream/tcp/tcp_session.h
src/stream/udp/udp_session.cc
src/stream/udp/udp_session.h
src/stream/user/user_session.cc
src/stream/user/user_session.h

index 0351f8ffb730c540cabf866cc43401e164337eb9..fdc5faf8b83b2c35a43c0189fb33b1eab10df428 100644 (file)
@@ -81,7 +81,7 @@ class ThreadRegexOffload : public RegexOffload
 {
 public:
     ThreadRegexOffload(unsigned max);
-    ~ThreadRegexOffload();
+    ~ThreadRegexOffload() override;
 
     void stop() override;
 
index f70a20977b81816f23001914fc037890a8e99fe4..91df59d58630b8b2170911fed023dfa3de8dd352 100644 (file)
@@ -92,6 +92,9 @@ public:
     void set_file_policy(FilePolicyBase* fp) { file_policy = fp; }
     FilePolicyBase* get_file_policy() { return file_policy; }
 
+    size_t size_of() override
+    { return sizeof(*this); }
+
 private:
     void init_file_context(FileDirection, FileContext*);
     FileContext* find_main_file_context(FilePosition, FileDirection, size_t id = 0);
index c30287cc6a94a4adbdcec6b7f40f517f5cc9adb1..1b28fd04afaea284d4922bed726d511759782577 100644 (file)
@@ -28,6 +28,7 @@
 #include "flow/session.h"
 #include "framework/data_bus.h"
 #include "ips_options/ips_flowbits.h"
+#include "memory/memory_cap.h"
 #include "protocols/packet.h"
 #include "sfip/sf_ip.h"
 #include "utils/bitop.h"
@@ -52,8 +53,27 @@ FlowData::~FlowData()
 {
     if ( handler )
         handler->rem_ref();
+
+    assert(mem_in_use == 0);
+}
+
+void FlowData::update_allocations(size_t n)
+{
+    memory::MemoryCap::free_space(n);
+    memory::MemoryCap::update_allocations(n);
+    mem_in_use += n;
+}
+
+void FlowData::update_deallocations(size_t n)
+{
+    assert(mem_in_use >= n);
+    memory::MemoryCap::update_deallocations(n);
+    mem_in_use -= n;
 }
 
+size_t FlowData::size_of()
+{ return 1024; }  // FIXIT-H remove this default impl
+
 Flow::Flow()
 {
     memset(this, 0, sizeof(*this));
@@ -76,10 +96,13 @@ void Flow::init(PktType type)
 
 void Flow::term()
 {
-    if ( session )
-        delete session;
+    if ( !session )
+        return;
 
-    free_flow_data();
+    delete session;
+    session = nullptr;
+
+    assert(!flow_data);
 
     if ( mpls_client.length )
         delete[] mpls_client.start;
@@ -227,6 +250,12 @@ int Flow::set_flow_data(FlowData* fd)
         flow_data->prev = fd;
 
     flow_data = fd;
+
+    // this is after actual allocation so we can't prune beforehand
+    // but if we are that close to the edge we are in trouble anyway
+    // large allocations can be accounted for directly
+    fd->update_allocations(fd->size_of());
+
     return 0;
 }
 
@@ -262,6 +291,7 @@ void Flow::free_flow_data(FlowData* fd)
         fd->prev->next = fd->next;
         fd->next->prev = fd->prev;
     }
+    fd->update_deallocations(fd->size_of());
     delete fd;
 }
 
@@ -281,6 +311,7 @@ void Flow::free_flow_data()
     {
         FlowData* tmp = fd;
         fd = fd->next;
+        tmp->update_deallocations(tmp->size_of());
         delete tmp;
     }
     flow_data = nullptr;
index bd931507a8827c9973af4b9cf478f214320ba3fe..5e28e1f3dd6a531e85957b7356b19f04cb930611 100644 (file)
@@ -115,6 +115,14 @@ public:
     static unsigned create_flow_data_id()
     { return ++flow_data_id; }
 
+    void update_allocations(size_t);
+    void update_deallocations(size_t);
+
+    // return fixed size (could be an approx avg)
+    // this must be fixed for life of flow data instance
+    // track significant supplemental allocations with the above updaters
+    virtual size_t size_of() = 0;
+
     virtual void handle_expected(Packet*) { }
     virtual void handle_retransmit(Packet*) { }
     virtual void handle_eof(Packet*) { }
@@ -126,6 +134,7 @@ public:  // FIXIT-L privatize
 private:
     static unsigned flow_data_id;
     Inspector* handler;
+    size_t mem_in_use = 0;
     unsigned id;
 };
 
index 0696259c17cbd556da751d2635d99fd18e05dfb1..d1c80030352516354dce3f42d34009f9ee83e1c0 100644 (file)
@@ -28,6 +28,7 @@
 #include "hash/zhash.h"
 #include "helpers/flag_context.h"
 #include "ips_options/ips_flowbits.h"
+#include "memory/memory_cap.h"
 #include "packet_io/active.h"
 #include "time/packet_time.h"
 #include "utils/stats.h"
@@ -61,9 +62,6 @@ FlowCache::FlowCache (const FlowConfig& cfg) : config(cfg)
 
 FlowCache::~FlowCache ()
 {
-    while ( Flow* flow = (Flow*)hash_table->pop() )
-        flow->term();
-
     delete uni_head;
     delete uni_tail;
 
@@ -142,6 +140,7 @@ Flow* FlowCache::get(const FlowKey* key)
         link_uni(flow);
     }
 
+    memory::MemoryCap::update_allocations(config.cap_weight);
     flow->last_data_seen = timestamp;
 
     return flow;
@@ -159,9 +158,18 @@ int FlowCache::remove(Flow* flow)
     if ( flow->next )
         unlink_uni(flow);
 
+    memory::MemoryCap::update_deallocations(config.cap_weight);
     return hash_table->remove(flow->key);
 }
 
+int FlowCache::retire(Flow* flow)
+{
+    flow->reset(true);
+    flow->term();
+    prune_stats.update(PruneReason::NONE);
+    return remove(flow);
+}
+
 unsigned FlowCache::prune_stale(uint32_t thetime, const Flow* save_me)
 {
     ActiveSuspendContext act_susp;
@@ -335,10 +343,13 @@ unsigned FlowCache::purge()
 
     while ( auto flow = static_cast<Flow*>(hash_table->first()) )
     {
-        release(flow, PruneReason::NONE);
+        retire(flow);
         ++retired;
     }
 
+    while ( Flow* flow = (Flow*)hash_table->pop() )
+        flow->term();
+
     return retired;
 }
 
index a10ad9a8ab025309de566624542c5565838a731d..cff941974634677bdc7d958090d3038263025cf2 100644 (file)
@@ -79,6 +79,7 @@ public:
 private:
     void link_uni(snort::Flow*);
     int remove(snort::Flow*);
+    int retire(snort::Flow*);
 
 private:
     static const unsigned cleanup_flows = 1;
index a1972db3e8f05f5974ff022d4941293a51e4e1e9..95ab46bdbc4d33d5f77a8a64f8ade2f31ccc73e8 100644 (file)
@@ -28,6 +28,7 @@ struct FlowConfig
     unsigned max_sessions = 0;
     unsigned pruning_timeout = 0;
     unsigned nominal_timeout = 0;
+    unsigned cap_weight = 0;
 };
 
 #endif
index 141ab657066634c9d62aab40b9e5a68799397573..2cb2acf6ecce7196931ea0f5b5cbcb7d6d1024a0 100644 (file)
@@ -429,6 +429,8 @@ void Snort::term()
     already_exiting = true;
     initializing = false;  // just in case we cut out early
 
+    memory::MemoryCap::print();
+
     term_signals();
     IpsManager::global_term(SnortConfig::get_conf());
     SFAT_Cleanup();
index e2de794f2d9c80feb90dc782d468032e5f460012..182861556b9a3699790989ae23a5cd8fa722c42b 100644 (file)
@@ -1,12 +1,9 @@
 set ( MEMORY_SOURCES
-    memory_allocator.cc
-    memory_allocator.h
     memory_cap.cc
     memory_cap.h
     memory_module.cc
     memory_module.h
     memory_config.h
-    memory_manager.cc
     prune_handler.cc
     prune_handler.h
 )
diff --git a/src/memory/memory_allocator.cc b/src/memory/memory_allocator.cc
deleted file mode 100644 (file)
index a91ec1a..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-//--------------------------------------------------------------------------
-// Copyright (C) 2016-2019 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.
-//--------------------------------------------------------------------------
-
-// memory_allocator.cc author Joel Cornett <jocornet@cisco.com>
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "memory_allocator.h"
-
-#include <cstdlib>
-
-namespace memory
-{
-
-// FIXIT-L (de)allocate() could be inlined if defined in memory_manager.cc
-void* MemoryAllocator::allocate(size_t n)
-{ return malloc(n); }
-
-void MemoryAllocator::deallocate(void* p)
-{ free(p); }
-
-} // namespace memory
diff --git a/src/memory/memory_allocator.h b/src/memory/memory_allocator.h
deleted file mode 100644 (file)
index 71a867c..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-//--------------------------------------------------------------------------
-// Copyright (C) 2016-2019 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.
-//--------------------------------------------------------------------------
-
-// memory_allocator.h author Joel Cornett <jocornet@cisco.com>
-
-#ifndef MEMORY_ALLOCATOR_H
-#define MEMORY_ALLOCATOR_H
-
-#include <cstddef>
-
-namespace memory
-{
-
-struct MemoryAllocator
-{
-    static void* allocate(size_t);
-    static void deallocate(void*);
-};
-
-} // namespace memory
-
-#endif
index bb00c15d22b751331ef0c99ad33ab3b219b79043..524b269430451fb266e6c27b941a9def973b3da1 100644 (file)
@@ -50,37 +50,21 @@ namespace
 
 struct Tracker
 {
-    size_t used_memory = 0;
-
-    uint64_t allocations = 0;
-    uint64_t deallocations = 0;
-
     void allocate(size_t n)
-    {
-        used_memory += n;
-        ++allocations;
-    }
+    { mem_stats.allocated += n; ++mem_stats.allocations; }
 
     void deallocate(size_t n)
-    {
-        // FIXIT-H If memory is allocated in one thread and passed to another thread,
-        // when it is freed, we may have (used_memory < memory_to_be_freed)
-        // Following assertion will fail.
-        //assert(n <= used_memory);
-
-        if(n > used_memory)
-            return;
-
-        used_memory -= n;
-        ++deallocations;
-    }
+    { mem_stats.deallocated += n; ++mem_stats.deallocations; }
 
     size_t used() const
     {
-        return used_memory;
+        if ( mem_stats.allocated < mem_stats.deallocated )
+        {
+            assert(false);
+            return 0;
+        }
+        return mem_stats.allocated - mem_stats.deallocated;
     }
-
-    constexpr Tracker() = default;
 };
 
 static THREAD_LOCAL Tracker s_tracker;
@@ -99,24 +83,23 @@ inline bool free_space(size_t requested, size_t cap, Tracker& trk, Handler& hand
 
     auto used = trk.used();
 
-    if ( requested <= cap - used )
+    if ( used + requested <= cap )
         return true;
 
-    while ( requested > cap - used )
+    ++mem_stats.reap_attempts;
+
+    while ( used + requested > cap )
     {
         handler();
-
-        // check if the handler freed any space
         auto tmp = trk.used();
+
         if ( tmp >= used )
         {
-            // nope
+            ++mem_stats.reap_failures;
             return false;
         }
-
         used = tmp;
     }
-
     return true;
 }
 
@@ -144,18 +127,27 @@ bool MemoryCap::free_space(size_t n)
     if ( !thread_cap )
         return true;
 
-    const auto& config = *snort::SnortConfig::get_conf()->memory;
-    return memory::free_space(n, thread_cap, s_tracker, prune_handler) || config.soft;
+    return memory::free_space(n, thread_cap, s_tracker, prune_handler);
 }
 
+static size_t fudge_it(size_t n)
+{ return ((n >> 7) + 1) << 7; }
+
 void MemoryCap::update_allocations(size_t n)
 {
+    size_t k = n;
+    n = fudge_it(n);
+    mem_stats.total_fudge += (n - k);
     s_tracker.allocate(n);
+    auto in_use = mem_stats.allocated - mem_stats.deallocated;
+    if ( in_use > mem_stats.max_in_use )
+        mem_stats.max_in_use = in_use;
     mp_active_context.update_allocs(n);
 }
 
 void MemoryCap::update_deallocations(size_t n)
 {
+    n = fudge_it(n);
     s_tracker.deallocate(n);
     mp_active_context.update_deallocs(n);
 }
@@ -214,21 +206,20 @@ void MemoryCap::print()
 
     const MemoryConfig& config = *snort::SnortConfig::get_conf()->memory;
 
-    if ( snort::SnortConfig::log_verbose() or s_tracker.allocations )
+    if ( snort::SnortConfig::log_verbose() or mem_stats.allocations )
         LogLabel("memory (heap)");
 
     if ( snort::SnortConfig::log_verbose() )
     {
         LogMessage("    global cap: %zu\n", config.cap);
         LogMessage("    global preemptive threshold percent: %u\n", config.threshold);
-        LogMessage("    cap type: %s\n", config.soft? "soft" : "hard");
     }
 
-    if ( s_tracker.allocations )
+    if ( mem_stats.allocations )
     {
         LogMessage("    main thread usage: %zu\n", s_tracker.used());
-        LogMessage("    allocations: %" PRIu64 "\n", s_tracker.allocations);
-        LogMessage("    deallocations: %" PRIu64 "\n", s_tracker.deallocations);
+        LogMessage("    allocations: %" PRIu64 "\n", mem_stats.allocations);
+        LogMessage("    deallocations: %" PRIu64 "\n", mem_stats.deallocations);
         LogMessage("    thread cap: %zu\n", thread_cap);
         LogMessage("    preemptive threshold: %zu\n", preemptive_threshold);
     }
@@ -317,15 +308,6 @@ TEST_CASE( "memory cap free space", "[memory]" )
         CHECK_FALSE( memory::free_space(1, 1024, tracker, handler) );
         CHECK( handler.calls == 1 );
     }
-
-    SECTION( "free_space() does not rollover" )
-    {
-        MockTracker tracker { SIZE_MAX };
-        HandlerSpy handler { -5, tracker };
-        CHECK( memory::free_space(1, SIZE_MAX, tracker, handler) );
-        CHECK( handler.calls == 1 );
-    }
-
 }
 
 #endif
index 80f5da1ead88adee0b992aa8fca1f261e69b2555..58f3e4327f26fa87bcaa3efc533395304dd75608 100644 (file)
@@ -23,6 +23,8 @@
 
 #include <cstddef>
 
+#include "main/thread.h"
+
 namespace memory
 {
 
index 3e361191aa35f8b3c03074c16bf53ff9e2697a10..907752156ed2a26f147e6c33bbc9a4c7cef810cc 100644 (file)
@@ -27,7 +27,6 @@ struct MemoryConfig
 {
     size_t cap = 0;
     unsigned threshold = 0;
-    bool soft = false;
 
     constexpr MemoryConfig() = default;
 };
diff --git a/src/memory/memory_manager.cc b/src/memory/memory_manager.cc
deleted file mode 100644 (file)
index 339167a..0000000
+++ /dev/null
@@ -1,460 +0,0 @@
-//--------------------------------------------------------------------------
-// Copyright (C) 2016-2019 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.
-//--------------------------------------------------------------------------
-
-// memory_manager.cc author Joel Cornett <jocornet@cisco.com>
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <cassert>
-#include <new>
-
-#include "main/thread.h"
-
-#include "memory_allocator.h"
-#include "memory_cap.h"
-
-#ifdef UNIT_TEST
-#include "catch/snort_catch.h"
-#endif
-
-namespace memory
-{
-
-// -----------------------------------------------------------------------------
-// metadata
-// -----------------------------------------------------------------------------
-
-// NOTE: This structure must be aligned to max_align_t as long as we are prepending
-//       it to memory allocations so that the returned memory is also aligned.
-struct alignas(max_align_t) Metadata
-{
-#if defined(REG_TEST) || defined(UNIT_TEST)
-    static constexpr size_t SANITY_CHECK_VALUE = 0xabcdef;
-    size_t sanity;
-#endif
-
-    // number of requested bytes
-    size_t payload_size;
-
-    // total number of bytes allocated, including Metadata header
-    size_t total_size() const;
-    void* payload_offset();
-
-#if defined(REG_TEST) || defined(UNIT_TEST)
-    bool valid() const
-    { return sanity == SANITY_CHECK_VALUE; }
-#endif
-
-    Metadata(size_t = 0);
-
-    static size_t calculate_total_size(size_t);
-
-    template<typename Allocator>
-    static Metadata* create(size_t);
-
-    static Metadata* extract(void*);
-};
-
-inline size_t Metadata::total_size() const
-{ return calculate_total_size(payload_size); }
-
-inline void* Metadata::payload_offset()
-{ return this + 1; }
-
-inline Metadata::Metadata(size_t n) :
-#if defined(REG_TEST) || defined(UNIT_TEST)
-    sanity(SANITY_CHECK_VALUE),
-#endif
-    payload_size(n)
-{ }
-
-inline size_t Metadata::calculate_total_size(size_t n)
-{ return sizeof(Metadata) + n; }
-
-template<typename Allocator>
-Metadata* Metadata::create(size_t n)
-{
-    auto meta =
-        static_cast<Metadata*>(Allocator::allocate(calculate_total_size(n)));
-
-    if ( !meta )
-        return nullptr;
-
-    // Trigger metadata ctor
-    *meta = Metadata(n);
-
-#if defined(REG_TEST) || defined(UNIT_TEST)
-    assert(meta->valid());
-#endif
-
-    return meta;
-}
-
-Metadata* Metadata::extract(void* p)
-{
-    assert(p);
-
-    auto meta = static_cast<Metadata*>(p) - 1;
-
-#if defined(REG_TEST) || defined(UNIT_TEST)
-    assert(meta->valid());
-#endif
-
-    return meta;
-}
-
-// -----------------------------------------------------------------------------
-// the meat
-// -----------------------------------------------------------------------------
-
-class ReentryContext
-{
-public:
-    ReentryContext(bool& flag) :
-        already_entered(flag), flag(flag)
-    { flag = true; }
-
-    ~ReentryContext()
-    { flag = false; }
-
-    bool is_reentry() const
-    { return already_entered; }
-
-private:
-    const bool already_entered;
-    bool& flag;
-};
-
-template<typename Allocator = MemoryAllocator, typename Cap = MemoryCap>
-struct Interface
-{
-    static void* allocate(size_t);
-    static void deallocate(void*);
-
-    static THREAD_LOCAL bool in_allocation_call;
-};
-
-template<typename Allocator, typename Cap>
-void* Interface<Allocator, Cap>::allocate(size_t n)
-{
-    // prevent allocation reentry
-    ReentryContext reentry_context(in_allocation_call);
-    assert(!reentry_context.is_reentry());
-
-    if ( !Cap::free_space(Metadata::calculate_total_size(n)) )
-        return nullptr;
-
-    auto meta = Metadata::create<Allocator>(n);
-    if ( !meta )
-        return nullptr;
-
-    Cap::update_allocations(meta->total_size());
-    return meta->payload_offset();
-}
-
-template<typename Allocator, typename Cap>
-void Interface<Allocator, Cap>::deallocate(void* p)
-{
-    if ( !p )
-        return;
-
-    auto meta = Metadata::extract(p);
-    assert(meta);
-
-    Cap::update_deallocations(meta->total_size());
-    Allocator::deallocate(meta);
-}
-
-template<typename Allocator, typename Cap>
-THREAD_LOCAL bool Interface<Allocator, Cap>::in_allocation_call = false;
-
-} //namespace memory
-
-// -----------------------------------------------------------------------------
-// new /delete replacements
-// -----------------------------------------------------------------------------
-
-// these don't have to be visible to operate as replacements
-
-#ifndef NO_MEM_MGR
-void* operator new(size_t n)
-{
-    auto p = memory::Interface<>::allocate(n);
-    if ( !p )
-        throw std::bad_alloc();
-
-    return p;
-}
-
-void* operator new(size_t n, const std::nothrow_t&) noexcept
-{ return memory::Interface<>::allocate(n); }
-
-void operator delete(void* p) noexcept
-{ memory::Interface<>::deallocate(p); }
-
-void operator delete(void* p, const std::nothrow_t&) noexcept
-{ ::operator delete(p); }
-
-void* operator new[](size_t n)
-{ return ::operator new(n); }
-
-void* operator new[](size_t n, const std::nothrow_t& tag) noexcept
-{ return ::operator new(n, tag); }
-
-void operator delete[](void* p) noexcept
-{ ::operator delete(p); }
-
-void operator delete[](void* p, const std::nothrow_t&) noexcept
-{ ::operator delete[](p); }
-
-// C++14 delete operators are a special case and must be explicitly exported
-// since we're compiling as C++11 but must capture these for external libraries
-void operator delete(void* p, size_t) noexcept;
-SO_PUBLIC void operator delete(void* p, size_t) noexcept
-{ ::operator delete(p); }
-
-void operator delete[](void* p, size_t) noexcept;
-SO_PUBLIC void operator delete[](void* p, size_t) noexcept
-{ ::operator delete[](p); }
-#endif
-
-// -----------------------------------------------------------------------------
-// unit tests
-// -----------------------------------------------------------------------------
-
-#ifdef UNIT_TEST
-
-namespace t_memory
-{
-
-struct AllocatorSpy
-{
-    static void* allocate(size_t n)
-    { allocate_called = true; allocate_arg = n; return pool; }
-
-    static void deallocate(void* p)
-    { deallocate_called = true; deallocate_arg = p; }
-
-    static void reset()
-    {
-        pool = nullptr;
-        allocate_called = false;
-        allocate_arg = 0;
-        deallocate_called = false;
-        deallocate_arg = nullptr;
-    }
-
-    static void* pool;
-    static bool allocate_called;
-    static size_t allocate_arg;
-    static bool deallocate_called;
-    static void* deallocate_arg;
-};
-
-void* AllocatorSpy::pool = nullptr;
-bool AllocatorSpy::allocate_called = false;
-size_t AllocatorSpy::allocate_arg = 0;
-bool AllocatorSpy::deallocate_called = false;
-void* AllocatorSpy::deallocate_arg = nullptr;
-
-struct CapSpy
-{
-    static bool free_space(size_t n)
-    {
-        free_space_called = true;
-        free_space_arg = n;
-        return free_space_result;
-    }
-
-    static void update_allocations(size_t n)
-    {
-        update_allocations_called = true;
-        update_allocations_arg = n;
-    }
-
-    static void update_deallocations(size_t n)
-    {
-        update_deallocations_called = true;
-        update_deallocations_arg = n;
-    }
-
-    static void reset()
-    {
-        free_space_called = false;
-        free_space_arg = 0;
-        free_space_result = false;
-
-        update_allocations_called = false;
-        update_allocations_arg = 0;
-
-        update_deallocations_called = false;
-        update_deallocations_arg = 0;
-    }
-
-    static bool free_space_called;
-    static size_t free_space_arg;
-    static bool free_space_result;
-
-    static bool update_allocations_called;
-    static size_t update_allocations_arg;
-
-    static bool update_deallocations_called;
-    static size_t update_deallocations_arg;
-};
-
-bool CapSpy::free_space_called = false;
-size_t CapSpy::free_space_arg = 0;
-bool CapSpy::free_space_result = false;
-
-bool CapSpy::update_allocations_called = false;
-size_t CapSpy::update_allocations_arg = 0;
-
-bool CapSpy::update_deallocations_called = false;
-size_t CapSpy::update_deallocations_arg = 0;
-
-} // namespace t_memory
-
-TEST_CASE( "memory metadata", "[memory]" )
-{
-    using namespace t_memory;
-
-    AllocatorSpy::reset();
-    constexpr size_t n = 1;
-    char pool[sizeof(memory::Metadata) + n];
-
-    SECTION( "create" )
-    {
-        AllocatorSpy::pool = pool;
-
-        auto meta = memory::Metadata::create<AllocatorSpy>(n);
-
-        CHECK( (void*)meta == (void*)pool );
-        CHECK( meta->valid() );
-        CHECK( meta->payload_size == n );
-    }
-
-    SECTION( "extract" )
-    {
-        auto meta_pool = reinterpret_cast<memory::Metadata*>(pool);
-        meta_pool[0] = memory::Metadata(n);
-
-        void* p = &meta_pool[1];
-
-        auto meta = memory::Metadata::extract(p);
-
-        CHECK( (void*)meta == (void*)pool );
-        CHECK( meta->payload_offset() == p );
-    }
-}
-
-TEST_CASE( "memory manager interface", "[memory]" )
-{
-    using namespace t_memory;
-
-    AllocatorSpy::reset();
-    CapSpy::reset();
-
-    constexpr size_t n = 1;
-    char pool[sizeof(memory::Metadata) + n];
-
-    using Interface = memory::Interface<AllocatorSpy, CapSpy>;
-
-    SECTION( "allocation" )
-    {
-        SECTION( "free space failure" )
-        {
-            auto p = Interface::allocate(n);
-
-            CHECK( p == nullptr );
-
-            CHECK( CapSpy::free_space_called );
-            CHECK( CapSpy::free_space_arg == memory::Metadata::calculate_total_size(n) );
-
-            CHECK_FALSE( AllocatorSpy::allocate_called );
-            CHECK_FALSE( CapSpy::update_allocations_called );
-        }
-
-        SECTION( "allocation failure" )
-        {
-            CapSpy::free_space_result = true;
-
-            auto p = Interface::allocate(n);
-
-            CHECK( p == nullptr );
-
-            CHECK( CapSpy::free_space_called );
-            CHECK( CapSpy::free_space_arg == memory::Metadata::calculate_total_size(n) );
-
-            CHECK( AllocatorSpy::allocate_called );
-            CHECK( AllocatorSpy::allocate_arg == memory::Metadata::calculate_total_size(n) );
-
-            CHECK_FALSE( CapSpy::update_allocations_called );
-        }
-
-        SECTION( "success" )
-        {
-            CapSpy::free_space_result = true;
-            AllocatorSpy::pool = pool;
-
-            auto p = Interface::allocate(n);
-
-            CHECK( p > (void*)pool );
-
-            CHECK( CapSpy::free_space_called );
-            CHECK( CapSpy::free_space_arg == memory::Metadata::calculate_total_size(n) );
-
-            CHECK( AllocatorSpy::allocate_called );
-            CHECK( AllocatorSpy::allocate_arg == memory::Metadata::calculate_total_size(n) );
-
-            CHECK( CapSpy::update_allocations_called );
-            CHECK( CapSpy::update_allocations_arg == memory::Metadata::calculate_total_size(n) );
-        }
-    }
-
-    SECTION( "deallocation" )
-    {
-        SECTION( "nullptr" )
-        {
-            Interface::deallocate(nullptr);
-
-            CHECK_FALSE( AllocatorSpy::deallocate_called );
-            CHECK_FALSE( CapSpy::update_deallocations_called );
-        }
-
-        SECTION( "success" )
-        {
-            auto meta_pool = reinterpret_cast<memory::Metadata*>(pool);
-            meta_pool[0] = memory::Metadata(n);
-
-            auto p = meta_pool[0].payload_offset();
-
-            Interface::deallocate(p);
-
-            CHECK( AllocatorSpy::deallocate_called );
-            CHECK( AllocatorSpy::deallocate_arg == (void*)pool );
-            CHECK( CapSpy::update_deallocations_called );
-            CHECK( CapSpy::update_deallocations_arg == memory::Metadata::calculate_total_size(n) );
-        }
-    }
-    AllocatorSpy::pool = nullptr;
-    AllocatorSpy::deallocate_arg = nullptr;
-}
-
-#endif
index a0fe198631897aed8fc3f9d0c32ae7a938aa3ff7..9e255955b90a240a6bd426e81d1e398ed4b743ee 100644 (file)
@@ -43,9 +43,6 @@ static const Parameter s_params[] =
     { "cap", Parameter::PT_INT, "0:maxSZ", "0",
         "set the per-packet-thread cap on memory (bytes, 0 to disable)" },
 
-    { "soft", Parameter::PT_BOOL, nullptr, "false",
-        "always succeed in allocating memory, even if above the cap" },
-
     { "threshold", Parameter::PT_INT, "0:100", "0",
         "set the per-packet-thread threshold for preemptive cleanup actions "
         "(percent, 0 to disable)" },
@@ -53,6 +50,22 @@ static const Parameter s_params[] =
     { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
 };
 
+THREAD_LOCAL MemoryCounts mem_stats;
+static MemoryCounts zero_stats = { };
+
+const PegInfo mem_pegs[] =
+{
+    { CountType::SUM, "allocations", "total number of allocations" },
+    { CountType::SUM, "deallocations", "total number of deallocations" },
+    { CountType::SUM, "allocated", "total amount of memory allocated" },
+    { CountType::SUM, "deallocated", "total amount of memory allocated" },
+    { CountType::SUM, "reap_attempts", "attempts to reclaim memory" },
+    { CountType::SUM, "reap_failures", "failures to reclaim memory" },
+    { CountType::MAX, "max_in_use", "highest allocated - deallocated" },
+    { CountType::SUM, "total_fudge", "sum of all adjustments" },
+    { CountType::END, nullptr, nullptr }
+};
+
 // -----------------------------------------------------------------------------
 // memory module
 // -----------------------------------------------------------------------------
@@ -68,9 +81,6 @@ bool MemoryModule::set(const char*, Value& v, SnortConfig* sc)
     if ( v.is("cap") )
         sc->memory->cap = v.get_size();
 
-    else if ( v.is("soft") )
-        sc->memory->soft = v.get_bool();
-
     else if ( v.is("threshold") )
         sc->memory->threshold = v.get_uint8();
 
@@ -89,3 +99,9 @@ bool MemoryModule::end(const char*, int, SnortConfig*)
 bool MemoryModule::is_active()
 { return configured; }
 
+const PegInfo* MemoryModule::get_pegs() const
+{ return mem_pegs; }
+
+PegCount* MemoryModule::get_counts() const
+{ return is_active() ? (PegCount*)&mem_stats : (PegCount*)&zero_stats; }
+
index 02179e200a60e3983659f27ea4cc04712ebc47bc..11c17e83d9935c4ae595438120aa57e8af27e05f 100644 (file)
 
 #include "framework/module.h"
 
+struct MemoryCounts
+{
+    PegCount allocations;
+    PegCount deallocations;
+    PegCount allocated;
+    PegCount deallocated;
+    PegCount reap_attempts;
+    PegCount reap_failures;
+    PegCount max_in_use;
+    PegCount total_fudge;
+};
+
+extern THREAD_LOCAL MemoryCounts mem_stats;
+
 class MemoryModule : public snort::Module
 {
 public:
     MemoryModule();
 
+    const PegInfo* get_pegs() const override;
+    PegCount* get_counts() const override;
+
     bool set(const char*, snort::Value&, snort::SnortConfig*) override;
     bool end(const char*, int, snort::SnortConfig*) override;
 
index c90daa20d10dcd596139d8fe9085ff203d22eaf5..3665f6fc4f3a9b6b3736ead754e26d42170b8d9b 100644 (file)
@@ -195,9 +195,10 @@ public:
         uint16_t, IpProtocol, SnortProtocolId, int, AppIdInspector&);
 
     AppIdInspector& get_inspector() const
-    {
-        return inspector;
-    }
+    { return inspector; }
+
+    size_t size_of() override
+    { return sizeof(*this); }
 
     uint32_t session_id = 0;
     snort::Flow* flow = nullptr;
index 6fa3f1e2315285a8104aeb61c07e9cf629921030..d54977da304893b2844452da6b6c57535198255d 100644 (file)
@@ -470,9 +470,10 @@ public:
     ~Dce2SmbFlowData() override;
 
     static void init()
-    {
-        inspector_id = snort::FlowData::create_flow_data_id();
-    }
+    { inspector_id = snort::FlowData::create_flow_data_id(); }
+
+    size_t size_of() override
+    { return sizeof(*this); }
 
 public:
     static unsigned inspector_id;
index 4067d4f26208c52465843811bb05810118149d5b..a3c9994a8b8ba587d58f06cc0bd59783f348ae64 100644 (file)
@@ -119,9 +119,10 @@ public:
     ~Dce2TcpFlowData() override;
 
     static void init()
-    {
-        inspector_id = snort::FlowData::create_flow_data_id();
-    }
+    { inspector_id = snort::FlowData::create_flow_data_id(); }
+
+    size_t size_of() override
+    { return sizeof(*this); }
 
 public:
     static unsigned inspector_id;
index 11e710363b7c668849aa336bf60b584cb10df6c2..9c388ac627ca912821fbfe4a4fc3f2cbd634c628 100644 (file)
@@ -200,12 +200,13 @@ public:
     ~Dce2UdpFlowData() override;
 
     static void init()
-    {
-        inspector_id = snort::FlowData::create_flow_data_id();
-    }
+    { inspector_id = snort::FlowData::create_flow_data_id(); }
 
     static unsigned inspector_id;
     DCE2_UdpSsnData dce2_udp_session;
+
+    size_t size_of() override
+    { return sizeof(*this); }
 };
 
 DCE2_UdpSsnData* get_dce2_udp_session_data(snort::Flow*);
index 77ace13d0a4fd677c791ef15ec201cec0d9e1530..b8a21089b8cef446fdc73d4d7a038c9cd7aa382e 100644 (file)
@@ -174,9 +174,10 @@ public:
     ~Dnp3FlowData() override;
 
     static void init()
-    {
-        inspector_id = snort::FlowData::create_flow_data_id();
-    }
+    { inspector_id = snort::FlowData::create_flow_data_id(); }
+
+    size_t size_of() override
+    { return sizeof(*this); }
 
 public:
     static unsigned inspector_id;
index a201ee8fbe73dab792210c54b85de5689ceacea9..884dde9f7e2164cca814ec81d8acae9a02249661 100644 (file)
@@ -175,6 +175,9 @@ public:
     static void init()
     { inspector_id = snort::FlowData::create_flow_data_id(); }
 
+    size_t size_of() override
+    { return sizeof(*this); }
+
 public:
     static unsigned inspector_id;
     DNSData session;
index ccf5a7af4198f6c63906745c97b389f6820830e8..e8be8db6d6a2ce0c922f25ffe54a99a2ca9ba788 100644 (file)
@@ -102,6 +102,9 @@ public:
     static void init()
     { inspector_id = snort::FlowData::create_flow_data_id(); }
 
+    size_t size_of() override
+    { return sizeof(*this); }
+
 public:
     static unsigned inspector_id;
     TELNET_SESSION session;
@@ -185,6 +188,9 @@ public:
     static void init()
     { inspector_id = snort::FlowData::create_flow_data_id(); }
 
+    size_t size_of() override
+    { return sizeof(*this); }
+
 public:
     static unsigned inspector_id;
     FTP_SESSION session;
@@ -222,6 +228,9 @@ public:
     void handle_expected(snort::Packet*) override;
     void handle_eof(snort::Packet*) override;
 
+    size_t size_of() override
+    { return sizeof(*this); }
+
 public:
     static unsigned inspector_id;
     FTP_DATA_SESSION session;
index 299d1c0c3d474845e0002e4ed1c8ad5375ed8924..8b2ff19ce28bad8add874b82cffc3c977be4580b 100644 (file)
@@ -43,6 +43,9 @@ public:
 
     static void init();
 
+    size_t size_of() override
+    { return sizeof(*this); }
+
 public:
     static unsigned inspector_id;
     GTP_Roptions ropts;
index ebf897cea6a560f614b8ea08400e5531f863c51f..d23bdbe99ade9e21c7a14647b5eecbeaaca9dc2c 100644 (file)
@@ -41,6 +41,9 @@ public:
     friend bool implement_get_buf(unsigned id, Http2FlowData*, Http2Enums::SourceId,
         snort::InspectionBuffer&);
 
+    size_t size_of() override
+    { return sizeof(*this); }
+
 protected:
     // 0 element refers to client frame, 1 element refers to server frame
     bool preface[2] = { true, false };
index 678fffc5300d26fbc0b9e75e675389cfc8571516..73ebac51602779fb938572d53e2285413f18877a 100644 (file)
@@ -63,6 +63,9 @@ public:
     friend class HttpUnitTestSetup;
 #endif
 
+    size_t size_of() override
+    { return sizeof(*this); }
+
 private:
     // Convenience routines
     void half_reset(HttpEnums::SourceId source_id);
index ec644b14cf616c11bde9bc7653b1259b9533253a..0aa5ebd854e4c0f01d2cd4d96ed2ea3208b34c81 100644 (file)
@@ -172,6 +172,9 @@ public:
     static void init()
     { inspector_id = snort::FlowData::create_flow_data_id(); }
 
+    size_t size_of() override
+    { return sizeof(*this); }
+
 public:
     static unsigned inspector_id;
     IMAPData session;
index b5ffd73c894b353dc07742538d482eaaaf937f07..261644f9943576a1d705f3eddc9c10078901ebb2 100644 (file)
@@ -54,6 +54,9 @@ public:
         ssn_data.flags = 0;
     }
 
+    size_t size_of() override
+    { return sizeof(*this); }
+
 public:
     static unsigned inspector_id;
     modbus_session_data_t ssn_data;
index f9d420eb3ce94c286a92988eae6ab2e43aa10ce0..4e4bc2e03583735911014f10fb6a8d596850d00d 100644 (file)
@@ -126,6 +126,9 @@ public:
     static void init()
     { inspector_id = snort::FlowData::create_flow_data_id(); }
 
+    size_t size_of() override
+    { return sizeof(*this); }
+
 public:
     static unsigned inspector_id;
     POPData session;
index 0d30f17d6a77b965766d788dd4505a19d9044b97..37869c0735c6e823bfb4efee96459f4bb0dbae04 100644 (file)
@@ -89,6 +89,9 @@ public:
     static void init()
     { inspector_id = FlowData::create_flow_data_id(); }
 
+    size_t size_of() override
+    { return sizeof(*this); }
+
 public:
     static unsigned inspector_id;
     RpcSsnData session;
index bcac9829acba7a35020c690b2e3cd2528cfbf617..74b4930db57f43af50087f1ca5a8e46c87bb3e5b 100644 (file)
@@ -45,6 +45,9 @@ public:
     static void init()
     { inspector_id = snort::FlowData::create_flow_data_id(); }
 
+    size_t size_of() override
+    { return sizeof(*this); }
+
 public:
     static unsigned inspector_id;
     SIPData session;
index 5ccf7bc3cb4e6a8b8ea7830bd8eadd33fdd3a026..6a75a3cc5b1594d58b5d411c789dc61e5047f78c 100644 (file)
@@ -171,6 +171,9 @@ public:
     static void init()
     { inspector_id = snort::FlowData::create_flow_data_id(); }
 
+    size_t size_of() override
+    { return sizeof(*this); }
+
 public:
     static unsigned inspector_id;
     SMTPData session;
index 6c1f0392023eacebf063a5e95e84d44299ea6ba2..ad123b3b522789929b1899d6f8155fe9bc932c0e 100644 (file)
@@ -104,6 +104,9 @@ public:
     static void init()
     { inspector_id = snort::FlowData::create_flow_data_id(); }
 
+    size_t size_of() override
+    { return sizeof(*this); }
+
 public:
     static unsigned inspector_id;
     SSHData session;
index 63a812adf46bdb8d861ea083d53772bffb9670b5..f70ca34eb1d14e67727a29f717d8bb8ee3e5fa3d 100644 (file)
@@ -45,6 +45,9 @@ public:
     static void init()
     { inspector_id = snort::FlowData::create_flow_data_id(); }
 
+    size_t size_of() override
+    { return sizeof(*this); }
+
 public:
     static unsigned inspector_id;
     SSLData session;
index 5797838f10f1c6d3fcb55bbf7ba1523d8a36c65f..369048a95598fd57fc79b3f3636d61559a0964ed 100644 (file)
@@ -304,6 +304,7 @@ static void base_dtor(Inspector* p)
 
 static void base_tterm()
 {
+    // this can't happen sooner because the counts haven't been harvested yet
     delete flow_con;
     flow_con = nullptr;
 }
index 4a3f528666ffe14c8125f06d62b0dd02443321b5..c6aa2e058d33a070e51db77e0d2e6bf0b5df1e4e 100644 (file)
@@ -37,7 +37,7 @@ using namespace std;
 //-------------------------------------------------------------------------
 Trace TRACE_NAME(stream);
 
-#define CACHE_PARAMS(name, max, prune, idle, cleanup) \
+#define CACHE_PARAMS(name, max, prune, idle, weight) \
 static const Parameter name[] = \
 { \
     { "max_sessions", Parameter::PT_INT, "2:max32", max, \
@@ -48,16 +48,19 @@ static const Parameter name[] = \
  \
     { "idle_timeout", Parameter::PT_INT, "1:max32", idle, \
       "maximum inactive time before retiring session tracker" }, \
+ \
+    { "cap_weight", Parameter::PT_INT, "0:65535", idle, \
+      "additional bytes to track per flow for better estimation against cap" }, \
  \
     { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } \
 }
 
-CACHE_PARAMS(ip_params,    "16384",  "30",  "180", "5");
-CACHE_PARAMS(icmp_params,  "65536",  "30",  "180", "5");
-CACHE_PARAMS(tcp_params,  "262144",  "30", "3600", "5");
-CACHE_PARAMS(udp_params,  "131072",  "30",  "180", "5");
-CACHE_PARAMS(user_params,   "1024",  "30",  "180", "5");
-CACHE_PARAMS(file_params,    "128",  "30",  "180", "5");
+CACHE_PARAMS(ip_params,    "16384",  "30",  "180", "64");
+CACHE_PARAMS(icmp_params,  "65536",  "30",  "180", "8");
+CACHE_PARAMS(tcp_params,  "262144",  "30", "3600", "256");
+CACHE_PARAMS(udp_params,  "131072",  "30",  "180", "128");
+CACHE_PARAMS(user_params,   "1024",  "30",  "180", "256");
+CACHE_PARAMS(file_params,    "128",  "30",  "180", "32");
 
 #define CACHE_TABLE(cache, proto, params) \
     { cache, Parameter::PT_TABLE, params, nullptr, \
@@ -167,6 +170,9 @@ bool StreamModule::set(const char* fqn, Value& v, SnortConfig* c)
     else if ( v.is("idle_timeout") )
         fc->nominal_timeout = v.get_uint32();
 
+    else if ( v.is("cap_weight") )
+        fc->cap_weight = v.get_uint16();
+
     else
         return false;
 
index 464d1c377a29614be4fb2dd372d89b48540cf66b..d76e19b66f49fea671b4d810ba3e8026e88aeb01 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "detection/detection_engine.h"
 #include "file_api/file_flows.h"
+#include "memory/memory_cap.h"
 #include "packet_io/sfdaq.h"
 #include "profiler/profiler_defs.h"
 #include "protocols/packet.h"
@@ -42,8 +43,11 @@ static THREAD_LOCAL ProfileStats file_ssn_stats;
 // FileSession methods
 //-------------------------------------------------------------------------
 
-FileSession::FileSession(Flow* flow) : Session(flow) { }
+FileSession::FileSession(Flow* flow) : Session(flow)
+{ memory::MemoryCap::update_allocations(sizeof(*this)); }
 
+FileSession::~FileSession()
+{ memory::MemoryCap::update_deallocations(sizeof(*this)); }
 
 bool FileSession::setup(Packet*)
 {
index a3752970f994ba75d484fe4045bf949d5cfb5ab7..af443baa583a9ec2a8f2691ec804ef49bb4e9db8 100644 (file)
@@ -26,6 +26,7 @@ class FileSession : public Session
 {
 public:
     FileSession(snort::Flow*);
+    ~FileSession() override;
 
     bool setup(snort::Packet*) override;
     void clear() override;
index 59f7716fa56aa799e6475104419d527eab7c18cc..230bc34572d7a8f457c5d5901e56758b6ee3f854 100644 (file)
@@ -24,6 +24,7 @@
 #include "icmp_session.h"
 
 #include "flow/flow_key.h"
+#include "memory/memory_cap.h"
 #include "profiler/profiler_defs.h"
 #include "protocols/icmp4.h"
 #include "protocols/packet.h"
@@ -177,8 +178,10 @@ static int ProcessIcmpUnreach(Packet* p)
 //-------------------------------------------------------------------------
 
 IcmpSession::IcmpSession(Flow* flow) : Session(flow)
-{
-}
+{ memory::MemoryCap::update_allocations(sizeof(*this)); }
+
+IcmpSession::~IcmpSession()
+{ memory::MemoryCap::update_deallocations(sizeof(*this)); }
 
 bool IcmpSession::setup(Packet*)
 {
index afae36526ce216901437cbb78bacfaf14a107fb8..e0d35612fa3ceb61b7c29ee9e11e232423e04391 100644 (file)
@@ -26,6 +26,7 @@ class IcmpSession : public Session
 {
 public:
     IcmpSession(snort::Flow*);
+    ~IcmpSession() override;
 
     bool setup(snort::Packet*) override;
     void update_direction(char dir, const snort::SfIp*, uint16_t port) override;
index b6a1029810a912f6d5d5df421d27eb3d766cc198..15bd874aa0ad5f066593f96329879a33b49ed18b 100644 (file)
@@ -75,6 +75,7 @@
 #include "log/messages.h"
 #include "main/snort.h"
 #include "main/snort_config.h"
+#include "memory/memory_cap.h"
 #include "packet_io/active.h"
 #include "packet_io/sfdaq.h"
 #include "profiler/profiler_defs.h"
@@ -135,6 +136,7 @@ struct Fragment
     {
         delete[] fptr;
         ip_stats.nodes_released++;
+        memory::MemoryCap::update_deallocations(sizeof(*this) + flen);
     }
 
     uint8_t* data = nullptr;    /* ptr to adjusted start position */
@@ -154,6 +156,7 @@ private:
     inline void init(uint16_t flen, const uint8_t* fptr, int ord)
     {
         assert(flen > 0);
+        memory::MemoryCap::update_allocations(sizeof(*this) + flen);
 
         this->flen = flen;
         this->fptr = new uint8_t[flen];
index 2dd3966568a3e8e50689396928c3ec65702f8519..0a79c34b20d33892f4381e35c58e2d612faf9d40 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "ip_session.h"
 
+#include "memory/memory_cap.h"
 #include "profiler/profiler_defs.h"
 #include "protocols/packet.h"
 
@@ -115,8 +116,10 @@ static inline void UpdateSession(Packet* p, Flow* lws)
 //-------------------------------------------------------------------------
 
 IpSession::IpSession(Flow* flow) : Session(flow)
-{
-}
+{ memory::MemoryCap::update_allocations(sizeof(*this)); }
+
+IpSession::~IpSession()
+{ memory::MemoryCap::update_deallocations(sizeof(*this)); }
 
 void IpSession::clear()
 {
index ccedb73d489022cbcecf11261f02a2f10ec4e7e0..0a93354c8f803c429ff75ee3bceea6f8f2868670 100644 (file)
@@ -76,6 +76,7 @@ class IpSession : public Session
 {
 public:
     IpSession(snort::Flow*);
+    ~IpSession() override;
 
     bool setup(snort::Packet*) override;
     int process(snort::Packet*) override;
index f2a7100691bd7413f460df9c01a110d55ccaba1c..d8e1316785a3d8c286bb3ac59da14e637576218c 100644 (file)
@@ -463,14 +463,6 @@ void TcpStreamSession::start_proxy()
 // tcp module stuff
 //-------------------------------------------------------------------------
 
-void TcpStreamSession::sinit()
-{
-    //AtomSplitter::init();  // FIXIT-L PAF implement
-}
-
-void TcpStreamSession::sterm()
-{ }
-
 void TcpStreamSession::print()
 {
     char buf[64];
@@ -489,4 +481,3 @@ void TcpStreamSession::print()
     server.print();
 }
 
-
index 2bfe6319e4859133bfaf45490bedf83f3835c0cf..27d08672d99e83d73a8ae3742e22482eac73cfba 100644 (file)
@@ -58,9 +58,6 @@ public:
     uint16_t get_mss(bool to_server) const;
     uint8_t get_tcp_options_len(bool to_server) const;
 
-    static void sinit();
-    static void sterm();
-
     void reset();
     void start_proxy();
     void print();
index 4f473c31cecc027ffe343dc20b7dcc81a0ac6014..b973ab7e879ff66adaaf786e9a5a90bd45488f0c 100644 (file)
@@ -76,11 +76,13 @@ bool StreamTcp::configure(SnortConfig* sc)
 void StreamTcp::tinit()
 {
     TcpHAManager::tinit();
+    TcpSession::sinit();
 }
 
 void StreamTcp::tterm()
 {
     TcpHAManager::tterm();
+    TcpSession::sterm();
 }
 
 NORETURN_ASSERT void StreamTcp::eval(Packet*)
@@ -121,16 +123,6 @@ static Session* tcp_ssn(Flow* lws)
     return new TcpSession(lws);
 }
 
-static void tcp_tinit()
-{
-    TcpSession::sinit();
-}
-
-static void tcp_tterm()
-{
-    TcpSession::sterm();
-}
-
 static const InspectApi tcp_api =
 {
     {
@@ -151,8 +143,8 @@ static const InspectApi tcp_api =
     nullptr,  // service
     nullptr,  // init
     nullptr,  // term
-    tcp_tinit,
-    tcp_tterm,
+    nullptr,  // tinit,
+    nullptr,  // tterm,
     tcp_ctor,
     tcp_dtor,
     tcp_ssn,
index b538fa2b9695da14438ad6e6a93e7b18e91b4f15..7cf2510ac8b923c7aab6f1829e68f1d8e36f8e54 100644 (file)
@@ -28,6 +28,7 @@
 #include "detection/detection_engine.h"
 #include "log/log.h"
 #include "main/snort.h"
+#include "memory/memory_cap.h"
 #include "profiler/profiler.h"
 #include "protocols/packet_manager.h"
 #include "time/packet_time.h"
@@ -218,6 +219,7 @@ int TcpReassembler::add_reassembly_segment(
 
     // FIXIT-L don't allocate overlapped part
     tsn = TcpSegmentNode::init(tsd);
+
     tsn->offset = slide;
     tsn->c_len = (uint16_t)newSize;
     tsn->i_len = (uint16_t)newSize;
@@ -1075,8 +1077,10 @@ int TcpReassembler::flush_on_data_policy(TcpReassemblerState& trs, Packet* p)
     }
     break;
     }
-
-    if ( flushed and !trs.sos.session->flow->two_way_traffic() and !p->ptrs.tcph->is_syn() )
+    // FIXIT-H a drop rule will yoink the seglist out from under us
+    // because apply_delayed_action is only deferred to end of context
+    if ( flushed and trs.sos.seg_count and
+        !trs.sos.session->flow->two_way_traffic() and !p->ptrs.tcph->is_syn() )
     {
         TcpStreamTracker::TcpState peer = trs.tracker->session->get_peer_state(trs.tracker);
 
index 1ac52154551d3f6164eefa4f1e62862d55b6a7b6..8f613919cdd2ecff7ea32a152ee238e674a51946 100644 (file)
 
 #include "tcp_segment_node.h"
 
+#include "main/thread.h"
+#include "memory/memory_cap.h"
 #include "utils/util.h"
 
 #include "segment_overlap_editor.h"
 #include "tcp_module.h"
 
-TcpSegmentNode::TcpSegmentNode(const struct timeval& tv, const uint8_t* payload, uint16_t len) :
-    prev(nullptr), next(nullptr), tv(tv), ts(0), i_seq(0), c_seq(0), i_len(len),
-    c_len(len), offset(0)
+#define USE_RESERVE
+#ifdef USE_RESERVE
+static THREAD_LOCAL TcpSegmentNode* reserved = nullptr;
+static THREAD_LOCAL unsigned reserve_sz = 0;
+
+static constexpr unsigned num_res = 4096;
+static constexpr unsigned res_min = 1024;
+static constexpr unsigned res_max = 1460;
+#endif
+
+void TcpSegmentNode::setup()
+{
+#ifdef USE_RESERVE
+    reserved = nullptr;
+    reserve_sz = 0;
+#endif
+}
+
+void TcpSegmentNode::clear()
 {
-    data = ( uint8_t* )snort_alloc(len);
-    memcpy(data, payload, len);
-    tcpStats.mem_in_use += len;
+#ifdef USE_RESERVE
+    while ( reserved )
+    {
+        TcpSegmentNode* tsn = reserved;
+        reserved = reserved->next;
+        memory::MemoryCap::update_deallocations(sizeof(*tsn) + tsn->size);
+        tcpStats.mem_in_use -= tsn->size;
+        snort_free(tsn);
+    }
+    reserve_sz = 0;
+#endif
 }
 
 //-------------------------------------------------------------------------
 // TcpSegment stuff
 //-------------------------------------------------------------------------
+
+TcpSegmentNode* TcpSegmentNode::create(
+    const struct timeval& tv, const uint8_t* payload, uint16_t len)
+{
+    TcpSegmentNode* tsn;
+
+#ifdef USE_RESERVE
+    if ( reserved and len > res_min and len <= res_max )
+    {
+        tsn = reserved;
+        reserved = tsn->next;
+        --reserve_sz;
+    }
+    else
+#endif
+    {
+        size_t size = sizeof(*tsn) + len;
+        memory::MemoryCap::update_allocations(size);
+        tsn = (TcpSegmentNode*)snort_alloc(size);
+        tsn->size = len;
+        tcpStats.mem_in_use += len;
+    }
+    tsn->tv = tv;
+    tsn->i_len = tsn->c_len = len;
+    memcpy(tsn->data, payload, len);
+
+    tsn->prev = tsn->next = nullptr;
+    tsn->i_seq = tsn->c_seq = 0;
+    tsn->offset = 0;
+    tsn->ts = 0;
+
+    return tsn;
+}
+
 TcpSegmentNode* TcpSegmentNode::init(TcpSegmentDescriptor& tsd)
 {
-    return new TcpSegmentNode(tsd.get_pkt()->pkth->ts, tsd.get_pkt()->data, tsd.get_seg_len());
+    return create(tsd.get_pkt()->pkth->ts, tsd.get_pkt()->data, tsd.get_seg_len());
 }
 
 TcpSegmentNode* TcpSegmentNode::init(TcpSegmentNode& tns)
 {
-    return new TcpSegmentNode(tns.tv, tns.payload(), tns.c_len);
+    return create(tns.tv, tns.payload(), tns.c_len);
 }
 
 void TcpSegmentNode::term()
 {
-    snort_free(data);
+#ifdef USE_RESERVE
+    if ( size == res_max and reserve_sz < num_res )
+    {
+        next = reserved;
+        reserved = this;
+        reserve_sz++;
+    }
+    else
+#endif
+    {
+        memory::MemoryCap::update_deallocations(sizeof(*this) + size);
+        tcpStats.mem_in_use -= size;
+        snort_free(this);
+    }
     tcpStats.segs_released++;
-    tcpStats.mem_in_use -= i_len;
-    delete this;
 }
 
-bool TcpSegmentNode::is_retransmit(const uint8_t* rdata, uint16_t rsize, uint32_t rseq, uint16_t orig_dsize, bool *full_retransmit)
+bool TcpSegmentNode::is_retransmit(
+    const uint8_t* rdata, uint16_t rsize, uint32_t rseq, uint16_t orig_dsize,
+    bool *full_retransmit)
 {
     // retransmit must have same payload at same place
     if ( !SEQ_EQ(i_seq, rseq) )
index 5be3265b623e38d6c0ea8441d039dabb5b467f4d..885548d20cbbdd29145fece88d609a258e158d3a 100644 (file)
@@ -32,19 +32,25 @@ class TcpSegmentDescriptor;
 // we make a lot of TcpSegments so it is organized by member
 // size/alignment requirements to minimize unused space
 // ... however, use of padding below is critical, adjust if needed
+// and we use the struct hack to avoid 2 allocs per node
 //-----------------------------------------------------------------
 
 class TcpSegmentNode
 {
-public:
-       TcpSegmentNode(const struct timeval& tv, const uint8_t* segment, uint16_t len);
+private:
+    static TcpSegmentNode* create(const struct timeval& tv, const uint8_t* segment, uint16_t len);
 
-    static TcpSegmentNode* init(TcpSegmentDescriptor& tsd);
-    static TcpSegmentNode* init(TcpSegmentNode& tns);
-    static TcpSegmentNode* init(const struct timeval&, const uint8_t*, unsigned);
+public:
+    static TcpSegmentNode* init(TcpSegmentDescriptor&);
+    static TcpSegmentNode* init(TcpSegmentNode&);
 
     void term();
+
+    static void setup();
+    static void clear();
+
     bool is_retransmit(const uint8_t*, uint16_t size, uint32_t, uint16_t, bool*);
+
     uint8_t* payload()
     { return data + offset; }
 
@@ -56,11 +62,10 @@ public:
             return (c_seq + c_len) < to_seq;
     }
 
+public:
     TcpSegmentNode* prev;
     TcpSegmentNode* next;
 
-    uint8_t* data;
-
     struct timeval tv;
     uint32_t ts;
     uint32_t i_seq;             // initial seq # of the data segment
@@ -68,6 +73,9 @@ public:
     uint16_t i_len;             // initial length of the data segment
     uint16_t c_len;             // length of data remaining for reassembly
     uint16_t offset;
+    uint16_t size;              // actual allocated size (overlaps cause i_len to differ)
+
+    uint8_t data[1];
 };
 
 class TcpSegmentList
index 57813b6d7ffc14547cfe5ba207192090329edb27..f5aa03aa93af8b1ee0d2e88e20b588ff1cd4adcf 100644 (file)
@@ -51,6 +51,7 @@
 #include "detection/detection_engine.h"
 #include "detection/rules.h"
 #include "log/log.h"
+#include "memory/memory_cap.h"
 #include "profiler/profiler.h"
 #include "protocols/eth.h"
 
 #include "tcp_module.h"
 #include "tcp_normalizers.h"
 #include "tcp_reassemblers.h"
+#include "tcp_segment_node.h"
 #include "tcp_stream_state_machine.h"
 
 using namespace snort;
 
+void TcpSession::sinit()
+{ TcpSegmentNode::setup(); }
+
+void TcpSession::sterm()
+{ TcpSegmentNode::clear(); }
+
 TcpSession::TcpSession(Flow* flow)
     : TcpStreamSession(flow)
 {
@@ -73,11 +81,14 @@ TcpSession::TcpSession(Flow* flow)
     client.session = this;
     server.session = this;
     tcpStats.instantiated++;
+
+    memory::MemoryCap::update_allocations(sizeof(*this));
 }
 
 TcpSession::~TcpSession()
 {
     clear_session(true, false, false);
+    memory::MemoryCap::update_deallocations(sizeof(*this));
 }
 
 bool TcpSession::setup(Packet* p)
index 8183f6191f42362632a79d03b902f9ffe977ff63..7b9de1e5b0aa5cef944f578e672da82a85836fa4 100644 (file)
@@ -38,6 +38,9 @@ public:
     TcpSession(snort::Flow*);
     ~TcpSession() override;
 
+    static void sinit();
+    static void sterm();
+
     bool setup(snort::Packet*) override;
     void restart(snort::Packet* p) override;
     void precheck(snort::Packet* p) override;
index 3beb3d9e42e4fdfef9c0bfc6bc68f51a016d9680..f7f33a3f7c10e398b2aa4c0666f58a5694b4d44b 100644 (file)
@@ -26,6 +26,7 @@
 #include "flow/session.h"
 #include "framework/data_bus.h"
 #include "hash/xhash.h"
+#include "memory/memory_cap.h"
 #include "profiler/profiler_defs.h"
 #include "protocols/packet.h"
 
@@ -102,8 +103,10 @@ static int ProcessUdp(
 //-------------------------------------------------------------------------
 
 UdpSession::UdpSession(Flow* flow) : Session(flow)
-{
-}
+{ memory::MemoryCap::update_allocations(sizeof(*this)); }
+
+UdpSession::~UdpSession()
+{ memory::MemoryCap::update_deallocations(sizeof(*this)); }
 
 bool UdpSession::setup(Packet* p)
 {
@@ -135,7 +138,6 @@ void UdpSession::clear()
 {
     UdpSessionCleanup(flow);
     UdpHAManager::process_deletion(flow);
-    flow->clear();
 }
 
 void UdpSession::update_direction(
index 5d67010f458e6a6bf0ea0807f079599218c2472f..860a4532d6d367a18fd4031a2d24d1ee94bce026 100644 (file)
@@ -28,6 +28,7 @@ class UdpSession : public Session
 {
 public:
     UdpSession(snort::Flow*);
+    ~UdpSession() override;
 
     bool setup(snort::Packet*) override;
     void update_direction(char dir, const snort::SfIp*, uint16_t port) override;
index 8a6a62e885656de56a022066610fe1e9fbaef7f2..e510791afe85ee33704fd014c7cc4f4dd86c0af3 100644 (file)
@@ -26,6 +26,7 @@
 #include "detection/detection_engine.h"
 #include "detection/rules.h"
 #include "main/snort.h"
+#include "memory/memory_cap.h"
 #include "profiler/profiler_defs.h"
 #include "protocols/packet.h"
 #include "utils/util.h"
@@ -53,8 +54,12 @@ THREAD_LOCAL ProfileStats user_perf_stats;
 UserSegment* UserSegment::init(const uint8_t* p, unsigned n)
 {
     unsigned bucket = (n > BUCKET) ? n : BUCKET;
-    UserSegment* us = (UserSegment*)snort_alloc(sizeof(*us)+bucket-1);
+    unsigned size = sizeof(UserSegment) + bucket -1;
 
+    memory::MemoryCap::update_allocations(size);
+    UserSegment* us = (UserSegment*)snort_alloc(size);
+
+    us->size = size;
     us->len = 0;
     us->offset = 0;
     us->used = 0;
@@ -65,6 +70,7 @@ UserSegment* UserSegment::init(const uint8_t* p, unsigned n)
 
 void UserSegment::term(UserSegment* us)
 {
+    memory::MemoryCap::update_deallocations(us->size);
     snort_free(us);
 }
 
@@ -424,8 +430,11 @@ void UserSession::restart(Packet* p)
 // UserSession methods
 //-------------------------------------------------------------------------
 
-UserSession::UserSession(Flow* flow) : Session(flow) { }
+UserSession::UserSession(Flow* flow) : Session(flow)
+{ memory::MemoryCap::update_allocations(sizeof(*this)); }
 
+UserSession::~UserSession()
+{ memory::MemoryCap::update_deallocations(sizeof(*this)); }
 
 bool UserSession::setup(Packet*)
 {
@@ -443,7 +452,6 @@ void UserSession::clear()
 {
     client.term();
     server.term();
-    flow->restart();
 }
 
 void UserSession::set_splitter(bool c2s, StreamSplitter* ss)
index 73183016a85016f70570541adfbb82d4aee5a1c0..2f263f255a7b480ca4bad4f2ae6c58fd9cec8930 100644 (file)
@@ -48,6 +48,7 @@ private:
     unsigned len;
     unsigned offset;
     unsigned used;
+    unsigned size;
     uint8_t data[1];
 };
 
@@ -75,6 +76,7 @@ class UserSession : public Session
 {
 public:
     UserSession(snort::Flow*);
+    ~UserSession() override;
 
     bool setup(snort::Packet*) override;
     void clear() override;