#include "filters/detection_filter.h"
#include "framework/cursor.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_defs.h"
+#include "hash/hash_key_operations.h"
#include "hash/xhash.h"
#include "ips_options/extract.h"
#include "ips_options/ips_flowbits.h"
static inline bool operator==(const struct timeval& a, const struct timeval& b)
{ return a.tv_sec == b.tv_sec && a.tv_usec == b.tv_usec; }
-static uint32_t detection_option_hash_func(HashFnc*, const unsigned char* k, int)
+class DetectionOptionHashKeyOps : public HashKeyOperations
{
- const detection_option_key_t* key = (const detection_option_key_t*)k;
+public:
+ DetectionOptionHashKeyOps(int rows)
+ : HashKeyOperations(rows)
+ { }
- if ( key->option_type != RULE_OPTION_TYPE_LEAF_NODE )
+ unsigned do_hash(const unsigned char* k, int) override
{
- IpsOption* opt = (IpsOption*)key->option_data;
- return opt->hash();
+ const detection_option_key_t* key = (const detection_option_key_t*)k;
+
+ if ( key->option_type != RULE_OPTION_TYPE_LEAF_NODE )
+ {
+ IpsOption* opt = (IpsOption*)key->option_data;
+ return opt->hash();
+ }
+ return 0;
}
- return 0;
-}
-static bool detection_option_key_compare_func(const void* k1, const void* k2, size_t)
-{
- const detection_option_key_t* key1 = (const detection_option_key_t*)k1;
- const detection_option_key_t* key2 = (const detection_option_key_t*)k2;
+ bool key_compare(const void* k1, const void* k2, size_t) override
+ {
+ const detection_option_key_t* key1 = (const detection_option_key_t*)k1;
+ const detection_option_key_t* key2 = (const detection_option_key_t*)k2;
- assert(key1 && key2);
+ assert(key1 && key2);
- if ( key1->option_type != key2->option_type )
- return false;
+ if ( key1->option_type != key2->option_type )
+ return false;
- if ( key1->option_type != RULE_OPTION_TYPE_LEAF_NODE )
- {
- IpsOption* opt1 = (IpsOption*)key1->option_data;
- IpsOption* opt2 = (IpsOption*)key2->option_data;
+ if ( key1->option_type != RULE_OPTION_TYPE_LEAF_NODE )
+ {
+ IpsOption* opt1 = (IpsOption*)key1->option_data;
+ IpsOption* opt2 = (IpsOption*)key2->option_data;
- if ( *opt1 == *opt2 )
- return true;
+ if ( *opt1 == *opt2 )
+ return true;
+ }
+ return false;
}
- return false;
-}
+};
-static int detection_hash_free_func(void* option_key, void*)
+class DetectionOptionHash : public XHash
{
- detection_option_key_t* key = (detection_option_key_t*)option_key;
+public:
- if ( key->option_type != RULE_OPTION_TYPE_LEAF_NODE )
+ DetectionOptionHash(int rows, int key_len)
+ : XHash(rows, key_len)
{
- IpsOption* opt = (IpsOption*)key->option_data;
- IpsManager::delete_option(opt);
+ initialize(new DetectionOptionHashKeyOps(nrows));
}
- return 0;
-}
-
-static XHash* DetectionHashTableNew()
-{
- XHash* doht = new XHash(HASH_RULE_OPTIONS, sizeof(detection_option_key_t),
- 0, 0, false, nullptr, detection_hash_free_func, true);
-
- doht->set_key_opcodes(detection_option_hash_func, detection_option_key_compare_func);
-
- return doht;
-}
-
-void DetectionHashTableFree(XHash* doht)
-{
- delete doht;
-}
-
-void* add_detection_option(SnortConfig* sc, option_type_t type, void* option_data)
-{
- if ( !sc->detection_option_hash_table )
- sc->detection_option_hash_table = DetectionHashTableNew();
- detection_option_key_t key;
- key.option_type = type;
- key.option_data = option_data;
+ ~DetectionOptionHash() override
+ {
+ delete_hash_table();
+ }
- if ( void* p = sc->detection_option_hash_table->get_user_data(&key) )
- return p;
+ void free_user_data(HashNode* hnode) override
+ {
+ detection_option_key_t* key = (detection_option_key_t*)hnode->key;
- sc->detection_option_hash_table->insert(&key, option_data);
- return nullptr;
-}
+ if ( key->option_type != RULE_OPTION_TYPE_LEAF_NODE )
+ {
+ IpsOption* opt = (IpsOption*)key->option_data;
+ IpsManager::delete_option(opt);
+ }
+ }
+};
static uint32_t detection_option_tree_hash(detection_option_tree_node_t* node)
{
- uint32_t a,b,c;
- int i;
-
- if (!node)
- return 0;
+ assert(node);
+ uint32_t a, b, c;
a = b = c = 0;
- for (i=0; i<node->num_children; i++)
+ for ( int i = 0; i < node->num_children; i++)
{
#if (defined(__ia64) || defined(__amd64) || defined(_LP64))
{
return c;
}
-static uint32_t detection_option_tree_hash_func(HashFnc*, const unsigned char* k, int)
-{
- const detection_option_key_t* key = (const detection_option_key_t*)k;
- detection_option_tree_node_t* node;
-
- if (!key || !key->option_data)
- return 0;
-
- node = (detection_option_tree_node_t*)key->option_data;
-
- return detection_option_tree_hash(node);
-}
-
static bool detection_option_tree_compare(
const detection_option_tree_node_t* r, const detection_option_tree_node_t* l)
{
return true;
}
-static bool detection_option_tree_compare_func(const void* k1, const void* k2, size_t)
+void free_detection_option_tree(detection_option_tree_node_t* node)
+{
+ for (int i = 0; i < node->num_children; i++)
+ free_detection_option_tree(node->children[i]);
+
+ snort_free(node->children);
+ snort_free(node->state);
+ snort_free(node);
+}
+
+class DetectionOptionTreeHashKeyOps : public HashKeyOperations
{
- const detection_option_key_t* key_r = (const detection_option_key_t*)k1;
- const detection_option_key_t* key_l = (const detection_option_key_t*)k2;
+public:
+ DetectionOptionTreeHashKeyOps(int rows)
+ : HashKeyOperations(rows)
+ { }
- if ( !key_r or !key_l )
- return false;
+ unsigned do_hash(const unsigned char* k, int) override
+ {
+ assert(k);
+ const detection_option_key_t* key = (const detection_option_key_t*)k;
+ if ( !key->option_data )
+ return 0;
- const detection_option_tree_node_t* r = (const detection_option_tree_node_t*)key_r->option_data;
- const detection_option_tree_node_t* l = (const detection_option_tree_node_t*)key_l->option_data;
+ detection_option_tree_node_t* node = (detection_option_tree_node_t*)key->option_data;
- return detection_option_tree_compare(r, l);
-}
+ return detection_option_tree_hash(node);
+ }
+
+ bool key_compare(const void* k1, const void* k2, size_t) override
+ {
+ assert(k1 && k2);
+
+ const detection_option_key_t* key_r = (const detection_option_key_t*)k1;
+ const detection_option_key_t* key_l = (const detection_option_key_t*)k2;
+
+ const detection_option_tree_node_t* r = (const detection_option_tree_node_t*)key_r->option_data;
+ const detection_option_tree_node_t* l = (const detection_option_tree_node_t*)key_l->option_data;
+
+ return detection_option_tree_compare(r, l);
+ }
+};
+
+class DetectionOptionTreeHash : public XHash
+{
+public:
+ DetectionOptionTreeHash(int rows, int key_len)
+ : XHash(rows, key_len)
+ {
+ initialize(new DetectionOptionTreeHashKeyOps(nrows));
+ }
+
+ ~DetectionOptionTreeHash() override
+ {
+ delete_hash_table();
+ }
-static int detection_option_tree_free_func(void*, void* data)
+ void free_user_data(HashNode* hnode) override
+ {
+ free_detection_option_tree((detection_option_tree_node_t*)hnode->data);
+ }
+
+};
+
+static DetectionOptionHash* DetectionHashTableNew()
{
- detection_option_tree_node_t* node = (detection_option_tree_node_t*)data;
- free_detection_option_tree(node);
- return 0;
+ return new DetectionOptionHash(HASH_RULE_OPTIONS, sizeof(detection_option_key_t));
}
-void DetectionTreeHashTableFree(XHash* dtht)
+static DetectionOptionTreeHash* DetectionTreeHashTableNew()
{
- delete dtht;
+ return new DetectionOptionTreeHash(HASH_RULE_TREE, sizeof(detection_option_key_t));
}
-static XHash* DetectionTreeHashTableNew()
+void* add_detection_option(SnortConfig* sc, option_type_t type, void* option_data)
{
- XHash* dtht = new XHash(HASH_RULE_TREE, sizeof(detection_option_key_t),
- 0, 0, false, nullptr, detection_option_tree_free_func, true);
+ if ( !sc->detection_option_hash_table )
+ sc->detection_option_hash_table = DetectionHashTableNew();
- dtht->set_key_opcodes(detection_option_tree_hash_func, detection_option_tree_compare_func);
+ detection_option_key_t key;
+ key.option_type = type;
+ key.option_data = option_data;
+
+ if ( void* p = sc->detection_option_hash_table->get_user_data(&key) )
+ return p;
- return dtht;
+ sc->detection_option_hash_table->insert(&key, option_data);
+ return nullptr;
}
void print_option_tree(detection_option_tree_node_t* node, int level)
// Back up byte_extract vars so they don't get overwritten between rules
trace_log(detection, TRACE_RULE_VARS, "Rule options variables: \n");
- for ( int i = 0; i < NUM_IPS_OPTIONS_VARS; ++i )
+ for ( unsigned i = 0; i < NUM_IPS_OPTIONS_VARS; ++i )
{
GetVarValueByIndex(&(tmp_byte_extract_vars[i]), (int8_t)i);
trace_logf_wo_name(detection, TRACE_RULE_VARS, "var[%d]=%d ", i,
detection_option_tree_node_t* child_node = node->children[i];
dot_node_state_t* child_state = child_node->state + get_instance_id();
- for ( int j = 0; j < NUM_IPS_OPTIONS_VARS; ++j )
+ for ( unsigned j = 0; j < NUM_IPS_OPTIONS_VARS; ++j )
SetVarValueByIndex(tmp_byte_extract_vars[j], (int8_t)j);
if ( loop_count > 0 )
if ( node->num_children )
{
- for ( int i=0; i < node->num_children; ++i )
+ for ( int i = 0; i < node->num_children; ++i )
detection_option_node_update_otn_stats(node->children[i], &local_stats, checks,
timeouts, suspends);
}
return p;
}
-
-void free_detection_option_tree(detection_option_tree_node_t* node)
-{
- int i;
- for (i=0; i<node->num_children; i++)
- {
- free_detection_option_tree(node->children[i]);
- }
- snort_free(node->children);
- snort_free(node->state);
- snort_free(node);
-}
-
#include "detection/rule_option_types.h"
#include "time/clock_defs.h"
-
#include "main/snort_debug.h"
+
extern Trace TRACE_NAME(detection);
namespace snort
{
+class HashNode;
+class XHash;
struct Packet;
struct SnortConfig;
-class XHash;
}
struct RuleLatencyState;
int detection_option_node_evaluate(
detection_option_tree_node_t*, detection_option_eval_data_t&, const class Cursor&);
-void DetectionHashTableFree(snort::XHash*);
-void DetectionTreeHashTableFree(snort::XHash*);
-
void print_option_tree(detection_option_tree_node_t*, int level);
void detection_option_tree_update_otn_stats(snort::XHash*);
#include "framework/mpse.h"
#include "framework/mpse_batch.h"
#include "hash/ghash.h"
+#include "hash/xhash.h"
#include "log/messages.h"
#include "main/snort.h"
#include "main/snort_config.h"
return;
/* Cleanup the detection option tree */
- DetectionHashTableFree(sc->detection_option_hash_table);
- DetectionTreeHashTableFree(sc->detection_option_tree_hash_table);
+ delete sc->detection_option_hash_table;
+ delete sc->detection_option_tree_hash_table;
fpFreeRuleMaps(sc);
ServicePortGroupMapFree(sc->spgmmTable);
#include "signature.h"
+#include "hash/hash_defs.h"
#include "hash/ghash.h"
#include "log/messages.h"
#include "main/snort_config.h"
key.sid = otn->sigInfo.sid;
int status = otn_map->insert(&key, otn);
- if ( status == GHASH_OK )
+ if ( status == HASH_OK )
return;
- assert(status == GHASH_INTABLE);
+ assert(status == HASH_INTABLE);
ParseError("duplicate rule with same gid (%u) and sid (%u)", key.gid, key.sid);
}
#include "tag.h"
#include "events/event.h"
+#include "hash/hash_defs.h"
#include "hash/xhash.h"
#include "log/messages.h"
#include "main/snort_config.h"
};
/* G L O B A L S **************************************************/
-static THREAD_LOCAL XHash* host_tag_cache_ptr = nullptr;
-
-// FIXIT-M utilize Flow instead of separate cache
-static THREAD_LOCAL XHash* ssn_tag_cache_ptr = nullptr;
-
static THREAD_LOCAL uint32_t last_prune_time = 0;
static THREAD_LOCAL uint32_t tag_alloc_faults = 0;
static THREAD_LOCAL uint32_t tag_memory_usage = 0;
// (consecutive) sessions to be captured.
static const unsigned s_max_sessions = 1;
+
/* P R O T O T Y P E S ********************************************/
static TagNode* TagAlloc(XHash*);
static void TagFree(XHash*, TagNode*);
-static int TagFreeSessionNodeFunc(void* key, void* data);
-static int TagFreeHostNodeFunc(void* key, void* data);
static int PruneTagCache(uint32_t, int);
static int PruneTime(XHash* tree, uint32_t thetime);
static void TagSession(Packet*, TagData*, uint32_t, uint16_t, void*);
static void AddTagNode(Packet*, TagData*, int, uint32_t, uint16_t, void*);
static inline void SwapTag(TagNode*);
+class TagSessionCache : public XHash
+{
+public:
+ TagSessionCache(int rows, int key_len)
+ : XHash(rows, key_len)
+ {
+ initialize();
+ anr_enabled = false;
+ recycle_nodes = false;
+ }
+
+ ~TagSessionCache() override
+ {
+ delete_hash_table();
+ }
+
+ void free_user_data(HashNode* hnode) override
+ {
+ TagFree(this, (TagNode*)hnode->data);
+ }
+};
+
+class TagHostCache : public XHash
+{
+public:
+ TagHostCache(int rows, int key_len)
+ : XHash(rows, key_len)
+ {
+ initialize();
+ anr_enabled = false;
+ recycle_nodes = false;
+ }
+
+ ~TagHostCache() override
+ {
+ delete_hash_table();
+ }
+
+ void free_user_data(HashNode* hnode) override
+ {
+ TagFree(this, (TagNode*)hnode->data);
+ }
+};
+
+static THREAD_LOCAL TagHostCache* host_tag_cache = nullptr;
+
+// FIXIT-M utilize Flow instead of separate cache
+static THREAD_LOCAL TagSessionCache* ssn_tag_cache = nullptr;
+
+
/**Calculated memory needed per node insertion into respective cache. Its includes
* memory needed for allocating TagNode, HashNode and key size.
*
*/
static inline unsigned int memory_per_node(XHash* hash)
{
- if ( hash == ssn_tag_cache_ptr )
+ if ( hash == ssn_tag_cache )
return sizeof(tTagFlowKey) + sizeof(HashNode) + sizeof(TagNode);
- else if ( hash == host_tag_cache_ptr )
+ else if ( hash == host_tag_cache )
return sizeof(SfIp) + sizeof(HashNode) + sizeof(TagNode);
return 0;
tag_memory_usage -= memory_per_node(hash);
}
-/**Callback from session tag cache to free user data.
- * @param key - pointer to key to session tag
- * @param data - pointer to user data, to be freed.
- * @returns 0
- */
-static int TagFreeSessionNodeFunc(void*, void* data)
-{
- TagFree(ssn_tag_cache_ptr, (TagNode*)data);
- return 0;
-}
-
-/**Callback from host tag cache to free user data.
- * @param key - pointer to key to session tag
- * @param data - pointer to user data, to be freed.
- * @returns 0
- */
-static int TagFreeHostNodeFunc(void*, void* data)
-{
- TagFree(host_tag_cache_ptr, (TagNode*)data);
- return 0;
-}
-
/**
* swap the sips and dips, dp's and sp's
*
{
unsigned int hashTableSize = TAG_MEMCAP/sizeof(TagNode);
- ssn_tag_cache_ptr = new XHash(hashTableSize, sizeof(tTagFlowKey), 0, 0,
- false, nullptr, TagFreeSessionNodeFunc, false);
-
- host_tag_cache_ptr = new XHash(hashTableSize, sizeof(SfIp), 0, 0, false,
- nullptr, TagFreeHostNodeFunc, false);
+ ssn_tag_cache = new TagSessionCache(hashTableSize, sizeof(tTagFlowKey));
+ host_tag_cache = new TagHostCache(hashTableSize, sizeof(SfIp));
}
void CleanupTag()
{
- delete ssn_tag_cache_ptr;
- delete host_tag_cache_ptr;
+ delete ssn_tag_cache;
+ delete host_tag_cache;
}
static void TagSession(Packet* p, TagData* tag, uint32_t time, uint16_t event_id, void* log_list)
}
if (mode == TAG_SESSION)
{
- tag_cache_ptr = ssn_tag_cache_ptr;
+ tag_cache_ptr = ssn_tag_cache;
}
else
{
- tag_cache_ptr = host_tag_cache_ptr;
+ tag_cache_ptr = host_tag_cache;
}
idx = TagAlloc(tag_cache_ptr);
char create_event = 1;
/* check for active tags */
- if (!host_tag_cache_ptr->get_node_count() && !ssn_tag_cache_ptr->get_node_count())
+ if (!host_tag_cache->get_num_nodes() && !ssn_tag_cache->get_num_nodes())
{
return 0;
}
idx.key.dp = p->ptrs.dp;
/* check for session tags... */
- returned = (TagNode*)ssn_tag_cache_ptr->get_user_data(&idx);
+ returned = (TagNode*)ssn_tag_cache->get_user_data(&idx);
if (returned == nullptr)
{
idx.key.dp = p->ptrs.sp;
idx.key.sp = p->ptrs.dp;
- returned = (TagNode*)ssn_tag_cache_ptr->get_user_data(&idx);
+ returned = (TagNode*)ssn_tag_cache->get_user_data(&idx);
if (returned == nullptr)
{
- returned = (TagNode*)host_tag_cache_ptr->get_user_data(&idx);
+ returned = (TagNode*)host_tag_cache->get_user_data(&idx);
if (returned == nullptr)
{
*/
idx.key.sip = *p->ptrs.ip_api.get_src();
- returned = (TagNode*)host_tag_cache_ptr->get_user_data(&idx);
+ returned = (TagNode*)host_tag_cache->get_user_data(&idx);
}
if (returned != nullptr)
{
- taglist = host_tag_cache_ptr;
+ taglist = host_tag_cache;
}
}
else
{
- taglist = ssn_tag_cache_ptr;
+ taglist = ssn_tag_cache;
}
}
else
{
- taglist = ssn_tag_cache_ptr;
+ taglist = ssn_tag_cache;
}
if (returned != nullptr)
if ( !returned->metric )
{
- if (taglist->release_node(returned) != HASH_OK)
+ if (taglist->release_node(&returned->key) != HASH_OK)
{
LogMessage("WARNING: failed to remove tagNode from hash.\n");
}
if (mustdie == 0)
{
- if (ssn_tag_cache_ptr->get_node_count() != 0)
- pruned = PruneTime(ssn_tag_cache_ptr, thetime);
+ if (ssn_tag_cache->get_num_nodes() != 0)
+ pruned = PruneTime(ssn_tag_cache, thetime);
- if (host_tag_cache_ptr->get_node_count() != 0)
- pruned += PruneTime(host_tag_cache_ptr, thetime);
+ if (host_tag_cache->get_num_nodes() != 0)
+ pruned += PruneTime(host_tag_cache, thetime);
}
else
{
while (pruned < mustdie &&
- (ssn_tag_cache_ptr->get_node_count() > 0 || host_tag_cache_ptr->get_node_count() > 0))
+ (ssn_tag_cache->get_num_nodes() > 0 || host_tag_cache->get_num_nodes() > 0))
{
- TagNode* lru_node;
+ if ( ssn_tag_cache->delete_lru_node() )
+ ++pruned;
+ else
+ LogMessage("WARNING: failed to remove tagNode from ssn hash.\n");
- if ((lru_node = (TagNode*)ssn_tag_cache_ptr->get_lru_user_data()) != nullptr)
- {
- if (ssn_tag_cache_ptr->release_node(lru_node) != HASH_OK)
- {
- LogMessage("WARNING: failed to remove tagNode from hash.\n");
- }
- pruned++;
- }
- if ((lru_node = (TagNode*)host_tag_cache_ptr->get_lru_user_data()) != nullptr)
- {
- if (host_tag_cache_ptr->release_node(lru_node) != HASH_OK)
- {
- LogMessage("WARNING: failed to remove tagNode from hash.\n");
- }
- pruned++;
- }
+ if ( host_tag_cache->delete_lru_node() )
+ ++pruned;
+ else
+ LogMessage("WARNING: failed to remove tagNode from host hash.\n");
}
}
{
if ((lru_node->last_access + TAG_PRUNE_QUANTUM) < thetime)
{
- if (tree->release_node(lru_node) != HASH_OK)
+ if (tree->release_node(&lru_node->key) != HASH_OK)
{
LogMessage("WARNING: failed to remove tagNode from hash.\n");
}
#include "file_cache.h"
+#include "hash/hash_defs.h"
#include "hash/xhash.h"
#include "log/messages.h"
#include "main/snort_config.h"
using namespace snort;
-static int file_cache_anr_free_func(void*, void* data)
+class ExpectedFileCache : public XHash
{
- FileCache::FileNode* node = (FileCache::FileNode*)data;
+public:
+ ExpectedFileCache(unsigned rows, unsigned key_len, unsigned datasize)
+ : XHash(rows, key_len, datasize, 0)
+ { }
- if (!node)
- return 0;
-
- struct timeval now;
- packet_gettimeofday(&now);
+ ~ExpectedFileCache() override
+ {
+ delete_hash_table();
+ }
- // only recycle expired nodes
- if (timercmp(&node->cache_expire_time, &now, <))
+ bool is_node_recovery_ok(HashNode* hnode) override
{
- delete node->file;
- return 0;
+ FileCache::FileNode* node = (FileCache::FileNode*)hnode->data;
+ if ( !node )
+ return true;
+
+ struct timeval now;
+ packet_gettimeofday(&now);
+ if ( timercmp(&node->cache_expire_time, &now, <) )
+ return true;
+ else
+ return false;
}
- else
- return 1;
-}
-static int file_cache_free_func(void*, void* data)
-{
- FileCache::FileNode* node = (FileCache::FileNode*)data;
- if (node)
+ void free_user_data(HashNode* hnode) override
{
- delete node->file;
+ FileCache::FileNode* node = (FileCache::FileNode*)hnode->data;
+ if ( node )
+ delete node->file;
}
- return 0;
-}
+};
// Return the time in ms since we started waiting for pending file lookup.
static int64_t time_elapsed_ms(struct timeval* now, struct timeval* expire_time, int64_t lookup_timeout)
FileCache::FileCache(int64_t max_files_cached)
{
max_files = max_files_cached;
- fileHash = new XHash(max_files, sizeof(FileHashKey), sizeof(FileNode),
- 0, true, file_cache_anr_free_func, file_cache_free_func, true);
+ fileHash = new ExpectedFileCache(max_files, sizeof(FileHashKey), sizeof(FileNode));
fileHash->set_max_nodes(max_files);
}
{
std::lock_guard<std::mutex> lock(cache_mutex);
- if (!fileHash->get_node_count())
- {
+ if ( !fileHash->get_num_nodes() )
return nullptr;
- }
HashNode* hash_node = fileHash->find_node(&hashKey);
-
- if (!hash_node)
+ if ( !hash_node )
return nullptr;
FileNode* node = (FileNode*)hash_node->data;
- if (!node)
+ if ( !node )
{
fileHash->release_node(hash_node);
return nullptr;
struct timeval now;
packet_gettimeofday(&now);
- if (timercmp(&node->cache_expire_time, &now, <))
+ if ( timercmp(&node->cache_expire_time, &now, <) )
{
fileHash->release_node(hash_node);
return nullptr;
#include "file_config.h"
-namespace snort
-{
-class XHash;
-}
+class ExpectedFileCache;
class FileCache
{
int store_verdict(snort::Flow*, snort::FileInfo*, int64_t timeout);
/* The hash table of expected files */
- snort::XHash* fileHash = nullptr;
+ ExpectedFileCache* fileHash = nullptr;
int64_t block_timeout = DEFAULT_FILE_BLOCK_TIMEOUT;
int64_t lookup_timeout = DEFAULT_FILE_LOOKUP_TIMEOUT;
int64_t max_files = DEFAULT_MAX_FILES_CACHED;
#include <algorithm>
#include <cassert>
+#include "hash/ghash.h"
#include "log/messages.h"
#include "utils/util.h"
#include <list>
#include <vector>
-#include "hash/ghash.h"
-
#include "file_lib.h"
+namespace snort
+{
+class GHash;
+}
+
#define MAX_BRANCH (UINT8_MAX + 1)
enum IdNodeState
{
if (Snort::is_reloading() && !FileService::is_file_capture_enabled())
{
- ReloadError("Enabling file capture requires a restart\n");
+ ReloadError("Enabling file_id.enable_capture requires a restart\n");
return false;
}
fp.set_file_capture(true);
if (file_rule.use.capture_enabled && Snort::is_reloading()
&& !FileService::is_file_capture_enabled())
{
- ReloadError("Enabling file capture requires a restart\n");
+ ReloadError("Enabling file_id.enable_file_capture requires a restart\n");
return false;
}
}
return;
if (max_files_cached != conf->max_files_cached)
- ReloadError("Changing file_id:max_files_cached requires a restart\n");
+ ReloadError("Changing file_id.max_files_cached requires a restart\n");
if (file_capture_enabled)
{
if (capture_memcap != conf->capture_memcap)
- ReloadError("Changing file_id:capture_memcap requires a restart\n");
+ ReloadError("Changing file_id.capture_memcap requires a restart\n");
if (capture_block_size != conf->capture_block_size)
- ReloadError("Changing file_id:capture_block_size requires a restart\n");
+ ReloadError("Changing file_id.capture_block_size requires a restart\n");
}
}
#include "main/thread.h"
#include "detection/rules.h"
#include "hash/ghash.h"
+#include "hash/hash_defs.h"
#include "hash/xhash.h"
#include "sfip/sf_ip.h"
#include "sfip/sf_ipvar.h"
/* Calc max ip nodes for this memory */
if ( nbytes < SFRF_BYTES )
- {
nbytes = SFRF_BYTES;
- }
+
nrows = nbytes / (SFRF_BYTES);
/* Create global hash table for all of the IP Nodes */
- rf_hash = new XHash(nrows, sizeof(tSFRFTrackingNodeKey),
- sizeof(tSFRFTrackingNode), nbytes, true, nullptr, nullptr, true);
+ rf_hash = new XHash(nrows, sizeof(tSFRFTrackingNodeKey), sizeof(tSFRFTrackingNode), nbytes);
}
void SFRF_Delete()
void SFRF_Flush()
{
if ( rf_hash )
- rf_hash->clear();
+ rf_hash->clear_hash();
}
static void SFRF_ConfigNodeFree(void* item)
key.policyId = get_ips_policy()->policy_id;
key.padding = 0;
- /*
- * Check for any Permanent sid objects for this gid or add this one ...
- */
- HashNode* hnode = rf_hash->get_node((void*)&key);
- if ( !hnode )
+ // Check for any Permanent sid objects for this gid or add this one ...
+ if ( rf_hash->insert(&key, nullptr) == HASH_NOMEM )
{
// xhash_get_node fails to insert only if rf_hash is full.
rate_filter_stats.xhash_nomem_peg++;
return dynNode;
}
- if ( hnode->data )
+ dynNode = (tSFRFTrackingNode*)rf_hash->get_user_data();
+ if ( dynNode->filterState == FS_NEW )
{
- dynNode = (tSFRFTrackingNode*)hnode->data;
-
- if ( dynNode->filterState == FS_NEW )
- {
- // first time initialization
- dynNode->tstart = curTime;
+ // first time initialization
+ dynNode->tstart = curTime;
#ifdef SFRF_OVER_RATE
- dynNode->tlast = curTime;
+ dynNode->tlast = curTime;
#endif
- dynNode->filterState = FS_OFF;
- }
+ dynNode->filterState = FS_OFF;
}
+
return dynNode;
}
#include <cassert>
#include "hash/ghash.h"
+#include "hash/hash_defs.h"
#include "hash/xhash.h"
#include "main/thread.h"
#include "sfip/sf_ipvar.h"
nbytes = size;
nrows = nbytes / size;
- return new XHash(nrows, key, data, nbytes, true, nullptr, nullptr, true);
+ return new XHash(nrows, key, data, nbytes);
}
/*!
if (status == HASH_INTABLE)
{
/* Already in the table */
- sfthd_ip_node = (THD_IP_NODE*)local_hash->get_cnode()->data;
+ sfthd_ip_node = (THD_IP_NODE*)local_hash->get_user_data();
/* Increment the event count */
sfthd_ip_node->count++;
if (status == HASH_INTABLE)
{
/* Already in the table */
- sfthd_ip_node = (THD_IP_NODE*)global_hash->get_cnode()->data;
+ sfthd_ip_node = (THD_IP_NODE*)global_hash->get_user_data();
/* Increment the event count */
sfthd_ip_node->count++;
for (unsigned i = 0; i < MAX_PRUNE; ++i )
{
- ExpectNode* node = (ExpectNode*)hash_table->first();
+ ExpectNode* node = (ExpectNode*)hash_table->lru_first();
if ( !node || now <= node->expires )
break;
ExpectNode* ExpectCache::find_node_by_packet(Packet* p, FlowKey &key)
{
- if (!hash_table->get_count())
+ if (!hash_table->get_num_nodes())
return nullptr;
const SfIp* srcIP = p->ptrs.ip_api.get_src();
*/
// FIXIT-P X This should be optimized to only do full matches when full keys
// are present, likewise for partial keys.
- ExpectNode* node = (ExpectNode*) hash_table->find(&key);
+ ExpectNode* node = (ExpectNode*) hash_table->get_user_data(&key);
if (!node)
{
// FIXIT-M X This logic could fail if IPs were equal because the original key
port2 = key.port_h;
key.port_h = 0;
}
- node = (ExpectNode*) hash_table->find(&key);
+ node = (ExpectNode*) hash_table->get_user_data(&key);
if (!node)
{
key.port_l = port1;
key.port_h = port2;
- node = (ExpectNode*) hash_table->find(&key);
+ node = (ExpectNode*) hash_table->get_user_data(&key);
if (!node)
return nullptr;
}
{
if (node->head)
node->clear(free_list);
- hash_table->release(&key);
+ hash_table->release_node(&key);
return nullptr;
}
/* Make sure the packet direction is correct */
lws->ssn_state.snort_protocol_id = node->snort_protocol_id;
if (!node->count)
- hash_table->release(&key);
+ hash_table->release_node(&key);
return ignoring;
}
{
// -size forces use of abs(size) ie w/o bumping up
hash_table = new ZHash(-MAX_HASH, sizeof(FlowKey));
- hash_table->set_key_opcodes(FlowKey::hash, FlowKey::is_equal);
-
nodes = new ExpectNode[max];
for (unsigned i = 0; i < max; ++i)
- hash_table->push(nodes+i);
+ hash_table->push(nodes + i);
/* Preallocate a pool of ExpectFlows big enough to handle the worst case
requirement (max number of nodes * max flows per node) and add them all
uint16_t vlanId = (ctrlPkt->proto_bits & PROTO_BIT__VLAN) ? layer::get_vlan_layer(ctrlPkt)->vid() : 0;
uint32_t mplsId = (ctrlPkt->proto_bits & PROTO_BIT__MPLS) ? ctrlPkt->ptrs.mplsHdr.label : 0;
uint16_t addressSpaceId = ctrlPkt->pkth->address_space_id;
-
FlowKey key;
bool reversed_key = key.init(type, ip_proto, cliIP, cliPort, srvIP, srvPort,
vlanId, mplsId, addressSpaceId);
bool new_node = false;
- ExpectNode* node = (ExpectNode*) hash_table->get(&key, &new_node);
- if (!node)
+ ExpectNode* node = (ExpectNode*) hash_table->get_user_data(&key);
+ if ( !node )
{
prune();
- node = (ExpectNode*) hash_table->get(&key, &new_node);
- /* The flow free list should never be empty if there was a node
- to be (re-)used unless we managed to leak some. Check just
- in case. Maybe assert instead? */
- if (!node || !free_list)
- {
- ++overflows;
- return -1;
- }
+ node = (ExpectNode*) hash_table->get(&key);
+ assert(node);
+ new_node = true;
}
-
- /* If the node is past its expiration date, whack it and reuse it. */
- if (!new_node && packet_time() > node->expires)
+ else if ( packet_time() > node->expires )
{
+ // node is past its expiration date, whack it and reuse it.
node->clear(free_list);
new_node = true;
}
ExpectFlow* last = nullptr;
- if (!new_node)
+ if ( !new_node )
{
- // Requests will be rejected if the snort_protocol_id doesn't
- // match what has already been set.
- if (node->snort_protocol_id != snort_protocol_id)
+ // reject if the snort_protocol_id doesn't match
+ if ( node->snort_protocol_id != snort_protocol_id )
{
- if (node->snort_protocol_id && snort_protocol_id)
+ if ( node->snort_protocol_id && snort_protocol_id )
return -1;
node->snort_protocol_id = snort_protocol_id;
}
last = node->tail;
- if (last)
+ if ( last )
{
FlowData* lfd = last->data;
-
- while (lfd)
+ while ( lfd )
{
- if (lfd->get_id() == fd->get_id())
+ if ( lfd->get_id() == fd->get_id() )
{
last = nullptr;
break;
}
bool new_expect_flow = false;
- if (!last)
+ if ( !last )
{
- if (node->count >= MAX_LIST)
+ if ( node->count >= MAX_LIST )
{
// fail when maxed out
++overflows;
last = free_list;
free_list = free_list->next;
- if (!node->tail)
+ if ( !node->tail )
node->head = last;
else
node->tail->next = last;
last->add_flow_data(fd);
node->expires = packet_time() + MAX_WAIT;
++expects;
- if (new_expect_flow)
+ if ( new_expect_flow )
{
// chain all expected flows created by this packet
packet_expect_flows->emplace_back(last);
FlowCache::FlowCache(const FlowCacheConfig& cfg) : config(cfg)
{
hash_table = new ZHash(config.max_flows, sizeof(FlowKey));
- hash_table->set_key_opcodes(FlowKey::hash, FlowKey::is_equal);
-
uni_flows = new FlowUniList;
uni_ip_flows = new FlowUniList;
-
flags = 0x0;
assert(prune_stats.get_total() == 0);
unsigned FlowCache::get_count()
{
- return hash_table ? hash_table->get_count() : 0;
+ return hash_table ? hash_table->get_num_nodes() : 0;
}
Flow* FlowCache::find(const FlowKey* key)
{
- Flow* flow = (Flow*)hash_table->find(key);
+ Flow* flow = (Flow*)hash_table->get_user_data(key);
if ( flow )
{
{
time_t timestamp = packet_time();
Flow* flow = (Flow*)hash_table->get(key);
-
if ( !flow )
{
if ( flows_allocated < config.max_flows )
// FIXIT-M This check is added for offload case where both Flow::reset
// and Flow::retire try remove the flow from hash. Flow::reset should
// just mark the flow as pending instead of trying to remove it.
- if ( hash_table->release(flow->key) )
+ if ( !hash_table->release_node(flow->key) )
memory::MemoryCap::update_deallocations(config.proto[to_utype(flow->key->pkt_type)].cap_weight);
}
ActiveSuspendContext act_susp;
unsigned pruned = 0;
- auto flow = static_cast<Flow*>(hash_table->first());
+ auto flow = static_cast<Flow*>(hash_table->lru_first());
while ( flow and pruned <= cleanup_flows )
{
if ( hash_table->get_count() == 1 )
break;
- hash_table->touch();
+ hash_table->lru_touch();
}
#else
// Reached the current flow. This *should* be the newest flow
release(flow, PruneReason::IDLE);
++pruned;
- flow = static_cast<Flow*>(hash_table->first());
+ flow = static_cast<Flow*>(hash_table->lru_first());
}
return pruned;
// initially skip offloads but if that doesn't work the hash table is iterated from the
// beginning again. prune offloads at that point.
- unsigned ignore_offloads = hash_table->get_count();
+ unsigned ignore_offloads = hash_table->get_num_nodes();
- while ( hash_table->get_count() > max_cap and hash_table->get_count() > blocks )
+ while ( hash_table->get_num_nodes() > max_cap and hash_table->get_num_nodes() > blocks )
{
- auto flow = static_cast<Flow*>(hash_table->first());
+ auto flow = static_cast<Flow*>(hash_table->lru_first());
assert(flow); // holds true because hash_table->get_count() > 0
if ( (save_me and flow == save_me) or flow->was_blocked() or
// FIXIT-M we should update last_data_seen upon touch to ensure
// the hash_table LRU list remains sorted by time
- if ( !hash_table->touch() )
- break;
+ hash_table->lru_touch();
}
else
{
--ignore_offloads;
}
- if (!pruned and hash_table->get_count() > max_cap)
+ if (!pruned and hash_table->get_num_nodes() > max_cap)
{
prune_one(PruneReason::EXCESS, true);
++pruned;
bool FlowCache::prune_one(PruneReason reason, bool do_cleanup)
{
// so we don't prune the current flow (assume current == MRU)
- if ( hash_table->get_count() <= 1 )
+ if ( hash_table->get_num_nodes() <= 1 )
return false;
// ZHash returns in LRU order, which is updated per packet via find --> move_to_front call
- auto flow = static_cast<Flow*>(hash_table->first());
+ auto flow = static_cast<Flow*>(hash_table->lru_first());
assert(flow);
flow->ssn_state.session_flags |= SSNFLAG_PRUNED;
ActiveSuspendContext act_susp;
unsigned retired = 0;
- auto flow = static_cast<Flow*>(hash_table->current());
+ auto flow = static_cast<Flow*>(hash_table->lru_current());
if ( !flow )
- flow = static_cast<Flow*>(hash_table->first());
+ flow = static_cast<Flow*>(hash_table->lru_first());
while ( flow and (retired < num_flows) )
{
if ( HighAvailabilityManager::in_standby(flow) or
flow->is_suspended() )
{
- flow = static_cast<Flow*>(hash_table->next());
+ flow = static_cast<Flow*>(hash_table->lru_next());
continue;
}
++retired;
- flow = static_cast<Flow*>(hash_table->current());
+ flow = static_cast<Flow*>(hash_table->lru_current());
}
return retired;
unsigned FlowCache::delete_active_flows(unsigned mode, unsigned num_to_delete, unsigned &deleted)
{
- unsigned flows_to_check = hash_table->get_count();
+ unsigned flows_to_check = hash_table->get_num_nodes();
while ( num_to_delete && flows_to_check-- )
{
- auto flow = static_cast<Flow*>(hash_table->first());
+ auto flow = static_cast<Flow*>(hash_table->lru_first());
assert(flow);
if ( (mode == ALLOWED_FLOWS_ONLY and (flow->was_blocked() || flow->is_suspended()))
or (mode == OFFLOADED_FLOWS_TOO and flow->was_blocked()) )
{
- if ( !hash_table->touch() )
- break;
-
+ hash_table->lru_touch();
continue;
}
// we have a winner...
- hash_table->remove(flow->key);
+ hash_table->remove();
if ( flow->next )
unlink_uni(flow);
unsigned retired = 0;
- while ( auto flow = static_cast<Flow*>(hash_table->first()) )
+ while ( auto flow = static_cast<Flow*>(hash_table->lru_first()) )
{
retire(flow);
++retired;
#include "flow/flow_key.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "main/snort_config.h"
#include "protocols/icmp4.h"
#include "protocols/icmp6.h"
// hash foo
//-------------------------------------------------------------------------
-uint32_t FlowKey::hash(HashFnc* hf, const unsigned char* p, int)
-{
- uint32_t a, b, c;
- a = b = c = hf->hardener;
-
- const uint32_t* d = (const uint32_t*)p;
-
- a += d[0]; // IPv6 lo[0]
- b += d[1]; // IPv6 lo[1]
- c += d[2]; // IPv6 lo[2]
-
- mix(a, b, c);
-
- a += d[3]; // IPv6 lo[3]
- b += d[4]; // IPv6 hi[0]
- c += d[5]; // IPv6 hi[1]
-
- mix(a, b, c);
-
- a += d[6]; // IPv6 hi[2]
- b += d[7]; // IPv6 hi[3]
- c += d[8]; // mpls label
-
- mix(a, b, c);
-
- a += d[9]; // port lo & port hi
- b += d[10]; // vlan tag, address space id
- c += d[11]; // ip_proto, pkt_type, version, and 8 bits of zeroed pad
-
- finalize(a, b, c);
-
- return c;
-}
-
bool FlowKey::is_equal(const void* s1, const void* s2, size_t)
{
- const uint64_t* a,* b;
-
- a = (const uint64_t*)s1;
- b = (const uint64_t*)s2;
+ const uint64_t* a = (const uint64_t*)s1;
+ const uint64_t* b = (const uint64_t*)s2;
if (*a - *b)
return false; /* Compares IPv4 lo/hi
Compares IPv6 low[0,1] */
return true;
}
+unsigned FlowHashKeyOps::do_hash(const unsigned char* k, int)
+{
+ uint32_t a, b, c;
+ a = b = c = hardener;
+
+ const uint32_t* d = (const uint32_t*)k;
+
+ a += d[0]; // IPv6 lo[0]
+ b += d[1]; // IPv6 lo[1]
+ c += d[2]; // IPv6 lo[2]
+
+ mix(a, b, c);
+
+ a += d[3]; // IPv6 lo[3]
+ b += d[4]; // IPv6 hi[0]
+ c += d[5]; // IPv6 hi[1]
+
+ mix(a, b, c);
+
+ a += d[6]; // IPv6 hi[2]
+ b += d[7]; // IPv6 hi[3]
+ c += d[8]; // mpls label
+
+ mix(a, b, c);
+
+ a += d[9]; // port lo & port hi
+ b += d[10]; // vlan tag, address space id
+ c += d[11]; // ip_proto, pkt_type, version, and 8 bits of zeroed pad
+
+ finalize(a, b, c);
+
+ return c;
+}
+
+bool FlowHashKeyOps::key_compare(const void* k1, const void* k2, size_t len)
+{
+ return FlowKey::is_equal(k1, k2, len);
+}
+
+
#include <cstdint>
#include "framework/decode_data.h"
+#include "hash/hash_key_operations.h"
#include "utils/cpp_macros.h"
-struct HashFnc;
+class HashKeyOperations;
namespace snort
{
struct SfIp;
+class FlowHashKeyOps : public HashKeyOperations
+{
+public:
+ FlowHashKeyOps(int rows)
+ : HashKeyOperations(rows)
+ { }
+
+ unsigned do_hash(const unsigned char* k, int len) override;
+ bool key_compare(const void* k1, const void* k2, size_t) override;
+};
+
+
PADDING_GUARD_BEGIN
struct SO_PUBLIC FlowKey
{
void init_address_space(uint16_t);
// If this data structure changes size, compare must be updated!
- static uint32_t hash(HashFnc*, const unsigned char* d, int);
- static bool is_equal(const void* s1, const void* s2, size_t);
+ static bool is_equal(const void* k1, const void* k2, size_t);
private:
bool init4(
../flow_cache.cc
../flow_control.cc
../flow_key.cc
- ../../hash/zhash.cc
- ../../hash/hashfcn.cc
+ ../../hash/hash_key_operations.cc
+ ../../hash/hash_lru_cache.cc
../../hash/primetable.cc
+ ../../hash/xhash.cc
+ ../../hash/zhash.cc
)
add_cpputest( session_test )
#include <cstring>
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
using namespace snort;
set (HASH_INCLUDES
+ ghash.h
hashes.h
hash_defs.h
- ghash.h
- xhash.h
- hashfcn.h
+ hash_key_operations.h
lru_cache_shared.h
+ xhash.h
)
add_library( hash OBJECT
${HASH_INCLUDES}
+ ghash.cc
hashes.cc
+ hash_lru_cache.cc
+ hash_lru_cache.h
+ hash_key_operations.cc
lru_cache_shared.cc
- ghash.cc
- hashfcn.cc
- primetable.cc
- primetable.h
- xhash.cc
- zhash.cc
+ primetable.cc
+ primetable.h
+ xhash.cc
+ zhash.cc
zhash.h
)
#include "utils/util.h"
+#include "hash_defs.h"
#include "primetable.h"
namespace snort
else
nrows = -nrows_;
- hashfcn = hashfcn_new(nrows);
+ hashfcn = new HashKeyOperations(nrows);
table = (GHashNode**)snort_calloc(nrows, sizeof(GHashNode*));
for ( int i = 0; i < nrows; i++ )
table[i] = nullptr;
GHash::~GHash()
{
- hashfcn_free(hashfcn);
-
for (int i = 0; i < nrows; i++)
for ( GHashNode* node = table[i]; node; )
{
}
snort_free(table);
+ delete hashfcn;
}
-// set key length, hashkey, and index parameters required to find/add/remove a node
-// the parameters set are valid until for the life of the initial method called
-void GHash::set_node_parameters(const void* const key)
-{
- klen = ( keysize > 0 ) ? keysize : strlen((const char*)key) + 1;
- hashkey = hashfcn->hash_fcn(hashfcn, (const unsigned char*)key, klen);
- index = hashkey % nrows;
-}
-
-GHashNode* GHash::find_node(const void* const key)
+GHashNode* GHash::find_node(const void* const key, unsigned index)
{
assert(key);
- set_node_parameters(key);
for ( GHashNode* hnode = table[index]; hnode; hnode = hnode->next )
{
if ( keysize == 0 )
}
else
{
- if ( hashfcn->keycmp_fcn(hnode->key, key, keysize) )
+ if ( hashfcn->key_compare(hnode->key, key, keysize) )
return hnode;
}
}
{
assert(key && data);
- if ( GHashNode* hnode = find_node(key) )
+ unsigned index = get_index(key);
+ if ( GHashNode* hnode = find_node(key, index) )
{
cnode = hnode;
- return GHASH_INTABLE;
+ return HASH_INTABLE;
}
GHashNode* hnode = (GHashNode*)snort_calloc(sizeof(GHashNode));
}
else
{
+ unsigned klen = get_key_length(key);
hnode->key = snort_alloc(klen);
memcpy(const_cast<void*>(hnode->key), key, klen);
}
count++;
- return GHASH_OK;
+ return HASH_OK;
}
void* GHash::find(const void* const key)
{
assert(key);
- GHashNode* hnode = find_node(key);
+ unsigned index = get_index(key);
+ GHashNode* hnode = find_node(key, index);
if ( hnode )
return hnode->data;
snort_free(hnode);
count--;
- return GHASH_OK;
+ return HASH_OK;
}
int GHash::remove(const void* const key)
{
assert(key);
- if ( GHashNode* hnode = find_node(key) )
+ unsigned index = get_index(key);
+ if ( GHashNode* hnode = find_node(key, index) )
return free_node(index, hnode);
else
- return GHASH_NOT_FOUND;
+ return HASH_NOT_FOUND;
}
void GHash::next()
return n;
}
-void GHash::set_key_opcodes(hash_func hash_fcn, keycmp_func keycmp_fcn)
+void GHash::set_hashkey_ops(HashKeyOperations* hk)
{
- hashfcn_set_keyops(hashfcn, hash_fcn, keycmp_fcn);
+ delete hashfcn;
+ hashfcn = hk;
}
}
// generic hash table - stores and maps key + data pairs
+#include <string.h>
+#include "hash_key_operations.h"
#include "main/snort_types.h"
-#include "hashfcn.h"
-
namespace snort
{
-#define GHASH_NOT_FOUND (-1)
-#define GHASH_OK 0
-#define GHASH_INTABLE 1
-
struct GHashNode
{
struct GHashNode* next;
struct GHashNode* prev;
- const void* key; /* Copy of, or Pointer to, the Users key */
- void* data; /* The users data, this is never copied! */
+ const void* key;
+ void* data;
};
typedef void (* gHashFree)(void*);
void* find(const void* const key);
GHashNode* find_first();
GHashNode* find_next();
- void set_key_opcodes(hash_func, keycmp_func);
+ void set_hashkey_ops(HashKeyOperations*);
unsigned get_count() const
{ return count; }
private:
- void set_node_parameters(const void* const key);
- GHashNode* find_node(const void* const key);
+ GHashNode* find_node(const void* const key, unsigned index);
int free_node(unsigned index, GHashNode*);
void next();
+ unsigned get_key_length(const void* const key)
+ { return ( keysize > 0 ) ? keysize : strlen((const char*)key) + 1; }
+
+ unsigned get_index(const void* const key)
+ {
+ unsigned hashkey = hashfcn->do_hash((const unsigned char*)key, get_key_length(key));
+ return hashkey % nrows;
+ }
+
unsigned keysize; // bytes in key, if < 0 -> keys are strings
bool userkey; // user owns the key */
gHashFree userfree;
int nrows; // # rows int the hash table use a prime number 211, 9871
- HashFnc* hashfcn;
+ HashKeyOperations* hashfcn;
GHashNode** table; // array of node ptr's
unsigned count; // total # nodes in table
int crow; // findfirst/next row in table
GHashNode* cnode; // findfirst/next node ptr
- // node parameters for search/add/remove
- unsigned klen = 0;
- unsigned hashkey = 0;
- int index = 0;
};
#ifndef HASH_DEFS_H
#define HASH_DEFS_H
+#include "hash_key_operations.h"
#include "main/snort_types.h"
-#include "utils/sfmemcap.h"
-
-#include "hashfcn.h"
namespace snort
{
-#define HASH_NOMEM (-2)
-#define HASH_ERR (-1)
+#define HASH_NOMEM (-2)
+#define HASH_NOT_FOUND (-1)
#define HASH_OK 0
#define HASH_INTABLE 1
#define HASH_PENDING 2
-struct HashNode
+class HashNode
{
- struct HashNode* gnext; // global node list - used for aging nodes
- struct HashNode* gprev;
- struct HashNode* next; // row node list
- struct HashNode* prev;
- int rindex; // row index of table this node belongs to.
- void* key; // Pointer to the key.
- void* data; // Pointer to the users data, this is not copied !
+public:
+ HashNode* gnext; // lru or free node list
+ HashNode* gprev;
+ HashNode* next; // hash row node list
+ HashNode* prev;
+ void* key;
+ void* data;
+ int rindex;
};
-
-typedef int (* Hash_FREE_FCN)(void* key, void* data);
}
#endif
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//--------------------------------------------------------------------------
-/*
- hashfcn.c
-
- Each hash table allocates it's own GHash struct, using the number of
- rows in the hash table to modulo the random values.
-
- Updates:
-
- 8/31/2006 - man - changed to use sfprimetable.c
-*/
-
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
-#include "hashfcn.h"
+#include "hash_key_operations.h"
#include <cassert>
using namespace snort;
-static bool hashfcn_key_compare(const void* k1, const void* k2, size_t len)
-{
- if ( memcmp(k1, k2, len ) == 0 )
- return true;
- else
- return false;
-}
-
-HashFnc* hashfcn_new(int m)
+HashKeyOperations::HashKeyOperations(int rows)
{
- HashFnc* p;
- static int one = 1;
+ static bool one = true;
if ( one ) /* one time init */
{
srand( (unsigned)time(nullptr) );
- one = 0;
+ one = false;
}
- p = (HashFnc*)snort_calloc(sizeof(*p));
-
if ( SnortConfig::static_hash() )
{
- p->seed = 3193;
- p->scale = 719;
- p->hardener = 133824503;
+ seed = 3193;
+ scale = 719;
+ hardener = 133824503;
}
else
{
- p->seed = nearest_prime( (rand() % m) + 3191);
- p->scale = nearest_prime( (rand() % m) + 709);
- p->hardener = ((unsigned) rand() * rand()) + 133824503;
+ seed = nearest_prime( (rand() % rows) + 3191);
+ scale = nearest_prime( (rand() % rows) + 709);
+ hardener = ((unsigned) rand() * rand()) + 133824503;
}
-
- p->hash_fcn = &hashfcn_hash;
- p->keycmp_fcn = &hashfcn_key_compare;
-
- return p;
-}
-
-void hashfcn_free(HashFnc* p)
-{
- if ( p )
- snort_free(p);
}
-unsigned hashfcn_hash(HashFnc* p, const unsigned char* d, int n)
+unsigned HashKeyOperations::do_hash(const unsigned char* key, int len)
{
- unsigned hash = p->seed;
- while ( n )
+ unsigned hash = seed;
+ while ( len )
{
- hash *= p->scale;
- hash += *d++;
- n--;
+ hash *= scale;
+ hash += *key++;
+ len--;
}
- return hash ^ p->hardener;
+ return hash ^ hardener;
}
-/**
- * Make hashfcn use a separate set of opcodes for the backend.
- *
- * @param h hashfcn ptr
- * @param hash_fcn user specified hash function
- * @param keycmp_fcn user specified key comparison function
- */
-void hashfcn_set_keyops(HashFnc* h, hash_func hash_fcn, keycmp_func keycmp_fcn)
+bool HashKeyOperations::key_compare(const void* key1, const void* key2, size_t len)
{
- assert(h && hash_fcn && keycmp_fcn);
-
- h->hash_fcn = hash_fcn;
- h->keycmp_fcn = keycmp_fcn;
+ if ( memcmp(key1, key2, len) )
+ return false;
+ else
+ return true;
}
namespace snort
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//--------------------------------------------------------------------------
-#ifndef HASHFCN_H
-#define HASHFCN_H
+#ifndef HASH_KEY_OPERATIONS_H
+#define HASH_KEY_OPERATIONS_H
#include "main/snort_types.h"
return nrows;
}
-}
-
-struct HashFnc;
+class HashKeyOperations
+{
+public:
+ HashKeyOperations(int rows);
+ virtual ~HashKeyOperations()
+ { }
-typedef uint32_t (* hash_func)(HashFnc*, const unsigned char* d, int n);
-typedef bool (* keycmp_func)(const void* s1, const void* s2, size_t n);
+ virtual unsigned do_hash(const unsigned char* key, int len);
+ virtual bool key_compare(const void* key1, const void* key2, size_t len);
-struct HashFnc
-{
+protected:
unsigned seed;
unsigned scale;
unsigned hardener;
- hash_func hash_fcn;
- keycmp_func keycmp_fcn;
};
-
-HashFnc* hashfcn_new(int nrows);
-void hashfcn_free(HashFnc*);
-unsigned hashfcn_hash(HashFnc*, const unsigned char* d, int n);
-void hashfcn_set_keyops(HashFnc*, hash_func, keycmp_func);
+}
#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+
+// hash_lru_cache.cc author davis mcpherson davmcphe@cisco.com
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "hash_lru_cache.h"
+
+#include <cassert>
+#include "utils/util.h"
+
+using namespace snort;
+
+HashLruCache::HashLruCache()
+{
+ head = nullptr;
+ tail = nullptr;
+}
+
+void HashLruCache::insert(HashNode* hnode)
+{
+ if ( head )
+ {
+ hnode->gprev = nullptr;
+ hnode->gnext = head;
+ head->gprev = hnode;
+ head = hnode;
+ }
+ else
+ {
+ hnode->gprev = nullptr;
+ hnode->gnext = nullptr;
+ head = hnode;
+ tail = hnode;
+ }
+}
+
+void HashLruCache::touch(HashNode* hnode)
+{
+ if ( hnode == cursor )
+ cursor = hnode->gprev;
+
+ if ( hnode != head )
+ {
+ remove_node(hnode);
+ insert(hnode);
+ }
+}
+
+void HashLruCache::remove_node(HashNode* hnode)
+{
+ if ( cursor == hnode )
+ cursor = hnode->gprev;
+
+ if ( head == hnode )
+ {
+ head = head->gnext;
+ if ( head )
+ head->gprev = nullptr;
+ }
+
+ if ( hnode->gprev )
+ hnode->gprev->gnext = hnode->gnext;
+ if ( hnode->gnext )
+ hnode->gnext->gprev = hnode->gprev;
+
+ if ( tail == hnode )
+ tail = hnode->gprev;
+}
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+
+// hash_lru_cache.h author davis mcpherson davmcphe@cisco.com
+
+#ifndef HASH_LRU_CACHE_H
+#define HASH_LRU_CACHE_H
+
+#include "hash_defs.h"
+
+class HashLruCache
+{
+public:
+ HashLruCache();
+
+ void insert(snort::HashNode*);
+ void touch(snort::HashNode*);
+ void remove_node(snort::HashNode*);
+
+ snort::HashNode* get_lru_node()
+ {
+ cursor = tail;
+ return cursor;
+ }
+
+ snort::HashNode* get_next_lru_node()
+ {
+ if ( cursor )
+ cursor = cursor->gprev;
+ return cursor;
+ }
+
+ snort::HashNode* get_current_node()
+ { return cursor; }
+
+ void* get_mru_user_data()
+ { return ( head ) ? head->data : nullptr; }
+
+ void* get_lru_user_data()
+ { return ( tail ) ? tail->data : nullptr; }
+
+ snort::HashNode* remove_lru_node()
+ {
+ snort::HashNode* hnode = tail;
+ if ( hnode )
+ remove_node(hnode);
+
+ return hnode;
+ }
+
+private:
+ snort::HashNode* head = nullptr;
+ snort::HashNode* tail = nullptr;
+ snort::HashNode* cursor = nullptr;
+};
+
+#endif
+
SOURCES ../lru_cache_shared.cc
)
+add_cpputest( hash_lru_cache_test
+ SOURCES ../hash_lru_cache.cc
+)
+
add_cpputest( xhash_test
SOURCES
- ../xhash.cc
- ../hashfcn.cc
+ ../hash_key_operations.cc
+ ../hash_lru_cache.cc
../primetable.cc
- ../../utils/sfmemcap.cc
+ ../xhash.cc
)
add_cpputest( ghash_test
SOURCES
../ghash.cc
- ../hashfcn.cc
+ ../hash_key_operations.cc
+ ../hash_lru_cache.cc
../primetable.cc
)
+
+add_cpputest( zhash_test
+ SOURCES
+ ../hash_key_operations.cc
+ ../hash_lru_cache.cc
+ ../primetable.cc
+ ../xhash.cc
+ ../zhash.cc
+)
#include "hash/ghash.h"
+#include "hash/hash_defs.h"
#include "main/snort_config.h"
#include "utils/util.h"
snprintf(str, sizeof(str), "KeyWord%d",1);
str[sizeof(str) - 1] = '\0';
i = t->insert(str, (void *)(str + (1)));
- CHECK(i == GHASH_INTABLE);
+ CHECK(i == HASH_INTABLE);
// find those nodes
for (i=num-1; i>=0; i--)
CHECK(t->find(str) == nullptr);
// try to remove a node that is not in the table
- CHECK(t->remove( str) == GHASH_NOT_FOUND);
+ CHECK(t->remove( str) == HASH_NOT_FOUND);
for ( GHashNode* n = t->find_first(); n; n = t->find_next() )
{
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+
+// hash_lru_cache_test.cc author davis mcpherson <davmcphe@cisco.com>
+// unit tests for the HashLruCache class
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "../hash_lru_cache.h"
+
+#include "../../utils/util.h"
+
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+
+using namespace snort;
+
+HashLruCache* lru = nullptr;
+
+TEST_GROUP(hash_lru_cache)
+{
+ void setup() override
+ {
+ lru = new HashLruCache;
+ CHECK(lru);
+ }
+
+ void teardown() override
+ {
+ unsigned node_id = 1;
+ HashNode* node = lru->remove_lru_node();
+ while( node )
+ {
+ CHECK(*(unsigned*)node->data == node_id);
+ unsigned* data = (unsigned *)lru->get_mru_user_data();
+ if ( data )
+ CHECK(*data == 5);
+ data = (unsigned *)lru->get_lru_user_data();
+ if ( data )
+ CHECK(*data == node_id + 1);
+ ++node_id;
+ delete (unsigned*)node->data;
+ snort_free(node);
+ node = lru->remove_lru_node();
+ }
+
+ delete lru;
+ }
+};
+
+TEST(hash_lru_cache, create_hash_lru_cache_test)
+{
+ CHECK(!lru->get_next_lru_node());
+ CHECK(!lru->get_current_node());
+ CHECK(!lru->get_mru_user_data());
+ CHECK(!lru->get_lru_user_data());
+ CHECK(!lru->remove_lru_node());
+}
+
+TEST(hash_lru_cache, hash_lru_cache_insert_test)
+{
+ HashNode* node;
+ for (unsigned i = 0; i < 5; i++)
+ {
+ node = (HashNode*)snort_calloc(sizeof(HashNode));
+ unsigned* data = new unsigned;
+ *data = i + 1;
+ node->data = data;
+ lru->insert(node);
+ CHECK(*((unsigned *)lru->get_mru_user_data()) == i + 1);
+ CHECK(*((unsigned *)lru->get_lru_user_data()) == 1);
+ }
+
+ for (unsigned i = 0; i < 5; i++)
+ {
+ node = lru->get_lru_node();
+ CHECK(node);
+ CHECK(*((unsigned*)node->data) == i + 1);
+ lru->touch(node);
+ CHECK(*((unsigned *)lru->get_mru_user_data()) == i + 1);
+ CHECK(*((unsigned *)lru->get_lru_user_data()) == ((i + 1) % 5) + 1);
+ }
+
+ node = lru->get_lru_node();
+ CHECK(node);
+ CHECK(*((unsigned*)node->data) == 1);
+}
+
+TEST(hash_lru_cache, hash_lru_cache_browse_test)
+{
+ HashNode* node;
+ for (unsigned i = 0; i < 5; i++)
+ {
+ node = (HashNode*)snort_calloc(sizeof(HashNode));
+ unsigned* data = new unsigned;
+ *data = i + 1;
+ node->data = data;
+ lru->insert(node);
+ }
+
+ for (unsigned i = 0; i < 5; i++)
+ {
+ node = lru->get_lru_node();
+ CHECK(node);
+ CHECK(*((unsigned*)node->data) == (i + 1));
+ lru->touch(node);
+ }
+
+ node = lru->get_lru_node();
+ unsigned i = 1;
+ while( node )
+ {
+ CHECK(*(unsigned*)node->data == i);
+ CHECK(*(unsigned*)lru->get_current_node()->data == i);
+ node = lru->get_next_lru_node();
+ ++i;
+ }
+}
+
+int main(int argc, char** argv)
+{
+ return CommandLineTestRunner::RunAllTests(argc, argv);
+}
#include "hash/xhash.h"
+#include "hash/hash_defs.h"
#include "main/snort_config.h"
#include "utils/util.h"
// Test create a hash table, add nodes, find and delete.
TEST(xhash, create_xhash_test)
{
- XHash* test_table = new XHash(4, sizeof(struct xhash_test_key),
- 0, 0, false, nullptr, nullptr, false);
- CHECK(test_table->get_keysize() == sizeof(struct xhash_test_key));
+ XHash* test_table = new XHash(4, sizeof(struct xhash_test_key), 0, 0);
+ CHECK(test_table);
void* data = test_table->get_mru_user_data();
CHECK(data == nullptr);
// Create a free node in xhash and verifies if xhash_free_anr_lru() deletes it
TEST(xhash, free_anr_lru_delete_free_node_test)
{
- XHash* test_table = new XHash(3, sizeof(struct xhash_test_key),
- 1, 1040, false, nullptr, nullptr, true);
+ XHash* test_table = new XHash(3, sizeof(struct xhash_test_key), 1, 1040);
+ CHECK(test_table);
+
xhash_test_key xtk;
xtk.key = 10;
int ret = test_table->insert(&xtk, nullptr);
CHECK(ret == HASH_OK);
- HashNode* xnode = test_table->get_node(&xtk);
- CHECK(xnode != nullptr);
+ HashNode* xnode = test_table->find_node(&xtk);
+ CHECK(xnode);
ret = test_table->release_node(xnode);
CHECK(ret == HASH_OK);
- ret = test_table->delete_anr_or_lru_node();
+ ret = test_table->delete_lru_node();
CHECK(ret == HASH_OK);
HashNode* xhnode = test_table->find_node(&xtk);
- CHECK(xhnode == nullptr);
+ CHECK(!xhnode);
delete test_table;
}
-// No free node is available, verifies if xhash_free_anr_lru() deletes the last node
+// No free node is available, verifies the LRU node is deleted
TEST(xhash, free_anr_lru_delete_tail_node_test)
{
- XHash* test_table = new XHash(3, sizeof(struct xhash_test_key),
- 1, 1040, false, nullptr, nullptr, true);
+ XHash* test_table = new XHash(3, sizeof(struct xhash_test_key), 1, 1040);
+ CHECK(test_table);
+
xhash_test_key xtk;
int ret = test_table->insert(&xtk, nullptr);
CHECK(ret == HASH_OK);
- ret = test_table->delete_anr_or_lru_node();
- CHECK(ret == HASH_OK);
+ CHECK(test_table->delete_lru_node());
HashNode* xhnode = test_table->find_node(&xtk);
CHECK(xhnode == nullptr);
delete test_table;
}
-// No free node is available [recycle is not enabled], verifies if last node is deleted
-TEST(xhash, free_anr_lru_usr_free_delete_tail_node_test)
-{
- XHash* test_table = new XHash(3, sizeof(struct xhash_test_key),
- 1, 1040, false, nullptr, nullptr, false);
- xhash_test_key xtk;
- int ret = test_table->insert(&xtk, nullptr);
- CHECK(ret == HASH_OK);
-
- ret = test_table->delete_anr_or_lru_node();
- CHECK(ret == HASH_OK);
- delete test_table;
-}
-
int main(int argc, char** argv)
{
return CommandLineTestRunner::RunAllTests(argc, argv);
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+
+// zhash_test.cc author davis mcpherson <davmcphe@cisco.com>
+// unit tests for the HashLruCache class
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string>
+
+#include "../zhash.h"
+#include "../hash_key_operations.h"
+
+#include "flow/flow_key.h"
+#include "main/snort_config.h"
+
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+
+using namespace snort;
+
+namespace snort
+{
+unsigned FlowHashKeyOps::do_hash(const unsigned char* k, int len)
+{
+ unsigned hash = seed;
+ while ( len )
+ {
+ hash *= scale;
+ hash += *k++;
+ len--;
+ }
+ return hash ^ hardener;
+}
+
+bool FlowHashKeyOps::key_compare(const void* k1, const void* k2, size_t len)
+{
+ if ( memcmp(k1, k2, len ) == 0 )
+ return true;
+ else
+ return false;
+}
+}
+
+// Stubs whose sole purpose is to make the test code link
+static SnortConfig my_config;
+THREAD_LOCAL SnortConfig *snort_conf = &my_config;
+
+SnortConfig::SnortConfig(const SnortConfig* const)
+{ snort_conf->run_flags = 0;} // run_flags is used indirectly from HashFnc class by calling SnortConfig::static_hash()
+
+SnortConfig::~SnortConfig() = default;
+
+SnortConfig* SnortConfig::get_conf()
+{ return snort_conf; }
+
+const unsigned ZHASH_ROWS = 1000;
+const unsigned ZHASH_KEY_SIZE = 100;
+const unsigned MAX_ZHASH_NODES = 100;
+char key_buf[ZHASH_KEY_SIZE];
+
+ZHash* zh = nullptr;
+
+TEST_GROUP(zhash)
+{
+ void setup() override
+ {
+ zh = new ZHash(ZHASH_ROWS, ZHASH_KEY_SIZE);
+ CHECK(zh);
+
+ memset(key_buf, '\0', ZHASH_KEY_SIZE);
+ }
+
+ void teardown() override
+ {
+ delete zh;
+ }
+};
+
+TEST(zhash, create_zhash_test)
+{
+ for (unsigned i = 0; i < MAX_ZHASH_NODES; i++ )
+ {
+ unsigned* data;
+ data = (unsigned*)snort_calloc(sizeof(unsigned));
+ *data = 0;
+ zh->push(data);
+ }
+
+ std::string key_prefix = "foo";
+ for (unsigned i = 0; i < MAX_ZHASH_NODES; i++ )
+ {
+ std::string key;
+ key = key_prefix + std::to_string(i + 1);
+ memcpy(key_buf, key.c_str(), key.size());
+ unsigned* data = (unsigned*)zh->get(key_buf);
+ CHECK(*data == 0);
+ *data = i + 1;
+ }
+
+ unsigned nodes_walked = 0;
+ unsigned* data = (unsigned*)zh->lru_first();
+ while ( data )
+ {
+ CHECK(*data == ++nodes_walked);
+ data = (unsigned*)zh->lru_next();
+ }
+
+ CHECK(nodes_walked == MAX_ZHASH_NODES);
+
+ data = (unsigned*)zh->lru_first();
+ CHECK(*data == 1);
+ data = (unsigned*)zh->remove();
+ CHECK(*data == 1);
+ snort_free(data);
+ data = (unsigned*)zh->lru_current();
+ CHECK(*data == 2);
+ data = (unsigned*)zh->lru_first();
+ CHECK(*data == 2);
+
+ for (unsigned i = 1; i < MAX_ZHASH_NODES; i++ )
+ {
+ data = (unsigned*)zh->remove();
+ CHECK(*data == (i + 1));
+ snort_free(data);
+ }
+}
+
+int main(int argc, char** argv)
+{
+ return CommandLineTestRunner::RunAllTests(argc, argv);
+}
#include <cassert>
#include "utils/util.h"
+#include "hash_defs.h"
+#include "hash_key_operations.h"
+#include "hash_lru_cache.h"
+
using namespace snort;
namespace snort
{
-XHash::XHash(int nrows_, int keysize_, int datasize_, unsigned long maxmem,
- bool anr_enabled, Hash_FREE_FCN anr_free, Hash_FREE_FCN usr_free,
- bool recycle_nodes)
- : keysize(keysize_), datasize(datasize_), recycle_nodes(recycle_nodes),
- anr_enabled(anr_enabled), anr_free(anr_free), usr_free(usr_free)
+void XHash::initialize(HashKeyOperations* hk_ops)
+{
+ hashkey_ops = hk_ops;
+ table = (HashNode**)snort_calloc(sizeof(HashNode*) * nrows);
+ lru_cache = new HashLruCache();
+ mem_allocator = new MemCapAllocator(mem_cap, sizeof(HashNode) + keysize + datasize);
+}
+
+void XHash::initialize()
+{
+ initialize(new HashKeyOperations(nrows));
+}
+
+void XHash::set_number_of_rows (int rows)
{
- // adjust rows to be power of 2
- if ( nrows_ > 0 )
- nrows = hash_nearest_power_of_2(nrows_);
+ if ( rows > 0 )
+ nrows = hash_nearest_power_of_2 (rows);
else
- nrows = -nrows_; // if negative use as is
+ nrows = -rows;
+}
- table = (HashNode**)snort_calloc(sizeof(HashNode*) * nrows);
- hashfcn = hashfcn_new(nrows);
- sfmemcap_init(&mc, maxmem);
+XHash::XHash(int rows, int keysize)
+ : keysize(keysize)
- for ( unsigned i = 0; i < nrows; i++ )
- table[i] = nullptr;
+{
+ set_number_of_rows(rows);
+}
- mem_allocated_per_entry = sizeof(HashNode) + keysize + datasize + sizeof(long);
+XHash::XHash(int rows, int keysize, int datasize, unsigned long memcap)
+ : keysize(keysize), datasize(datasize), mem_cap(memcap)
+{
+ set_number_of_rows(rows);
+ initialize();
}
XHash::~XHash()
{
- if ( hashfcn )
- hashfcn_free(hashfcn);
-
- for (unsigned i = 0; i < nrows; i++)
+ if ( table )
{
- for ( HashNode* node = table[i]; node; )
- {
- HashNode* onode = node;
- node = node->next;
-
- if ( usr_free )
- usr_free(onode->key, onode->data);
+ for (unsigned i = 0; i < nrows; i++)
+ for (HashNode* node = table[i]; node;)
+ {
+ HashNode* xnode;
+ xnode = node;
+ node = node->next;
+ mem_allocator->free(xnode);
+ }
- sfmemcap_free(&mc, onode);
- }
+ snort_free(table);
}
- snort_free(table);
+
purge_free_list();
+ delete hashkey_ops;
+ delete lru_cache;
+ delete mem_allocator;
}
-void XHash::clear()
+void XHash::delete_hash_table()
{
+ assert( table );
+
for (unsigned i = 0; i < nrows; i++)
- {
- HashNode* n = table[i];
- while ( n )
+ for (HashNode* node = table[i]; node;)
{
- HashNode* tmp;
- tmp = n;
- n = n->next;
- release_node(tmp);
+ HashNode* xnode;
+ xnode = node;
+ node = node->next;
+ free_user_data(xnode);
+ mem_allocator->free(xnode);
}
- }
- max_nodes = 0;
- crow = 0;
- cnode = nullptr;
- count = 0;
- ghead = nullptr;
- gtail = nullptr;
- anr_count = 0;
- anr_tries = 0;
- find_success = 0;
- find_fail = 0;
+ snort_free(table);
+ table = nullptr;
}
-void XHash::save_free_node(HashNode* hnode)
+void XHash::initialize_node(HashNode *hnode, const void *key, void *data, int index)
{
- if ( fhead )
+ hnode->key = (char*) (hnode) + sizeof(HashNode);
+ memcpy(hnode->key, key, keysize);
+ if ( datasize )
{
- hnode->gprev = nullptr;
- hnode->gnext = fhead;
- fhead->gprev = hnode;
- fhead = hnode;
+ hnode->data = (char*) (hnode) + sizeof(HashNode) + keysize;
+ if ( data )
+ memcpy (hnode->data, data, datasize);
}
else
- {
- hnode->gprev = nullptr;
- hnode->gnext = nullptr;
- fhead = hnode;
- ftail = hnode;
- }
+ hnode->data = data;
+
+ hnode->rindex = index;
+ link_node(hnode);
+ lru_cache->insert(hnode);
}
-HashNode* XHash::get_free_node()
+HashNode* XHash::allocate_node(const void* key, void* data, int index)
{
- HashNode* node = fhead;
+ // use a free one if available...
+ HashNode* hnode = get_free_node();
- if ( fhead )
- {
- fhead = fhead->gnext;
- if ( fhead )
- fhead->gprev = nullptr;
+ // if no free nodes, try to allocate a new one...
+ if ( !hnode && ((max_nodes == 0) || (num_nodes < max_nodes)) )
+ hnode = (HashNode*)mem_allocator->allocate();
- if ( ftail == node )
- ftail = nullptr;
+ // if still no node then try to reuse one...
+ if ( !hnode && anr_enabled )
+ hnode = release_lru_node();
+
+ if ( hnode )
+ {
+ initialize_node(hnode, key, data, index);
+ ++num_nodes;
}
- return node;
+ return hnode;
}
-void XHash::purge_free_list()
+int XHash::insert(const void* key, void* data)
{
- HashNode* cur = fhead;
- while ( cur )
+ assert(key);
+
+ int index = 0;
+ HashNode* hnode = find_node_row(key, index);
+ if ( hnode )
{
- HashNode* next = cur->gnext;
- sfmemcap_free(&mc, (void*)cur);
- cur = next;
+ cursor = hnode;
+ return HASH_INTABLE;
}
- fhead = nullptr;
- ftail = nullptr;
+ hnode = allocate_node(key, data, index);
+ cursor = hnode;
+ return ( hnode ) ? HASH_OK : HASH_NOMEM;
}
-void XHash::glink_node(HashNode* hnode)
+HashNode* XHash::find_node(const void* key)
{
- if ( ghead )
- {
- hnode->gprev = nullptr;
- hnode->gnext = ghead;
- ghead->gprev = hnode;
- ghead = hnode;
- }
- else
- {
- hnode->gprev = nullptr;
- hnode->gnext = nullptr;
- ghead = hnode;
- gtail = hnode;
- }
+ assert(key);
+
+ int rindex = 0;
+ return find_node_row(key, rindex);
}
-void XHash::gunlink_node(HashNode* hnode)
+HashNode* XHash::find_first_node()
{
- if ( gnode == hnode )
- gnode = hnode->gnext;
-
- if ( ghead == hnode )
+ for ( crow = 0; crow < nrows; crow++ )
{
- ghead = ghead->gnext;
- if ( ghead )
- ghead->gprev = nullptr;
+ cursor = table[crow];
+ if ( cursor )
+ {
+ HashNode* n = cursor;
+ update_cursor();
+ return n;
+ }
}
- if ( hnode->gprev )
- hnode->gprev->gnext = hnode->gnext;
- if ( hnode->gnext )
- hnode->gnext->gprev = hnode->gprev;
+ return nullptr;
+}
- if ( gtail == hnode )
- gtail = hnode->gprev;
+HashNode* XHash::find_next_node()
+{
+ HashNode* n = cursor;
+ if ( !n )
+ return nullptr;
+
+ update_cursor();
+ return n;
}
-void XHash::gmove_to_front(HashNode* hnode)
+void* XHash::get_user_data()
{
- if ( hnode != ghead )
+ if ( cursor )
+ return cursor->data;
+ else
+ return nullptr;
+}
+
+void XHash::update_cursor()
+{
+ if ( !cursor )
+ return;
+
+ cursor = cursor->next;
+ if ( cursor )
+ return;
+
+ for ( crow++; crow < nrows; crow++ )
{
- gunlink_node(hnode);
- glink_node(hnode);
+ cursor = table[crow];
+ if ( cursor )
+ return;
}
}
-HashNode* XHash::gfind_next()
+void* XHash::get_user_data(const void* key)
{
- HashNode* n = gnode;
- if ( n )
- gnode = n->gnext;
- return n;
+ assert(key);
+
+ int rindex = 0;
+ HashNode* hnode = find_node_row(key, rindex);
+ return ( hnode ) ? hnode->data : nullptr;
}
-HashNode* XHash::gfind_first()
+void XHash::release()
{
- if ( ghead )
- gnode = ghead->gnext;
- else
- gnode = nullptr;
- return ghead;
+ HashNode* node = lru_cache->get_current_node();
+ assert(node);
+ release_node(node);
}
-void* XHash::get_mru_user_data()
+int XHash::release_node(HashNode* hnode)
{
- if ( ghead )
- return ghead->data;
+ assert(hnode);
+
+ free_user_data(hnode);
+ unlink_node(hnode);
+ lru_cache->remove_node(hnode);
+ num_nodes--;
+
+ if ( recycle_nodes )
+ {
+ save_free_node(hnode);
+ ++stats.release_recycles;
+ }
else
- return nullptr;
+ {
+ mem_allocator->free(hnode);
+ ++stats.release_deletes;
+ }
+
+ return HASH_OK;
}
-void* XHash::get_lru_user_data()
+int XHash::release_node(const void* key)
{
- if ( gtail )
- return gtail->data;
- else
- return nullptr;
+ assert(key);
+
+ unsigned hashkey = hashkey_ops->do_hash((const unsigned char*)key, keysize);
+
+ unsigned index = hashkey & (nrows - 1);
+ for (HashNode* hnode = table[index]; hnode; hnode = hnode->next)
+ {
+ if ( hashkey_ops->key_compare(hnode->key, key, keysize) )
+ return release_node(hnode);
+ }
+
+ return HASH_NOT_FOUND;
}
void XHash::link_node(HashNode* hnode)
}
}
-void XHash::move_to_front(HashNode* n)
+void XHash::move_to_front(HashNode* node)
{
- if ( table[n->rindex] != n )
+ if ( table[node->rindex] != node )
{
- unlink_node(n);
- link_node(n);
+ unlink_node(node);
+ link_node(node);
}
- if (n == gnode)
- gnode = n->gnext;
- gmove_to_front(n);
+ lru_cache->touch(node);
}
-/*
- * Allocate a new hash node, uses Auto Node Recovery if needed and enabled.
- *
- * The oldest node is the one with the longest time since it was last touched,
- * and does not have any direct indication of how long the node has been around.
- * We don't monitor the actual time since last being touched, instead we use a
- * splayed global list of node pointers. As nodes are accessed they are splayed
- * to the front of the list. The oldest node is just the tail node.
- *
- */
-HashNode* XHash::allocate_node()
+HashNode* XHash::find_node_row(const void* key, int& rindex)
{
- // use previously allocated node if there is a free one...
- HashNode* hnode = get_free_node();
- if ( !hnode )
- {
- if ( (max_nodes == 0) || (count < max_nodes) )
- hnode = (HashNode*)sfmemcap_alloc(&mc,
- sizeof(HashNode) + keysize + datasize);
+ unsigned hashkey = hashkey_ops->do_hash((const unsigned char*)key, keysize);
- if ( !hnode && anr_enabled && gtail )
- {
- /* Find the oldest node the users willing to let go. */
- for (hnode = gtail; hnode; hnode = hnode->gprev )
- {
- if ( anr_free )
- {
- anr_tries++;
- if ( anr_free(hnode->key, hnode->data) )
- continue; // don't recycle this one...
- }
-
- gunlink_node(hnode);
- unlink_node(hnode);
- count--;
- anr_count++;
- break;
- }
- }
- }
-
- return hnode;
-}
-
-HashNode* XHash::find_node_row(const void* key, int* rindex)
-{
- unsigned hashkey = hashfcn->hash_fcn(hashfcn, (const unsigned char*)key, keysize);
-
- // Modulus is slow. masking since table size is a power of 2.
- int index = hashkey & (nrows - 1);
- *rindex = index;
-
- for (HashNode* hnode = table[index]; hnode; hnode = hnode->next )
+ /* Modulus is slow. Switched to a table size that is a power of 2. */
+ rindex = hashkey & (nrows - 1);
+ for (HashNode* hnode = table[rindex]; hnode; hnode = hnode->next )
{
- if ( hashfcn->keycmp_fcn(hnode->key, key, keysize) )
+ if ( hashkey_ops->key_compare(hnode->key, key, keysize) )
{
- if ( splay > 0 )
- move_to_front(hnode);
-
- find_success++;
+ move_to_front(hnode);
return hnode;
}
}
- find_fail++;
return nullptr;
}
-int XHash::insert(const void* key, void* data)
+void XHash::save_free_node(HashNode* hnode)
{
- assert(key);
-
- int index = 0;
-
- /* Enforce uniqueness: Check for the key in the table */
- HashNode* hnode = find_node_row(key, &index);
- if ( hnode )
+ if ( fhead )
{
- cnode = hnode;
- return HASH_INTABLE;
+ hnode->gprev = nullptr;
+ hnode->gnext = fhead;
+ fhead->gprev = hnode;
+ fhead = hnode;
}
-
- hnode = allocate_node();
- if ( !hnode )
- return HASH_NOMEM;
-
- hnode->key = (char*)hnode + sizeof(HashNode);
- memcpy(hnode->key, key, keysize);
- hnode->rindex = index;
-
- if ( datasize )
+ else
{
- hnode->data = (char*)hnode + sizeof(HashNode) + keysize;
- if ( data )
- memcpy(hnode->data, data, datasize);
+ hnode->gprev = nullptr;
+ hnode->gnext = nullptr;
+ fhead = hnode;
}
- else
- hnode->data = data;
-
- link_node (hnode);
- glink_node(hnode);
- count++;
-
- return HASH_OK;
}
-HashNode* XHash::get_node(const void* key)
+HashNode* XHash::get_free_node()
{
- assert(key);
-
- int index = 0;
-
- // Enforce uniqueness: Check for the key in the table
- HashNode* hnode = find_node_row( key, &index);
- if ( hnode )
+ HashNode* node = fhead;
+ if ( fhead )
{
- cnode = hnode;
- return hnode;
+ fhead = fhead->gnext;
+ if ( fhead )
+ fhead->gprev = nullptr;
}
- hnode = allocate_node();
- if ( !hnode )
- return nullptr;
-
- hnode->key = (char*)hnode + sizeof(HashNode);
- memcpy(hnode->key, key, keysize);
- hnode->rindex = index;
-
- if ( datasize )
- hnode->data = (char*)hnode + sizeof(HashNode) + keysize;
- else
- hnode->data = nullptr;
-
- link_node(hnode);
- glink_node(hnode);
- count++;
-
- return hnode;
-}
-
-HashNode* XHash::get_node_with_prune(const void* key, bool* prune_performed)
-{
- assert(key);
-
- size_t mem_after_alloc = mc.memused + mem_allocated_per_entry;
- bool over_capacity = (mc.memcap < mem_after_alloc);
-
- if ( over_capacity )
- *prune_performed = (delete_anr_or_lru_node() == HASH_OK);
-
- HashNode* hnode = nullptr;
- if ( *prune_performed or !over_capacity )
- hnode = get_node(key);
-
- return hnode;
+ return node;
}
-HashNode* XHash::find_node(const void* key)
+bool XHash::delete_free_node()
{
- assert(key);
-
- int rindex = 0;
- return find_node_row(key, &rindex);
+ HashNode* hnode = get_free_node();
+ if ( hnode )
+ {
+ mem_allocator->free(hnode);
+ return true;
+ }
+ return false;
}
-void* XHash::get_user_data(void* key)
+void XHash::purge_free_list()
{
- assert(key);
-
- int rindex = 0;
- HashNode* hnode = find_node_row(key, &rindex);
- if ( hnode )
- return hnode->data;
+ HashNode* cur = fhead;
+ while ( cur )
+ {
+ HashNode* next = cur->gnext;
+ mem_allocator->free(cur);
+ cur = next;
+ }
- return nullptr;
+ fhead = nullptr;
}
-int XHash::release_node(HashNode* hnode)
+void XHash::clear_hash()
{
- assert(hnode);
-
- unlink_node(hnode);
- gunlink_node(hnode);
- count--;
-
- if ( usr_free )
- usr_free(hnode->key, hnode->data);
-
- if ( recycle_nodes )
- save_free_node(hnode);
- else
- sfmemcap_free(&mc, hnode);
+ for (unsigned i = 0; i < nrows; i++)
+ for (HashNode* node = table[i]; node;)
+ {
+ HashNode* xnode = node;
+ node = node->next;
+ release_node(xnode);
+ }
- return HASH_OK;
+ max_nodes = 0;
+ num_nodes = 0;
+ crow = 0;
+ cursor = nullptr;
}
-int XHash::release_node(void* key)
+void* XHash::get_mru_user_data()
{
- assert(key);
-
- unsigned hashkey = hashfcn->hash_fcn(hashfcn, (unsigned char*)key, keysize);
-
- unsigned index = hashkey & (nrows - 1);
- for ( HashNode* hnode = table[index]; hnode; hnode = hnode->next )
- {
- if ( hashfcn->keycmp_fcn(hnode->key, key, keysize) )
- return release_node(hnode);
- }
-
- return HASH_ERR;
+ return lru_cache->get_mru_user_data();
}
-int XHash::delete_free_node()
+void* XHash::get_lru_user_data()
{
- HashNode* fn = get_free_node();
- if (fn)
- {
- sfmemcap_free(&mc, fn);
- return HASH_OK;
- }
- return HASH_ERR;
+ return lru_cache->get_lru_user_data();
}
-int XHash::delete_anr_or_lru_node()
+HashNode* XHash::release_lru_node()
{
- if ( fhead )
+ HashNode* hnode = lru_cache->get_lru_node();
+ while ( hnode )
{
- if (delete_free_node() == HASH_OK)
- return HASH_OK;
- }
-
- if ( gtail )
- {
- if ( release_node(gtail) == HASH_OK )
+ if ( is_node_recovery_ok(hnode) )
{
- if ( fhead )
- {
- if ( delete_free_node() == HASH_OK )
- return HASH_OK;
- }
- else if ( !recycle_nodes )
- return HASH_OK;
+ lru_cache->remove_node(hnode);
+ free_user_data(hnode);
+ unlink_node(hnode);
+ --num_nodes;
+ ++stats.memcap_prunes;
+ break;
}
+ else
+ hnode = lru_cache->get_next_lru_node ();
}
- return HASH_ERR;
+ return hnode;
}
-int XHash::free_over_allocations(unsigned work_limit, unsigned* num_freed)
+bool XHash::delete_lru_node()
{
-
- while (mc.memcap < mc.memused and work_limit--)
+ if ( HashNode* hnode = lru_cache->remove_lru_node() )
{
- if (delete_anr_or_lru_node() != HASH_OK)
- return HASH_ERR;
-
- ++*num_freed;
+ unlink_node(hnode);
+ free_user_data(hnode);
+ mem_allocator->free(hnode);
+ --num_nodes;
+ return true;
}
- return (mc.memcap >= mc.memused) ? HASH_OK : HASH_PENDING;
+ return false;
}
-void XHash::update_cnode()
+bool XHash::delete_a_node()
{
- if ( !cnode )
- return;
+ if ( delete_free_node() )
+ return true;
- cnode = cnode->next;
- if ( cnode )
- return;
+ if ( delete_lru_node() )
+ return true;
- for ( crow++; crow < nrows; crow++ )
- {
- cnode = table[crow];
- if ( cnode )
- return;
- }
+ return false;
}
-HashNode* XHash::find_first_node()
+int XHash::tune_memory_resources(unsigned work_limit, unsigned& num_freed)
{
- for ( crow = 0; crow < nrows; crow++ )
+ while ( work_limit-- and mem_allocator->is_over_capacity() )
{
- cnode = table[crow];
- if ( cnode )
- {
- HashNode* n = cnode;
- update_cnode();
- return n;
- }
- }
-
- return nullptr;
-}
-
-HashNode* XHash::find_next_node()
-{
- HashNode* n = cnode;
- if ( !n )
- return nullptr;
-
- update_cnode();
+ if ( !delete_a_node() )
+ break;
- return n;
-}
+ ++stats.memcap_deletes;
+ ++num_freed;
+ }
-void XHash::set_key_opcodes(hash_func hash_fcn, keycmp_func keycmp_fcn)
-{
- hashfcn_set_keyops(hashfcn, hash_fcn, keycmp_fcn);
+ return ( mem_allocator->is_over_capacity() ) ? HASH_PENDING : HASH_OK;
}
} // namespace snort
// generic hash table - stores and maps key + data pairs
// (supports memcap and automatic memory recovery when out of memory)
-#include "utils/sfmemcap.h"
+#include "framework/counts.h"
#include "main/snort_types.h"
+#include "utils/memcap_allocator.h"
-#include "hash_defs.h"
-#include "hashfcn.h"
+class HashLruCache;
namespace snort
{
+class HashKeyOperations;
+class HashNode;
+
+struct XHashStats
+{
+ PegCount nodes_created = 0;
+ PegCount memcap_prunes = 0;
+ PegCount memcap_deletes = 0;
+ PegCount release_recycles = 0;
+ PegCount release_deletes = 0;
+};
+
class SO_PUBLIC XHash
{
public:
- XHash(int nrows_, int keysize_, int datasize_, unsigned long memcap,
- bool anr_enabled, Hash_FREE_FCN, Hash_FREE_FCN, bool recycle_nodes);
- ~XHash();
-
- int free_over_allocations(unsigned work_limit, unsigned* num_freed);
- void clear();
+ XHash(int rows, int keysize);
+ XHash(int rows, int keysize, int datasize, unsigned long memcap);
+ virtual ~XHash();
int insert(const void* key, void* data);
- HashNode* get_node(const void* key);
- HashNode* get_node_with_prune(const void* key, bool* prune_performed);
- int release_node(void* key);
- int release_node(HashNode* node);
- int delete_anr_or_lru_node();
HashNode* find_node(const void* key);
HashNode* find_first_node();
HashNode* find_next_node();
- void* get_user_data(void* key);
+ void* get_user_data();
+ void* get_user_data(const void* key);
+ void release();
+ int release_node(const void* key);
+ int release_node(HashNode* node);
void* get_mru_user_data();
void* get_lru_user_data();
- void set_key_opcodes(hash_func, keycmp_func);
+ bool delete_lru_node();
+ void clear_hash();
- // Set the maximum nodes used in this hash table.
- // Specifying 0 is unlimited (or otherwise limited by memcap).
+ // set max hash nodes, 0 == no limit
void set_max_nodes(int max)
{ max_nodes = max; }
- unsigned get_node_count()
- { return count; }
+ unsigned get_num_nodes()
+ { return num_nodes; }
- unsigned get_anr_count()
- { return anr_count; }
+ void set_memcap(unsigned long memcap)
+ { mem_allocator->set_mem_capacity(memcap); }
- unsigned get_total_finds()
- { return find_success + find_fail; }
+ unsigned long get_memcap()
+ { return mem_allocator->get_mem_capacity(); }
- unsigned get_find_fails()
- { return find_fail; }
+ unsigned long get_mem_used()
+ { return mem_allocator->get_mem_allocated(); }
- unsigned get_find_successes()
- { return find_success; }
+ const XHashStats& get_stats() const
+ { return stats; }
- void set_memcap(unsigned long new_memcap)
- { mc.memcap = new_memcap; }
+ virtual int tune_memory_resources(unsigned work_limit, unsigned& num_freed);
- unsigned long get_memcap()
- { return mc.memcap; }
+protected:
+ void initialize(HashKeyOperations*);
+ void initialize();
- unsigned long get_mem_used()
- { return mc.memused; }
+ void initialize_node (HashNode*, const void* key, void* data, int index);
+ HashNode* allocate_node(const void* key, void* data, int index);
+ HashNode* find_node_row(const void* key, int& rindex);
+ void link_node(HashNode*);
+ void unlink_node(HashNode*);
+ bool delete_a_node();
+ void save_free_node(HashNode*);
+ HashNode* get_free_node();
+ void delete_hash_table();
+
+ virtual bool is_node_recovery_ok(HashNode*)
+ { return true; }
- const HashNode* get_cnode () const
- { return cnode; }
+ virtual void free_user_data(HashNode*)
+ { }
- int get_keysize () const
- { return keysize; }
+ MemCapAllocator* mem_allocator = nullptr;
+ HashLruCache* lru_cache = nullptr;
+ unsigned nrows = 0;
+ unsigned keysize = 0;
+ unsigned num_nodes = 0;
+ bool recycle_nodes = true;
+ bool anr_enabled = true;
private:
+ HashNode** table = nullptr;
+ HashKeyOperations* hashkey_ops = nullptr;
+ HashNode* cursor = nullptr;
+ HashNode* fhead = nullptr;
+ unsigned datasize = 0;
+ unsigned long mem_cap = 0;
+ unsigned max_nodes = 0;
+ unsigned crow = 0;
+ XHashStats stats;
+
+ void set_number_of_rows(int nrows);
+ void move_to_front(HashNode*);
+ bool delete_free_node();
+ HashNode* release_lru_node();
+ void update_cursor();
void purge_free_list();
- void save_free_node(HashNode* hnode);
- HashNode* get_free_node();
- void glink_node(HashNode* hnode);
- void gunlink_node(HashNode* hnode);
- void gmove_to_front(HashNode* hnode);
- HashNode* gfind_first();
- HashNode* gfind_next();
- void link_node(HashNode* hnode);
- void unlink_node(HashNode* hnode);
- void move_to_front(HashNode* n);
- HashNode* allocate_node();
- HashNode* find_node_row(const void* key, int* rindex);
- void update_cnode();
- int delete_free_node();
-
- HashFnc* hashfcn = nullptr; // hash function
- int keysize = 0; // bytes in key, if <= 0 -> keys are strings - FIXIT-H does negative keysize work?
- int datasize = 0; // bytes in key, if == 0 -> user data
- unsigned mem_allocated_per_entry = 0;
- HashNode** table = nullptr; // array of node ptr's */
- unsigned nrows = 0; // # rows int the hash table use a prime number 211, 9871
- unsigned count = 0; // total # nodes in table
- unsigned crow = 0; // findfirst/next row in table
- HashNode* cnode = nullptr; // find_[first|next] node ptr
- int splay = 1; // whether to splay nodes with same hash bucket
- unsigned max_nodes = 0; // maximum # of nodes within a hash
- MEMCAP mc;
- unsigned find_fail = 0;
- unsigned find_success = 0;
-
- HashNode* ghead = nullptr; // global - root of all nodes allocated in table
- HashNode* gtail = nullptr;
- HashNode* gnode = nullptr; // gfirst/gnext node ptr */
- HashNode* fhead = nullptr; // list of free nodes, which are recycled
- HashNode* ftail = nullptr;
- bool recycle_nodes = false; // recycle nodes...
-
- // Automatic Node Recover (ANR): When number of nodes in hash is equal
- // to max_nodes, remove the least recently used nodes and use it for
- // the new node. anr_tries indicates # of ANR tries.*/
- unsigned anr_tries = 0;
- unsigned anr_count = 0; // # ANR ops performed
- bool anr_enabled = false; // false = anr disable, true = anr enabled
-
- Hash_FREE_FCN anr_free = nullptr;
- Hash_FREE_FCN usr_free = nullptr;
};
} // namespace snort
#include <cassert>
#include <cstring>
+#include "flow/flow_key.h"
+
#include "hash_defs.h"
+#include "hash_key_operations.h"
+#include "hash_lru_cache.h"
using namespace snort;
//-------------------------------------------------------------------------
-// private stuff
+// public stuff
//-------------------------------------------------------------------------
-static inline HashNode* s_node_alloc(int keysize)
-{
- auto node = static_cast<HashNode*>(
- ::operator new(sizeof(HashNode) + keysize));
-
- *node = {};
- return node;
-}
-
-static inline void s_node_free(HashNode* node)
-{ ::operator delete(node); }
-
-void ZHash::delete_free_list()
-{
- if ( !fhead )
- return;
-
- HashNode* cur = fhead;
-
- while ( cur )
- {
- fhead = cur->gnext;
- s_node_free(cur);
- cur = fhead;
- }
-}
-
-void ZHash::save_free_node(HashNode* node)
-{
- if ( fhead )
- {
- node->gprev = nullptr;
- node->gnext = fhead;
- fhead->gprev = node;
- fhead = node;
- }
- else
- {
- node->gprev = nullptr;
- node->gnext = nullptr;
- fhead = node;
- }
-}
-
-HashNode* ZHash::get_free_node()
-{
- HashNode* node = fhead;
-
- if ( fhead )
- {
- fhead = fhead->gnext;
-
- if ( fhead )
- fhead->gprev = nullptr;
- }
-
- return node;
-}
-
-void ZHash::glink_node(HashNode* node)
-{
- if ( ghead )
- {
- node->gprev = nullptr;
- node->gnext = ghead;
- ghead->gprev = node;
- ghead = node;
- }
- else
- {
- node->gprev = nullptr;
- node->gnext = nullptr;
- ghead = node;
- gtail = node;
- }
-}
-
-void ZHash::gunlink_node(HashNode* node)
-{
- if ( cursor == node )
- cursor = node->gprev;
-
- if ( ghead == node )
- {
- ghead = ghead->gnext;
- if ( ghead )
- ghead->gprev = nullptr;
- }
-
- if ( node->gprev )
- node->gprev->gnext = node->gnext;
-
- if ( node->gnext )
- node->gnext->gprev = node->gprev;
-
- if ( gtail == node )
- gtail = node->gprev;
-}
-
-void ZHash::link_node(HashNode* node)
-{
- if ( table[node->rindex] ) // UNINITUSE
- {
- node->prev = nullptr;
- node->next = table[node->rindex];
- table[node->rindex]->prev = node;
- table[node->rindex] = node;
- }
- else
- {
- node->prev = nullptr;
- node->next = nullptr;
- table[node->rindex] = node; // UNINITUSE
- }
-}
-void ZHash::unlink_node(HashNode* node)
+ZHash::ZHash(int rows, int key_len)
+ : XHash(rows, key_len)
{
- if ( node->prev )
- {
- node->prev->next = node->next;
- if ( node->next )
- node->next->prev = node->prev;
- }
- else if ( table[node->rindex] )
- {
- table[node->rindex] = table[node->rindex]->next;
-
- if ( table[node->rindex] )
- table[node->rindex]->prev = nullptr;
- }
+ initialize(new FlowHashKeyOps(nrows));
+ anr_enabled = false;
}
-void ZHash::move_to_front(HashNode* node)
+void* ZHash::get(const void* key)
{
- // move to front of row list
- if ( table[node->rindex] != node )
- {
- unlink_node(node);
- link_node(node);
- }
-
- // move to front of global list
- if ( node != ghead )
- {
- gunlink_node(node);
- glink_node(node);
- }
-}
+ assert(key);
-HashNode* ZHash::find_node_row(const void* key, int& row)
-{
- unsigned hashkey = hashfcn->hash_fcn(
- hashfcn, (const unsigned char*)key, keysize);
-
- // Modulus is slow; use a table size that is a power of 2.
- int index = hashkey & (nrows - 1);
- row = index;
-
- for ( HashNode* node = table[index]; node; node = node->next ) // UNINITUSE
- {
- if ( hashfcn->keycmp_fcn(node->key, key, keysize) )
- {
- move_to_front(node);
- find_success++;
- return node;
- }
- }
-
- find_fail++;
- return nullptr;
-}
-
-//-------------------------------------------------------------------------
-// public stuff
-//-------------------------------------------------------------------------
-
-ZHash::ZHash(int rows, int keysz)
- : keysize(keysz)
-{
- // adjust rows to be power of 2
- if ( rows > 0 )
- nrows = hash_nearest_power_of_2(rows);
- else
- nrows = -rows; // if negative use as is
+ int index;
+ HashNode* node = find_node_row(key, index);
+ if ( node )
+ return node->data;
- table = new HashNode*[nrows]();
- hashfcn = hashfcn_new(nrows);
+ node = get_free_node();
+ if ( !node )
+ return nullptr;
- fhead = cursor = nullptr;
- ghead = gtail = nullptr;
- count = find_success = find_fail = 0;
+ memcpy(node->key, key, keysize);
+ node->rindex = index;
+ link_node(node);
+ lru_cache->insert(node);
+ num_nodes++;
+ return node->data;
}
-ZHash::~ZHash()
+void* ZHash::remove()
{
- hashfcn_free(hashfcn);
-
- for ( unsigned i = 0; i < nrows; ++i )
- {
- for ( HashNode* node = table[i]; node; )
- {
- HashNode* onode = node;
- node = node->next;
- s_node_free(onode);
- }
- }
+ HashNode* node = lru_cache->get_current_node();
+ assert(node);
+ void* pv = node->data;
- delete[] table;
- delete_free_list();
+ unlink_node(node);
+ lru_cache->remove_node(node);
+ num_nodes--;
+ mem_allocator->free(node);
+ return pv;
}
void* ZHash::push(void* p)
{
- auto node = s_node_alloc(keysize);
-
+ auto node = (HashNode*)mem_allocator->allocate();
node->key = (char*)node + sizeof(HashNode);
node->data = p;
-
save_free_node(node);
return node->key;
}
void* ZHash::pop()
{
HashNode* node = get_free_node();
-
if ( !node )
return nullptr;
void* pv = node->data;
- s_node_free(node);
+ mem_allocator->free(node);
return pv;
}
-void* ZHash::get(const void* key, bool *new_node)
+void* ZHash::lru_first()
{
- int row;
- HashNode* node = find_node_row(key, row);
-
- if ( node )
- return node->data;
-
- node = get_free_node();
-
- if ( !node )
- return nullptr;
-
- memcpy(node->key, key, keysize);
-
- node->rindex = row;
- link_node (node);
- glink_node(node);
-
- count++;
-
- if (new_node)
- *new_node = true;
-
- return node->data;
-}
-
-void* ZHash::find(const void* key)
-{
- int row;
- HashNode* node = find_node_row(key, row);
-
- if ( node )
- return node->data;
-
- return nullptr;
+ HashNode* node = lru_cache->get_lru_node();
+ return node ? node->data : nullptr;
}
-void* ZHash::first()
+void* ZHash::lru_next()
{
- cursor = gtail;
- return cursor ? cursor->data : nullptr;
+ HashNode* node = lru_cache->get_next_lru_node();
+ return node ? node->data : nullptr;
}
-void* ZHash::next()
+void* ZHash::lru_current()
{
- if ( !cursor )
- return nullptr;
-
- cursor = cursor->gprev;
- return cursor ? cursor->data : nullptr;
-}
-
-void* ZHash::current()
-{
- return cursor ? cursor->data : nullptr;
-}
-
-bool ZHash::touch()
-{
- HashNode* node = cursor;
-
- if ( !node )
- return false;
-
- cursor = cursor->gprev;
-
- if ( node != ghead )
- {
- gunlink_node(node);
- glink_node(node);
- return true;
- }
- return false;
-}
-
-bool ZHash::move_to_free_list(HashNode* node)
-{
- if ( !node )
- return false;
-
- unlink_node(node);
- gunlink_node(node);
- count--;
- save_free_node(node);
-
- return true;
-}
-
-bool ZHash::release()
-{
- HashNode* node = cursor;
- cursor = nullptr;
- return move_to_free_list(node);
-}
-
-bool ZHash::release(const void* key)
-{
- int row;
- HashNode* node = find_node_row(key, row);
- return move_to_free_list(node);
-}
-
-void* ZHash::remove(const void* key)
-{
- void* pv = nullptr;
- int row;
- HashNode* node = find_node_row(key, row);
- if ( node )
- {
- unlink_node(node);
- gunlink_node(node);
- count--;
- pv = node->data;
- s_node_free(node);
- }
-
- return pv;
+ HashNode* node = lru_cache->get_current_node();
+ return node ? node->data : nullptr;
}
-void ZHash::set_key_opcodes(hash_func hash_fcn, keycmp_func keycmp_fcn)
+void ZHash::lru_touch()
{
- hashfcn_set_keyops(hashfcn, hash_fcn, keycmp_fcn);
+ HashNode* node = lru_cache->get_current_node();
+ assert(node);
+ lru_cache->touch(node);
}
#include <cstddef>
-#include "hashfcn.h"
+#include "hash/xhash.h"
-namespace snort
-{
-struct HashNode;
-}
-
-class ZHash
+class ZHash : public snort::XHash
{
public:
ZHash(int nrows, int keysize);
- ~ZHash();
ZHash(const ZHash&) = delete;
ZHash& operator=(const ZHash&) = delete;
void* push(void* p);
void* pop();
- void* first();
- void* next();
- void* current();
- bool touch();
-
- void* find(const void* key);
- void* get(const void* key, bool *new_node = nullptr);
- bool release(const void* key);
- bool release();
- void* remove(const void* key);
- void set_key_opcodes(hash_func, keycmp_func);
-
- inline unsigned get_count()
- { return count; }
-
-private:
- snort::HashNode* get_free_node();
- snort::HashNode* find_node_row(const void*, int&);
-
- void glink_node(snort::HashNode*);
- void gunlink_node(snort::HashNode*);
-
- void link_node(snort::HashNode*);
- void unlink_node(snort::HashNode*);
-
- void delete_free_list();
- void save_free_node(snort::HashNode*);
-
- bool move_to_free_list(snort::HashNode*);
- void move_to_front(snort::HashNode*);
-
-private:
- HashFnc* hashfcn;
- int keysize;
- unsigned nrows;
- unsigned count;
-
- unsigned find_fail;
- unsigned find_success;
+ void* get(const void* key);
+ void* remove();
- snort::HashNode** table;
- snort::HashNode* ghead;
- snort::HashNode* gtail;
- snort::HashNode* fhead;
- snort::HashNode* cursor;
+ void* lru_first();
+ void* lru_next();
+ void* lru_current();
+ void lru_touch();
};
#endif
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler_defs.h"
#include "protocols/packet.h"
#include "protocols/tcp.h"
#include "framework/cursor.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "detection/detection_engine.h"
#include "detection/treenodes.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "framework/cursor.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/cursor.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "utils/util_ber.h"
#include "framework/cursor.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "utils/util_ber.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
using namespace snort;
#include "framework/endianness.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "log/messages.h"
#include "protocols/packet.h"
#include "profiler/profiler.h"
#include "framework/endianness.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "log/messages.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/endianness.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "log/messages.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/endianness.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "log/messages.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/cursor.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "helpers/literal_search.h"
#include "log/messages.h"
#include "parser/parse_utils.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "log/messages.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
using namespace snort;
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "log/messages.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "detection/treenodes.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "log/messages.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "hash/ghash.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_defs.h"
+#include "hash/hash_key_operations.h"
#include "log/messages.h"
#include "parser/mstring.h"
#include "protocols/packet.h"
}
int hstatus = flowbit_state->flowbits_hash->insert(flowbitName, flowbits_item);
- if (hstatus != GHASH_OK)
+ if (hstatus != HASH_OK)
ParseError("Could not add flowbits key (%s) to hash.",flowbitName);
}
flowbits_item->toggle = flowbit_state->flowbits_toggle;
// new group defined, add (bitop set later once we know size)
flowbits_grp = (FLOWBITS_GRP*)snort_calloc(sizeof(*flowbits_grp));
int hstatus = flowbit_state->flowbits_grp_hash->insert(groupName, flowbits_grp);
- if (hstatus != GHASH_OK)
+ if (hstatus != HASH_OK)
ParseAbort("Could not add flowbits group (%s) to hash.\n",groupName);
flowbit_state->flowbits_grp_count++;
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "log/messages.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "hash/hashes.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "log/messages.h"
#include "parser/parse_utils.h"
#include "profiler/profiler.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/icmp4.h"
#include "protocols/icmp6.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/icmp4.h"
#include "protocols/icmp6.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/icmp4.h"
#include "protocols/packet.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "log/messages.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/ipv4_options.h"
#include "protocols/packet.h"
#include "framework/cursor.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "log/messages.h"
#include "parser/mstring.h"
#include "profiler/profiler.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/icmp4.h"
#include "protocols/packet.h"
#include "framework/cursor.h"
#include "framework/decode_data.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "helpers/chunk.h"
#include "lua/lua.h"
#include "log/messages.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/parameter.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "helpers/scratch_allocator.h"
#include "log/messages.h"
#include "main/snort_config.h"
#include "framework/cursor.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "helpers/hyper_scratch_allocator.h"
#include "log/messages.h"
#include "main/snort_config.h"
#include "framework/cursor.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "log/messages.h"
#include "main/snort_config.h"
#include "main/thread_config.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/cursor.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "helpers/hyper_scratch_allocator.h"
#include "log/messages.h"
#include "log/obfuscator.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "protocols/tcp.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "log/messages.h"
#include "main/snort_config.h"
#include "profiler/profiler.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/so_rule.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "log/messages.h"
#include "main/snort_config.h"
#include "managers/so_manager.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "protocols/tcp.h"
#include <cassert>
#include <cstdarg>
-#include <cstdio>
-#include <cstring>
#include "main/snort_config.h"
#include "parser/parser.h"
static unsigned parse_warnings = 0;
static unsigned reload_errors = 0;
+static std::string reload_errors_description;
+
void reset_parse_errors()
{
parse_errors = 0;
return tmp;
}
+void reset_reload_errors()
+{
+ reload_errors = 0;
+ reload_errors_description.clear();
+}
+
unsigned get_reload_errors()
{
return reload_errors;
}
+std::string& get_reload_errors_description()
+{
+ return reload_errors_description;
+}
+
static void log_message(FILE* file, const char* type, const char* msg)
{
const char* file_name;
va_list ap;
va_start(ap, format);
- vsnprintf(buf, STD_BUF, format, ap);
+ vsnprintf(buf, sizeof(buf), format, ap);
va_end(ap);
- buf[STD_BUF] = '\0';
+ buf[sizeof(buf)-1] = '\0';
log_message(stderr, "ERROR", buf);
+ if ( reload_errors_description.empty() )
+ reload_errors_description = buf;
+ else
+ {
+ reload_errors_description += ",";
+ reload_errors_description += buf;
+ }
+
reload_errors++;
}
#include <arpa/inet.h>
#include <cstdio>
+#include <string>
#include <ctime>
#include "main/snort_types.h"
void reset_parse_errors();
unsigned get_parse_errors();
unsigned get_parse_warnings();
+void reset_reload_errors();
unsigned get_reload_errors();
+std::string& get_reload_errors_description();
namespace snort
{
if ( !sc )
{
if (get_reload_errors())
- current_request->respond("== reload failed - restart required\n");
+ {
+ std::string response_message = "== reload failed - restart required - ";
+ response_message += get_reload_errors_description() + "\n";
+ current_request->respond(response_message.c_str());
+ reset_reload_errors();
+ }
else
current_request->respond("== reload failed - bad config\n");
state = new std::vector<void*>[num_slots];
}
-// FIXIT-L this is a work around till snort supports adding/removing
-// stream cache during reload
-bool SnortConfig::verify_stream_inspectors()
-{
- const std::vector<const char*> inspector_names
- { "stream_file", "stream_icmp", "stream_ip", "stream_tcp", "stream_udp", "stream_user" };
- static std::map <const char*, bool> orig_inspectors;
-
- // If wasn't initialized before try to initialize from current config
- if (orig_inspectors.empty())
- {
- const Inspector* const ptr = InspectorManager::get_inspector("stream", true);
- if (ptr != nullptr)
- {
- for (auto name: inspector_names)
- {
- const bool in_orig = InspectorManager::inspector_exists_in_any_policy(name, get_conf());
- orig_inspectors[name] = in_orig;
- }
- }
- }
-
- // If now available - compare
- if (!orig_inspectors.empty())
- {
- const Inspector* const ptr = InspectorManager::get_inspector("stream", true, this);
- if (ptr != nullptr)
- {
- for (auto name: inspector_names)
- {
- const bool in_new = InspectorManager::inspector_exists_in_any_policy(name, this);
- if (orig_inspectors[name] != in_new)
- {
- ReloadError("Snort Reload: Adding/removing %s requires a restart.\n", name);
- return false;
- }
- }
- }
- }
-
- return true;
-}
-
bool SnortConfig::verify()
{
+ bool config_ok = false;
+
if (get_conf()->asn1_mem != asn1_mem)
- {
- ReloadError("Snort Reload: Changing the asn1 memory configuration "
- "requires a restart.\n");
- return false;
- }
+ ReloadError("Changing detection.asn1_mem requires a restart.\n");
- if ( bpf_filter != get_conf()->bpf_filter )
- {
- ReloadError("Snort Reload: Changing the bpf filter configuration "
- "requires a restart.\n");
- return false;
- }
+ else if ( get_conf()->bpf_filter != bpf_filter )
+ ReloadError("Changing packets.bfp_filter requires a restart.\n");
- if ( respond_attempts != get_conf()->respond_attempts ||
- respond_device != get_conf()->respond_device )
- {
- ReloadError("Snort Reload: Changing config response requires a restart.\n");
- return false;
- }
+ else if ( get_conf()->respond_attempts != respond_attempts )
+ ReloadError("Changing active.attempts requires a restart.\n");
- if (get_conf()->chroot_dir != chroot_dir)
- {
- ReloadError("Snort Reload: Changing the chroot directory "
- "configuration requires a restart.\n");
- return false;
- }
+ else if ( get_conf()->respond_device != respond_device )
+ ReloadError("Changing active.device requires a restart.\n");
- if ((get_conf()->run_flags & RUN_FLAG__DAEMON) !=
- (run_flags & RUN_FLAG__DAEMON))
- {
- ReloadError("Snort Reload: Changing to or from daemon mode "
- "requires a restart.\n");
- return false;
- }
+ else if (get_conf()->chroot_dir != chroot_dir)
+ ReloadError("Changing process.chroot requires a restart.\n");
- /* Orig log dir because a chroot might have changed it */
- if (get_conf()->orig_log_dir != orig_log_dir)
- {
- ReloadError("Snort Reload: Changing the log directory "
- "configuration requires a restart.\n");
- return false;
- }
+ else if ((get_conf()->run_flags & RUN_FLAG__DAEMON) != (run_flags & RUN_FLAG__DAEMON))
+ ReloadError("Changing process.daemon requires a restart.\n");
- if (get_conf()->max_attribute_hosts != max_attribute_hosts)
- {
- ReloadError("Snort Reload: Changing max_attribute_hosts "
- "configuration requires a restart.\n");
- return false;
- }
- if (get_conf()->max_attribute_services_per_host != max_attribute_services_per_host)
- {
- ReloadError("Snort Reload: Changing max_attribute_services_per_host "
- "configuration requires a restart.\n");
- return false;
- }
+ else if (get_conf()->orig_log_dir != orig_log_dir)
+ ReloadError("Changing output.logdir requires a restart.\n");
- if ((get_conf()->run_flags & RUN_FLAG__NO_PROMISCUOUS) !=
- (run_flags & RUN_FLAG__NO_PROMISCUOUS))
- {
- ReloadError("Snort Reload: Changing to or from promiscuous mode "
- "requires a restart.\n");
- return false;
- }
+ else if (get_conf()->group_id != group_id)
+ ReloadError("Changing process.setgid requires a restart.\n");
- if (get_conf()->group_id != group_id)
- {
- ReloadError("Snort Reload: Changing the group id configuration requires a restart.\n");
- return false;
- }
+ else if (get_conf()->user_id != user_id)
+ ReloadError("Changing process.setuid requires a restart.\n");
- if (get_conf()->user_id != user_id)
- {
- ReloadError("Snort Reload: Changing the user id configuration requires a restart.\n");
- return false;
- }
+ else if (get_conf()->daq_config->get_mru_size() != daq_config->get_mru_size())
+ ReloadError("Changing daq.snaplen requires a restart.\n");
- if (get_conf()->daq_config->get_mru_size() != daq_config->get_mru_size())
- {
- ReloadError("Snort Reload: Changing the packet snaplen "
- "configuration requires a restart.\n");
- return false;
- }
+ else if (get_conf()->threshold_config->memcap != threshold_config->memcap)
+ ReloadError("Changing alerts.event_filter_memcap requires a restart.\n");
- if (get_conf()->threshold_config->memcap !=
- threshold_config->memcap)
- {
- ReloadError("Snort Reload: Changing the threshold memcap "
- "configuration requires a restart.\n");
- return false;
- }
+ else if (get_conf()->rate_filter_config->memcap != rate_filter_config->memcap)
+ ReloadError("Changing alerts.rate_filter_memcap requires a restart.\n");
- if (get_conf()->rate_filter_config->memcap !=
- rate_filter_config->memcap)
- {
- ReloadError("Snort Reload: Changing the rate filter memcap "
- "configuration requires a restart.\n");
- return false;
- }
+ else if (get_conf()->detection_filter_config->memcap != detection_filter_config->memcap)
+ ReloadError("Changing alerts.detection_filter_memcap requires a restart.\n");
- if (get_conf()->detection_filter_config->memcap !=
- detection_filter_config->memcap)
- {
- ReloadError("Snort Reload: Changing the detection filter memcap "
- "configuration requires a restart.\n");
- return false;
- }
+ else
+ config_ok = true;
- return verify_stream_inspectors();
+ return config_ok;
}
void SnortConfig::set_alert_before_pass(bool enabled)
{
RUN_FLAG__READ = 0x00000001,
RUN_FLAG__DAEMON = 0x00000002,
- RUN_FLAG__NO_PROMISCUOUS = 0x00000004,
+ // unused = 0x00000004,
// unused = 0x00000008,
RUN_FLAG__INLINE = 0x00000010,
namespace snort
{
-class ProtocolReference;
class GHash;
+class ProtocolReference;
class XHash;
struct ProfilerConfig;
struct SnortConfig;
{
private:
void init(const SnortConfig* const, ProtocolReference*);
- bool verify_stream_inspectors();
public:
SnortConfig(const SnortConfig* const other_conf = nullptr);
#include "detection/detection_engine.h"
#include "file_api/file_flows.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "log/messages.h"
#include "search_engines/search_tool.h"
#include "utils/util_cstring.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "utils/util.h"
#include "flow_ip_tracker.h"
+#include "hash/hash_defs.h"
#include "log/messages.h"
#include "protocols/packet.h"
{
FlowStateKey key;
FlowStateValue* value = nullptr;
- bool prune_required = false;
- if (src_addr->less_than(*dst_addr))
+ if ( src_addr->less_than(*dst_addr) )
{
key.ipA = *src_addr;
key.ipB = *dst_addr;
}
value = (FlowStateValue*)ip_map->get_user_data(&key);
- if (!value)
+ if ( !value )
{
- HashNode* node = ip_map->get_node_with_prune(&key, &prune_required);
-
- if (!node)
+ if ( ip_map->insert(&key, nullptr) != HASH_OK )
return nullptr;
-
- if (prune_required)
- {
- ++pmstats.total_frees;
- ++pmstats.alloc_prunes;
- }
-
- memset(node->data, 0, sizeof(FlowStateValue));
- value = (FlowStateValue*)node->data;
+ value = (FlowStateValue*)ip_map->get_user_data();
+ memset(value, 0, sizeof(FlowStateValue));
}
return value;
if ( !ip_map )
{
- ip_map = new XHash(DEFAULT_XHASH_NROWS, sizeof(FlowStateKey), sizeof(FlowStateValue),
- new_memcap, true, nullptr, nullptr, true);
+ ip_map = new XHash(DEFAULT_XHASH_NROWS, sizeof(FlowStateKey),
+ sizeof(FlowStateValue), new_memcap);
}
else
{
formatter->finalize_fields();
memcap = perf->flowip_memcap;
- ip_map = new XHash(DEFAULT_XHASH_NROWS, sizeof(FlowStateKey), sizeof(FlowStateValue),
- memcap, true, nullptr, nullptr, true);
+ ip_map = new XHash(DEFAULT_XHASH_NROWS, sizeof(FlowStateKey), sizeof(FlowStateValue), memcap);
}
FlowIPTracker::~FlowIPTracker()
{
+ const XHashStats& stats = ip_map->get_stats();
+ pmstats.flow_tracker_creates = stats.nodes_created;
+ pmstats.flow_tracker_total_deletes = stats.memcap_deletes;
+ pmstats.flow_tracker_prunes = stats.memcap_prunes;
+
delete ip_map;
}
void FlowIPTracker::reset()
-{
- ip_map->clear();
-}
+{ ip_map->clear_hash(); }
void FlowIPTracker::update(Packet* p)
{
- if (p->has_ip() && !p->is_rebuilt())
+ if ( p->has_ip() && !p->is_rebuilt() )
{
FlowType type = SFS_TYPE_OTHER;
int swapped;
type = SFS_TYPE_UDP;
FlowStateValue* value = find_stats(src_addr, dst_addr, &swapped);
- if (!value)
+ if ( !value )
return;
TrafficStats* stats = &value->traffic_stats[type];
- if (!swapped)
+ if ( !swapped )
{
stats->packets_a_to_b++;
stats->bytes_a_to_b += len;
int swapped;
FlowStateValue* value = find_stats(src_addr, dst_addr, &swapped);
- if (!value)
+ if ( !value )
return 1;
value->state_changes[state]++;
-
return 0;
}
#endif
#include "framework/data_bus.h"
+#include "hash/hash_defs.h"
#include "hash/xhash.h"
#include "log/messages.h"
#include "managers/inspector_manager.h"
if (flow_ip_tracker)
{
unsigned num_freed = 0;
- int result = flow_ip_tracker->get_ip_map()->free_over_allocations(work_limit, &num_freed);
- pmstats.total_frees += num_freed;
- pmstats.reload_frees += num_freed;
+ int result = flow_ip_tracker->get_ip_map()->tune_memory_resources(work_limit, num_freed);
+ pmstats.flow_tracker_reload_deletes += num_freed;
return (result == HASH_OK);
}
else
static const PegInfo perf_module_pegs[] =
{
-
{ CountType::SUM, "packets", "total packets processed by performance monitor" },
- { CountType::SUM, "total_frees", "total flows pruned or freed by performance monitor" },
- { CountType::SUM, "reload_frees", "flows freed on reload with changed memcap" },
- { CountType::SUM, "alloc_prunes", "flows pruned on allocation of IP flows" },
+ { CountType::SUM, "flow_tracker_creates", "total number of flow trackers created" },
+ { CountType::SUM, "flow_tracker_total_deletes", "flow trackers deleted to stay below memcap limit" },
+ { CountType::SUM, "flow_tracker_reload_deletes", "flow trackers deleted due to memcap change on config reload" },
+ { CountType::SUM, "flow_tracker_prunes", "flow trackers pruned for reuse by new flows" },
{ CountType::END, nullptr, nullptr },
};
struct PerfPegStats
{
PegCount total_packets;
- PegCount total_frees;
- PegCount reload_frees;
- PegCount alloc_prunes;
+ PegCount flow_tracker_creates;
+ PegCount flow_tracker_total_deletes;
+ PegCount flow_tracker_reload_deletes;
+ PegCount flow_tracker_prunes;
};
#endif
#include "ps_detect.h"
+#include "hash/hash_defs.h"
#include "hash/xhash.h"
#include "log/messages.h"
#include "protocols/icmp4.h"
};
PADDING_GUARD_END
-static THREAD_LOCAL XHash* portscan_hash = nullptr;
+class PortScanCache : public XHash
+{
+public:
+ PortScanCache(unsigned rows, unsigned key_len, unsigned datasize, unsigned memcap)
+ : XHash(rows, key_len, datasize, memcap)
+ { }
+
+ bool is_node_recovery_ok(HashNode* hnode) override
+ {
+ PS_TRACKER* tracker = (PS_TRACKER*)hnode->data;
+
+ if ( !tracker->priority_node )
+ return true;
+
+ /*
+ ** Cycle through the protos to see if it's past the time.
+ ** We only get here if we ARE a priority node.
+ */
+ if ( tracker->proto.window >= packet_time() )
+ return false;
+
+ return true;
+ }
+};
+
+static THREAD_LOCAL PortScanCache* portscan_hash = nullptr;
extern THREAD_LOCAL PsPegStats spstats;
PS_PKT::PS_PKT(Packet* p)
ipset_free(watch_ip);
}
-/*
-** This function is passed into the hash algorithm, so that
-** we only reuse nodes that aren't priority nodes. We have to make
-** sure that we only track so many priority nodes, otherwise we could
-** have all priority nodes and not be able to allocate more.
-*/
-static int ps_tracker_free(void* key, void* data)
-{
- if (!key || !data)
- return 0;
-
- PS_TRACKER* tracker = (PS_TRACKER*)data;
-
- if (!tracker->priority_node)
- return 0;
-
- /*
- ** Cycle through the protos to see if it's past the time.
- ** We only get here if we ARE a priority node.
- */
- if (tracker->proto.window >= packet_time())
- return 1;
-
- return 0;
-}
-
void ps_cleanup()
{
if ( portscan_hash )
}
int rows = memcap / ps_node_size();
- portscan_hash = new XHash(rows, sizeof(PS_HASH_KEY), sizeof(PS_TRACKER),
- memcap, true, ps_tracker_free, nullptr, true);
+ portscan_hash = new PortScanCache(rows, sizeof(PS_HASH_KEY), sizeof(PS_TRACKER),
+ memcap);
return false;
}
return true;
unsigned num_pruned = 0;
- int result = portscan_hash->free_over_allocations(work_limit, &num_pruned);
+ int result = portscan_hash->tune_memory_resources(work_limit, num_pruned);
spstats.reload_prunes += num_pruned;
return result != HASH_PENDING;
}
void ps_reset()
{
if ( portscan_hash )
- portscan_hash->clear();
+ portscan_hash->clear_hash();
}
// Check scanner and scanned ips to see if we can filter them out.
if ( ht )
return ht;
- auto prev_count = portscan_hash->get_node_count();
+ auto prev_count = portscan_hash->get_num_nodes();
if ( portscan_hash->insert((void*)key, nullptr) != HASH_OK )
return nullptr;
++spstats.trackers;
- if ( prev_count == portscan_hash->get_node_count() )
+ if ( prev_count == portscan_hash->get_num_nodes() )
++spstats.alloc_prunes;
ht = (PS_TRACKER*)portscan_hash->get_mru_user_data();
#include "filters/detection_filter.h"
#include "filters/rate_filter.h"
#include "filters/sfthreshold.h"
-#include "hash/hashfcn.h"
+#include "hash/ghash.h"
+#include "hash/hash_key_operations.h"
#include "hash/xhash.h"
#include "helpers/directory.h"
#include "log/messages.h"
static std::string s_aux_rules;
+class RuleTreeHashKeyOps : public HashKeyOperations
+{
+public:
+ RuleTreeHashKeyOps(int rows)
+ : HashKeyOperations(rows)
+ { }
+
+ unsigned do_hash(const unsigned char* k, int) override
+ {
+ uint32_t a,b,c;
+ const RuleTreeNodeKey* rtnk = (const RuleTreeNodeKey*)k;
+ RuleTreeNode* rtn = rtnk->rtn;
+
+ a = rtn->action;
+ b = rtn->flags;
+ c = (uint32_t)(uintptr_t)rtn->listhead;
+
+ mix(a,b,c);
+
+ a += (uint32_t)(uintptr_t)rtn->src_portobject;
+ b += (uint32_t)(uintptr_t)rtn->dst_portobject;
+ c += (uint32_t)(uintptr_t)rtnk->policyId;
+
+ finalize(a,b,c);
+
+ return c;
+ }
+
+ bool key_compare(const void* k1, const void* k2, size_t) override
+ {
+ assert(k1 && k2);
+
+ const RuleTreeNodeKey* rtnk1 = (const RuleTreeNodeKey*)k1;
+ const RuleTreeNodeKey* rtnk2 = (const RuleTreeNodeKey*)k2;
+
+ if (rtnk1->policyId != rtnk2->policyId)
+ return false;
+
+ if (same_headers(rtnk1->rtn, rtnk2->rtn))
+ return true;
+
+ return false;
+ }
+};
+
+class RuleTreeCache : public XHash
+{
+public:
+ RuleTreeCache(int rows, int key_len)
+ : XHash(rows, key_len)
+ {
+ initialize(new RuleTreeHashKeyOps(nrows));
+ anr_enabled = false;
+ }
+};
+
//-------------------------------------------------------------------------
// private / implementation methods
//-------------------------------------------------------------------------
return deleteRtnFromOtn(otn, get_ips_policy()->policy_id, sc);
}
-static uint32_t rtn_hash_func(HashFnc*, const unsigned char* k, int)
-{
- uint32_t a,b,c;
- const RuleTreeNodeKey* rtnk = (const RuleTreeNodeKey*)k;
- RuleTreeNode* rtn = rtnk->rtn;
-
- a = rtn->action;
- b = rtn->flags;
- c = (uint32_t)(uintptr_t)rtn->listhead;
-
- mix(a,b,c);
-
- a += (uint32_t)(uintptr_t)rtn->src_portobject;
- b += (uint32_t)(uintptr_t)rtn->dst_portobject;
- c += (uint32_t)(uintptr_t)rtnk->policyId;
-
- finalize(a,b,c);
-
- return c;
-}
-
-static bool rtn_compare_func(const void* k1, const void* k2, size_t)
-{
- const RuleTreeNodeKey* rtnk1 = (const RuleTreeNodeKey*)k1;
- const RuleTreeNodeKey* rtnk2 = (const RuleTreeNodeKey*)k2;
-
- if (!rtnk1 || !rtnk2)
- return false;
-
- if (rtnk1->policyId != rtnk2->policyId)
- return false;
-
- if (same_headers(rtnk1->rtn, rtnk2->rtn))
- return true;
-
- return false;
-}
-
int addRtnToOtn(SnortConfig* sc, OptTreeNode* otn, RuleTreeNode* rtn, PolicyId policyId)
{
if (otn->proto_node_num <= policyId)
}
RuleTreeNode* curr = otn->proto_nodes[policyId];
-
if ( curr )
{
deleteRtnFromOtn(otn, policyId, sc, (curr->otnRefCount == 1));
rtn->otnRefCount++;
if (!sc->rtn_hash_table)
- {
- sc->rtn_hash_table = new XHash(
- 10000, sizeof(RuleTreeNodeKey), 0, 0, false, nullptr, nullptr, true);
- sc->rtn_hash_table->set_key_opcodes(rtn_hash_func, rtn_compare_func);
- }
+ sc->rtn_hash_table = new RuleTreeCache(10000, sizeof(RuleTreeNodeKey));
RuleTreeNodeKey key;
memset(&key, 0, sizeof(key));
/*
PortObjects should be normalized, prior to testing
*/
-int PortObjectEqual(PortObject* a, PortObject* b)
+bool PortObjectEqual(PortObject* a, PortObject* b)
{
PortObjectItem* pa;
PortObjectItem* pb;
SF_LNODE* posb;
if ( a->item_list->count != b->item_list->count )
- return 0;
+ return false;
pa = (PortObjectItem*)sflist_first(a->item_list,&posa);
pb = (PortObjectItem*)sflist_first(b->item_list,&posb);
while ( pa && pb )
{
if ( !PortObjectItemsEqual(pa, pb) )
- return 0;
+ return false;
pa = (PortObjectItem*)sflist_next(&posa);
pb = (PortObjectItem*)sflist_next(&posb);
}
if ( pa || pb ) /* both are not done - cannot match */
- return 0;
+ return false;
- return 1; /* match */
+ return true; /* match */
}
/*
int PortObjectNormalize(PortObject*);
void PortObjectToggle(PortObject*);
-int PortObjectEqual(PortObject* poa, PortObject* pob);
+bool PortObjectEqual(PortObject* poa, PortObject* pob);
int PortObjectPortCount(PortObject*);
int PortObjectHasPort(PortObject*, int port);
#include "port_object2.h"
-#include "hash/hashfcn.h"
+#include "hash/ghash.h"
+#include "hash/hash_defs.h"
+#include "hash/hash_key_operations.h"
#include "log/messages.h"
#include "parser/parser.h"
#include "utils/util.h"
#define SWAP_BYTES(a)
#endif
-static unsigned po_rule_hash_func(HashFnc* p, const unsigned char* k, int n)
+class PortObject2HashKeyOps : public HashKeyOperations
{
- unsigned char* key;
- int ikey = *(const int*)k;
+public:
+ PortObject2HashKeyOps(int rows)
+ : HashKeyOperations(rows)
+ { }
- /* Since the input is really an int, put the bytes into a normalized
- * order so that the hash function returns consistent results across
- * on BE & LE hardware. */
- SWAP_BYTES(ikey);
+ unsigned do_hash(const unsigned char* k, int len) override
+ {
+ unsigned char* key;
+ int ikey = *(const int*)k;
- /* Set a pointer to the key to pass to the hashing function */
- key = (unsigned char*)&ikey;
+ /* Since the input is really an int, put the bytes into a normalized
+ * order so that the hash function returns consistent results across
+ * on BE & LE hardware. */
+ SWAP_BYTES(ikey);
- return hashfcn_hash(p, key, n);
-}
+ /* Set a pointer to the key to pass to the hashing function */
+ key = (unsigned char*)&ikey;
+
+ return HashKeyOperations::do_hash(key, len);
+ }
+};
static int* RuleHashToSortedArray(GHash* rh)
{
return ra;
}
-static bool port_object_key_compare_func(const void* k1, const void* k2, size_t len)
-{
- if ( memcmp(k1, k2, len ) )
- return false;
- else
- return true;
-}
-
//-------------------------------------------------------------------------
// PortObject2 - public
//-------------------------------------------------------------------------
PortObject2* po = (PortObject2*)snort_calloc(sizeof(PortObject2));
po->item_list = sflist_new();
po->rule_hash = new GHash(nrules, sizeof(int), 0, snort_free);
-
- /* Use hash function defined above for hashing the key as an int. */
- po->rule_hash->set_key_opcodes(po_rule_hash_func, port_object_key_compare_func);
+ po->rule_hash->set_hashkey_ops(new PortObject2HashKeyOps(nrules));
return po;
}
int* prule = (int*)snort_calloc(sizeof(int));
*prule = *prid;
- if ( ponew->rule_hash->insert(prule, prule) != GHASH_OK )
+ if ( ponew->rule_hash->insert(prule, prule) != HASH_OK )
snort_free(prule);
}
}
int* prid2 = (int*)snort_calloc(sizeof(int));
*prid2 = *prid;
- if ( poa->rule_hash->insert(prid2, prid2) != GHASH_OK )
+ if ( poa->rule_hash->insert(prid2, prid2) != HASH_OK )
snort_free(prid2);
}
return poa;
int* prid2 = (int*)snort_calloc(sizeof(int));
*prid2 = *prid;
- if ( poa->rule_hash->insert(prid2, prid2) != GHASH_OK )
+ if ( poa->rule_hash->insert(prid2, prid2) != HASH_OK )
snort_free(prid2);
}
return poa;
#define PORT_OBJECT2_H
#include "framework/bits.h"
-#include "hash/ghash.h"
#include "utils/sflsq.h"
//-------------------------------------------------------------------------
// PortObject2 is similar to PortObject
//-------------------------------------------------------------------------
+namespace snort
+{
+class GHash;
+}
struct PortObject;
#include <memory>
-#include "hash/hashfcn.h"
+#include "hash/ghash.h"
+#include "hash/hash_defs.h"
+#include "hash/hash_key_operations.h"
#include "log/messages.h"
#include "main/snort_debug.h"
#include "utils/util.h"
snort_free(p);
}
-static unsigned plx_hash(HashFnc* p, const unsigned char* d, int)
-{
- unsigned hash = p->seed;
- const plx_t* plx = *(plx_t* const*)d;
-
- for ( int i = 0; i < plx->n; i++ )
- {
- unsigned char* pc_ptr = (unsigned char*)&plx->p[i];
-
- for ( unsigned k = 0; k < sizeof(void*); k++ )
- {
- hash *= p->scale;
- hash += pc_ptr[k];
- }
- }
- return hash ^ p->hardener;
-}
-
/* for sorting an array of pointers */
static inline int p_keycmp(const void* a, const void* b)
{
return 0; /* they are equal */
}
-/*
- Hash Key Comparisons for treating plx_t types as Keys
-
- return values memcmp style
-
- this only needs to produce 0 => exact match, otherwise not.
- -1, and +1 are not strictly needed, they could both return
- a non zero value for the purposes of hashing and searching.
-*/
-static bool plx_keycmp(const void* a, const void* b, size_t)
+class PlxHashKeyOps : public HashKeyOperations
{
- const plx_t* pla = *(plx_t* const*)a;
- const plx_t* plb = *(plx_t* const*)b;
+public:
+ PlxHashKeyOps(int rows)
+ : HashKeyOperations(rows)
+ { }
- if ( pla->n < plb->n )
- return false;
+ unsigned do_hash(const unsigned char* k, int) override
+ {
+ unsigned hash = seed;
+ const plx_t* plx = *(plx_t* const*)k;
+
+ for ( int i = 0; i < plx->n; i++ )
+ {
+ unsigned char* pc_ptr = (unsigned char*)&plx->p[i];
- if ( pla->n > plb->n )
- return false;
+ for ( unsigned k = 0; k < sizeof(void*); k++ )
+ {
+ hash *= scale;
+ hash += pc_ptr[k];
+ }
+ }
+ return hash ^ hardener;
+ }
- for ( int i = 0; i < pla->n; i++ )
+ bool key_compare(const void* k1, const void* k2, size_t) override
{
- if ( p_keycmp(&pla->p[i], &plb->p[i]) )
+ const plx_t* pla = *(plx_t* const*)k1;
+ const plx_t* plb = *(plx_t* const*)k2;
+
+ if ( pla->n < plb->n )
return false;
- }
- return true; /* they are equal */
-}
+ if ( pla->n > plb->n )
+ return false;
+
+ for ( int i = 0; i < pla->n; i++ )
+ {
+ if ( p_keycmp(&pla->p[i], &plb->p[i]) )
+ return false;
+ }
+
+ return true; /* they are equal */ }
+};
//-------------------------------------------------------------------------
// PortTable - private - other
//-------------------------------------------------------------------------
-/*
- Hash Key Comparisons for treating PortObjects as Keys
-
- return values memcmp style
-*/
-static bool PortObject_keycmp(const void* a, const void* b, size_t)
+class PortObjectHashKeyOps : public HashKeyOperations
{
- return PortObjectEqual(*(PortObject* const*)a, *(PortObject* const*)b);
-}
-
-/*
- Hash routine for hashing PortObjects as Keys
+public:
+ PortObjectHashKeyOps(int rows)
+ : HashKeyOperations(rows)
+ { }
- p - HashFnc *
- d - PortObject *
- n = 4 bytes (sizeof*) - not used
+ unsigned do_hash(const unsigned char* k, int) override
+ {
+ unsigned hash = seed;
+ const PortObject* po = *(PortObject* const*)k;
+ SF_LNODE* pos;
- Don't use this for type=ANY port objects
-*/
-static unsigned PortObject_hash(HashFnc* p, const unsigned char* d, int)
-{
- unsigned hash = p->seed;
- const PortObject* po = *(PortObject* const*)d;
- SF_LNODE* pos;
+ for (PortObjectItem* poi = (PortObjectItem*)sflist_first(po->item_list, &pos);
+ poi != nullptr;
+ poi = (PortObjectItem*)sflist_next(&pos) )
+ {
+ if ( poi->any() )
+ continue;
- /* hash up each item */
- for (PortObjectItem* poi = (PortObjectItem*)sflist_first(po->item_list, &pos);
- poi != nullptr;
- poi = (PortObjectItem*)sflist_next(&pos) )
- {
- if ( poi->any() )
- continue;
+ hash *= scale;
+ hash += poi->lport & 0xff;
+ hash *= scale;
+ hash += (poi->lport >> 8) & 0xff;
- hash *= p->scale;
- hash += poi->lport & 0xff;
- hash *= p->scale;
- hash += (poi->lport >> 8) & 0xff;
+ hash *= scale;
+ hash += poi->hport & 0xff;
+ hash *= scale;
+ hash += (poi->hport >> 8) & 0xff;
+ }
+ return hash ^ hardener;
+ }
- hash *= p->scale;
- hash += poi->hport & 0xff;
- hash *= p->scale;
- hash += (poi->hport >> 8) & 0xff;
+ bool key_compare(const void* k1, const void* k2, size_t) override
+ {
+ return PortObjectEqual(*(PortObject* const*)k1, *(PortObject* const*)k2);
}
- return hash ^ p->hardener;
-}
+};
/*
* Merge multiple PortObjects into a final PortObject2,
// Add the Merged PortObject2 to the PortObject2 hash table keyed by ports.
int stat = mhash->insert(&ponew, ponew);
// This is possible since PLX hash on a different key
- if ( stat == GHASH_INTABLE )
+ if ( stat == HASH_INTABLE )
{
PortObject2* pox = (PortObject2*)mhash->find(&ponew);
assert( pox );
// Add the plx node to the PLX hash table
stat = mhashx->insert(&plx_tmp, ponew);
- if ( stat == GHASH_INTABLE )
+ if ( stat == HASH_INTABLE )
FatalError("Could not add merged plx to PLX HASH table-INTABLE\n");
return ponew;
// Create a Merged Port Object Table - hash by ports, no user keys, don't free data
GHash* mhash = new GHash(PO_HASH_TBL_ROWS, sizeof(PortObject*), 0, nullptr);
-
- mhash->set_key_opcodes(PortObject_hash, PortObject_keycmp);
+ mhash->set_hashkey_ops(new PortObjectHashKeyOps(PO_HASH_TBL_ROWS));
p->pt_mpo_hash = mhash;
// Create a Merged Port Object Table - hash by pointers, no user keys, don't free data
GHash* mhashx = new GHash(PO_HASH_TBL_ROWS, sizeof(plx_t*), 0, nullptr);
- mhashx->set_key_opcodes(plx_hash, plx_keycmp);
+ mhashx->set_hashkey_ops(new PlxHashKeyOps(PO_HASH_TBL_ROWS));
p->pt_mpxo_hash = mhashx;
SF_LIST* plx_list = sflist_new();
#ifndef PORT_TABLE_H
#define PORT_TABLE_H
-#include "hash/ghash.h"
#include "ports/port_item.h"
#include "ports/port_object.h"
#include "ports/port_object2.h"
#include "utils/sflsq.h"
+namespace snort
+{
+class GHash;
+}
+
//-------------------------------------------------------------------------
// PortTable - provides support to analyze the Port List objects defined by
// the user as either PortVar entries or simply as inline rule port list
#include "port_var_table.h"
+#include "hash/ghash.h"
+#include "hash/hash_defs.h"
+
using namespace snort;
//-------------------------------------------------------------------------
int PortVarTableAdd(PortVarTable* h, PortObject* po)
{
int stat = h->insert(po->name, po);
- if ( stat == GHASH_INTABLE )
+ if ( stat == HASH_INTABLE )
return 1;
- if ( stat == GHASH_OK )
+ if ( stat == HASH_OK )
return 0;
return -1;
#ifndef PORT_VAR_TABLE_H
#define PORT_VAR_TABLE_H
-#include "hash/ghash.h"
#include "ports/port_object.h"
#include "ports/port_table.h"
+namespace snort
+{
+class GHash;
+}
+
//-------------------------------------------------------------------------
// PortVarTable
// port lists may be defined as 'name port-list'
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/cursor.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/cursor.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/cursor.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/cursor.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/module.h"
#include "framework/ips_option.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "target_based/snort_protocols.h"
#include "utils/util.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "utils/util.h"
#include "framework/cursor.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "dce_common.h"
#include "framework/cursor.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "protocols/packet.h"
#include "profiler/profiler.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "detection/detection_engine.h"
#include "detection/detection_util.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "file_api/file_service.h"
#include "protocols/packet.h"
#include "stream/stream.h"
// gtp_info rule option implementation
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "framework/cursor.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "protocols/packet.h"
#include "profiler/profiler.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "protocols/packet.h"
#include "profiler/profiler.h"
/*
* The purpose of this Packet subclass is to enable H2I to take direction from http_inspect on
* whether or not to send a frame to detection. When http_inspect is processing normal HTTP/1.1
- * traffic it is dealing with a 'real' packet that has a context, the field on which disable_all()
+ * traffic it is dealing with a real packet that has a context, the field on which disable_all()
* is called to disable detection on that packet. With HTTP/2 traffic, http_inspect is processing a
* dummy packet that H2I created, which does not contain a context object. Rather than create an
* entire new context object when we really only need a bool, http_inspect checks if the flow is
//--------------------------------------------------------------------------
// http_buffer_info.cc author Brandon Stultz <brastult@cisco.com>
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "http_buffer_info.h"
using namespace snort;
#include "http_msg_status.h"
#include "http_msg_trailer.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
using namespace HttpCommon;
using namespace HttpEnums;
#include "http_common.h"
#include "http_enum.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
using namespace HttpCommon;
using namespace HttpEnums;
#include "ips_http.h"
#include "framework/cursor.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "log/messages.h"
#include "parser/parse_utils.h"
#include "protocols/packet.h"
#include "framework/cursor.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "protocols/packet.h"
#include "profiler/profiler.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "protocols/packet.h"
#include "profiler/profiler.h"
#include "framework/cursor.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "protocols/packet.h"
#include "profiler/profiler.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "protocols/packet.h"
#include "profiler/profiler.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "log/messages.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "log/messages.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include <cstring>
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
/*
* Trim spaces non-destructively on both sides of string : '', \t, \n, \r
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "protocols/ssl.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "protocols/ssl.h"
if ( config.flow_cache_cfg.max_flows > 0 )
flow_con->init_exp(config.flow_cache_cfg.max_flows);
+#ifdef REG_TEST
FlushBucket::set(config.footprint);
+#else
+ FlushBucket::set();
+#endif
}
void StreamBase::tterm()
static const Parameter s_params[] =
{
+#ifdef REG_TEST
{ "footprint", Parameter::PT_INT, "0:max32", "0",
"use zero for production, non-zero for testing at given size (for TCP and user)" },
+#endif
{ "ip_frags_only", Parameter::PT_BOOL, nullptr, "false",
"don't process non-frag flows" },
{
PktType type = PktType::NONE;
+#ifdef REG_TEST
if ( v.is("footprint") )
{
config.footprint = v.get_uint32();
return true;
}
- else if ( v.is("ip_frags_only") )
+#endif
+
+ if ( v.is("ip_frags_only") )
{
if ( v.get_bool() )
c->set_run_flags(RUN_FLAG__IP_FRAGS_ONLY);
return true;
}
-// FIXIT-L the detection of stream.xxx_cache changes below is a temporary workaround
-// remove this check when stream.xxx_cache params become reloadable
bool StreamModule::end(const char*, int, SnortConfig* sc)
{
if ( reload_resource_manager.initialize(config) )
// Stream handler to adjust allocated resources as needed on a config reload
bool StreamReloadResourceManager::initialize(const StreamModuleConfig& config_)
{
- // FIXIT-L - saving config here to check footprint change is a bit of a hack,
- if ( Snort::is_reloading() )
+ // saving a copy of the config only works here because there is only
+ // one stream inspector per packet thread...
+ if ( !Snort::is_reloading() )
{
- if ( config.footprint != config_.footprint )
- {
- // FIXIT-M - reinit FlushBucket...
- ReloadError("Changing of stream.footprint requires a restart\n");
- return false;
- }
-
config = config_;
- return true;
+ return false;
}
+#ifdef REG_TEST
+ if ( config.footprint != config_.footprint )
+ {
+ ReloadError("Changing of stream.footprint requires a restart\n");
+ return false;
+ }
+#endif
config = config_;
- return false;
+ return true;
}
bool StreamReloadResourceManager::tinit()
struct StreamModuleConfig
{
FlowCacheConfig flow_cache_cfg;
+#ifdef REG_TEST
unsigned footprint = 0;
+#endif
+
};
class StreamReloadResourceManager : public snort::ReloadResourceTuner
assert(s_flush_bucket);
}
+void FlushBucket::set()
+{ set(0); }
+
void FlushBucket::clear()
{
delete s_flush_bucket;
static uint16_t get_size();
static void set(unsigned sz);
+ static void set();
static void clear();
protected:
#include "detection/detection_engine.h"
#include "framework/ips_option.h"
#include "framework/module.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "main/snort_config.h"
#include "profiler/profiler.h"
#include "framework/ips_option.h"
#include "framework/module.h"
#include "framework/range.h"
-#include "hash/hashfcn.h"
+#include "hash/hash_key_operations.h"
#include "profiler/profiler_defs.h"
#include "tcp_session.h"
event_gen.h
infractions.h
kmap.h
+ memcap_allocator.h
primed_allocator.h
safec.h
segment_mem.h
sflsq.h
- sfmemcap.h
stats.h
util.h
util_ber.h
kmap.cc
segment_mem.cc
sflsq.cc
- sfmemcap.cc
snort_bounds.h
stats.cc
util.cc
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+
+// memcap_allocator.h author davis mcpherson davmcphe@cisco.com
+
+// MemCapAllocator manages allocation and freeing of fixed size blocks of memory with
+// an option of limiting total size of allocations to a specified capacity
+
+#ifndef MEMCAP_ALLOCATOR_H
+#define MEMCAP_ALLOCATOR_H
+
+#include "framework/counts.h"
+#include "utils/util.h"
+
+class MemCapAllocator
+{
+public:
+ MemCapAllocator(unsigned long capacity, unsigned long allocation_size)
+ : mem_capacity(capacity), allocation_size(allocation_size)
+ { }
+
+ void* allocate()
+ {
+ void* data = nullptr;
+ if ( is_space_available() )
+ {
+ data = snort_calloc(allocation_size);
+ mem_allocated += allocation_size;
+ ++allocation_requests;
+ }
+ else
+ ++no_memory_available;
+
+ return data;
+ }
+
+ void free(void* mem)
+ {
+ snort_free(mem);
+ mem_allocated -= allocation_size;
+ ++free_requests;
+ }
+
+ bool is_space_available() const
+ {
+ if ( !mem_capacity )
+ return true;
+ else
+ return mem_allocated + allocation_size <= mem_capacity;
+ }
+
+ bool is_over_capacity() const
+ {
+ if ( !mem_capacity )
+ return false;
+ else
+ return mem_allocated > mem_capacity;
+ }
+
+ void set_mem_capacity(unsigned long capacity)
+ { mem_capacity = capacity; }
+
+ unsigned long get_mem_capacity() const
+ { return mem_capacity; }
+
+ unsigned long get_allocation_size() const
+ { return allocation_size; }
+
+ unsigned long get_mem_allocated() const
+ { return mem_allocated; }
+
+ PegCount get_allocation_requests() const
+ { return allocation_requests; }
+
+ PegCount get_free_requests() const
+ { return free_requests; }
+
+ PegCount get_no_memory_available() const
+ { return no_memory_available; }
+
+private:
+ unsigned long mem_capacity;
+ unsigned long allocation_size;
+ unsigned long mem_allocated = 0;
+
+ PegCount allocation_requests = 0;
+ PegCount free_requests = 0;
+ PegCount no_memory_available = 0;
+};
+
+#endif
+
+++ /dev/null
-//--------------------------------------------------------------------------
-// Copyright (C) 2014-2020 Cisco and/or its affiliates. All rights reserved.
-// Copyright (C) 2003-2013 Sourcefire, Inc.
-//
-// 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.
-//--------------------------------------------------------------------------
-
-/*
- sfmemcap.c
-
- These functions wrap the alloc & free functions. They enforce a memory cap using
- the MEMCAP structure. The MEMCAP structure tracks memory usage. Each allocation
- has 4 bytes added to it so we can store the allocation size. This allows us to
- free a block and accurately track how much memory was recovered.
-
- Marc Norton
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "sfmemcap.h"
-
-#include "util.h"
-#include "util_cstring.h"
-
-/*
-* Set max # bytes & init other variables.
-*/
-void sfmemcap_init(MEMCAP* mc, unsigned long nbytes)
-{
- mc->memcap = nbytes;
- mc->memused= 0;
- mc->nblocks= 0;
-}
-
-/*
-* Allocate some memory
-*/
-void* sfmemcap_alloc(MEMCAP* mc, unsigned long nbytes)
-{
- long* data;
-
- //printf("sfmemcap_alloc: %d bytes requested, memcap=%d,
- // used=%d\n",nbytes,mc->memcap,mc->memused);
-
- nbytes += sizeof(long);
-
- /* Check if we are limiting memory use */
- if ( mc->memcap > 0 )
- {
- /* Check if we've maxed out our memory - if we are tracking memory */
- if ( (mc->memused + nbytes) > mc->memcap )
- {
- return nullptr;
- }
- }
-
- //data = (long*)snort_alloc( nbytes );
- data = (long*)snort_calloc(nbytes);
- *data++ = (long)nbytes;
-
- mc->memused += nbytes;
- mc->nblocks++;
-
- return data;
-}
-
-/*
-* Free some memory
-*/
-void sfmemcap_free(MEMCAP* mc, void* p)
-{
- long* q;
-
- q = (long*)p;
- q--;
- mc->memused -= (unsigned)(*q);
- mc->nblocks--;
-
- snort_free(q);
-}
-
-/*
-* For debugging.
-*/
-void sfmemcap_showmem(MEMCAP* mc)
-{
- fprintf(stderr, "memcap: memcap = %lu bytes,",mc->memcap);
- fprintf(stderr, " memused= %lu bytes,",mc->memused);
- fprintf(stderr, " nblocks= %d blocks\n",mc->nblocks);
-}
-
+++ /dev/null
-//--------------------------------------------------------------------------
-// Copyright (C) 2014-2020 Cisco and/or its affiliates. All rights reserved.
-// Copyright (C) 2003-2013 Sourcefire, Inc.
-//
-// 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.
-//--------------------------------------------------------------------------
-
-#ifndef SFMEMCAP_H
-#define SFMEMCAP_H
-
-// _alloc and _free wrappers that enforce a memory cap
-
-struct MEMCAP
-{
- unsigned long memused;
- unsigned long memcap;
- int nblocks;
-};
-
-// FIXIT-L could be refactored as a class but should be deleted
-void sfmemcap_init(MEMCAP* mc, unsigned long nbytes);
-void* sfmemcap_alloc(MEMCAP* mc, unsigned long nbytes);
-void sfmemcap_showmem(MEMCAP* mc);
-void sfmemcap_free(MEMCAP* mc, void* memory);
-
-#endif
-
../boyer_moore.cc
)
+add_cpputest( memcap_allocator_test )
+
add_catch_test( bitop_test )
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+
+// memcap_allocator_test.cc author davis mcpherson <davmcphe@cisco.com>
+// unit tests for MemCapAllocator class
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "../memcap_allocator.h"
+
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+
+const unsigned long ALLOCATION_SIZE = 250;
+const unsigned long NO_MEM_CAP_LIMIT = 0;
+const unsigned NO_MEMCAP_LIMIT_ALLOCATIONS = 10000;
+
+const unsigned long MEM_CAP_5000 = 5000;
+const unsigned MAX_ALLOCATIONS_5000 = MEM_CAP_5000 / ALLOCATION_SIZE;
+
+const unsigned long MEM_CAP_10000 = 10000;
+const unsigned MAX_ALLOCATIONS_10000 = MEM_CAP_10000 / ALLOCATION_SIZE;
+
+const unsigned long MEM_CAP_20000 = 20000;
+const unsigned MAX_ALLOCATIONS_20000 = MEM_CAP_20000 / ALLOCATION_SIZE;
+
+TEST_GROUP(memcap_allocator)
+{ };
+
+TEST(memcap_allocator, no_memcap_limit_allocator_test)
+{
+ MemCapAllocator* mca = new MemCapAllocator(NO_MEM_CAP_LIMIT, ALLOCATION_SIZE);
+ CHECK(mca);
+ CHECK(mca->get_allocation_size() == ALLOCATION_SIZE);
+ CHECK(mca->get_mem_capacity() == NO_MEM_CAP_LIMIT);
+ CHECK(mca->is_space_available());
+ CHECK(!mca->is_over_capacity());
+ CHECK(mca->get_mem_allocated() == 0);
+ CHECK(mca->get_allocation_requests() == 0);
+ CHECK(mca->get_free_requests() == 0);
+ CHECK(mca->get_no_memory_available() == 0);
+ delete mca;
+}
+
+TEST(memcap_allocator, no_memcap_limit_allocations_test)
+{
+ MemCapAllocator* mca = new MemCapAllocator(NO_MEM_CAP_LIMIT, ALLOCATION_SIZE);
+ CHECK(mca);
+
+ unsigned bytes_allocated;
+ uint8_t** mem_blocks = (uint8_t**)snort_alloc(sizeof(uint8_t*) * NO_MEMCAP_LIMIT_ALLOCATIONS);
+ for ( unsigned i = 0; i < NO_MEMCAP_LIMIT_ALLOCATIONS; i++)
+ {
+ mem_blocks[i] = (uint8_t*)mca->allocate();
+ bytes_allocated = (i + 1) * ALLOCATION_SIZE;
+ CHECK(mem_blocks[i]);
+ CHECK(mca->get_mem_allocated() == bytes_allocated);
+ CHECK(mca->is_space_available());
+ CHECK(!mca->is_over_capacity());
+ }
+
+ CHECK(mca->get_allocation_requests() == NO_MEMCAP_LIMIT_ALLOCATIONS);
+ CHECK(mca->get_free_requests() == 0);
+ CHECK(mca->get_no_memory_available() == 0);
+
+ for ( unsigned i = 0; i < NO_MEMCAP_LIMIT_ALLOCATIONS; i++)
+ {
+ mca->free(mem_blocks[i]);
+ bytes_allocated = (NO_MEMCAP_LIMIT_ALLOCATIONS - (i + 1)) * ALLOCATION_SIZE;
+ CHECK(mca->get_mem_allocated() == bytes_allocated);
+ }
+
+ CHECK(mca->is_space_available());
+ CHECK(!mca->is_over_capacity());
+ CHECK(mca->get_mem_allocated() == 0);
+ CHECK(mca->get_free_requests() == NO_MEMCAP_LIMIT_ALLOCATIONS);
+ CHECK(mca->get_no_memory_available() == 0);
+
+ snort_free(mem_blocks);
+ delete mca;
+}
+
+TEST(memcap_allocator, create_memcap_allocator_test)
+{
+ MemCapAllocator* mca = new MemCapAllocator(MEM_CAP_10000, ALLOCATION_SIZE);
+ CHECK(mca);
+ CHECK(mca->get_allocation_size() == ALLOCATION_SIZE);
+ CHECK(mca->get_mem_capacity() == MEM_CAP_10000);
+ mca->set_mem_capacity(MEM_CAP_20000);
+ CHECK(mca->get_mem_capacity() == MEM_CAP_20000);
+ CHECK(mca->get_mem_allocated() == 0);
+ CHECK(mca->get_allocation_requests() == 0);
+ CHECK(mca->get_free_requests() == 0);
+ CHECK(mca->get_no_memory_available() == 0);
+ delete mca;
+}
+
+TEST(memcap_allocator, allocate_and_free_memory_test)
+{
+ MemCapAllocator* mca = new MemCapAllocator(MEM_CAP_10000, ALLOCATION_SIZE);
+ CHECK(mca);
+ CHECK(mca->is_space_available());
+ CHECK(!mca->is_over_capacity());
+
+ uint8_t* mem = (uint8_t*)mca->allocate();
+ CHECK(mem);
+ CHECK(mca->get_mem_allocated() == ALLOCATION_SIZE);
+ CHECK(mca->is_space_available());
+ CHECK(!mca->is_over_capacity());
+ CHECK(mca->get_allocation_requests() == 1);
+ CHECK(mca->get_free_requests() == 0);
+ CHECK(mca->get_no_memory_available() == 0);
+
+ mca->free(mem);
+ CHECK(mca->get_mem_allocated() == 0);
+ CHECK(mca->is_space_available());
+ CHECK(!mca->is_over_capacity());
+ CHECK(mca->get_allocation_requests() == 1);
+ CHECK(mca->get_free_requests() == 1);
+ CHECK(mca->get_no_memory_available() == 0);
+
+ delete mca;
+}
+
+TEST(memcap_allocator, max_allocations_test)
+{
+ MemCapAllocator* mca = new MemCapAllocator(MEM_CAP_10000, ALLOCATION_SIZE);
+ CHECK(mca);
+
+ unsigned bytes_allocated;
+ uint8_t* mem_blocks[MAX_ALLOCATIONS_10000];
+ for ( unsigned i = 0; i < MAX_ALLOCATIONS_10000; i++)
+ {
+ mem_blocks[i] = (uint8_t*)mca->allocate();
+ bytes_allocated = (i + 1) * ALLOCATION_SIZE;
+ CHECK(mem_blocks[i]);
+ CHECK(mca->get_mem_allocated() == bytes_allocated);
+ if ( i < MAX_ALLOCATIONS_10000 - 1 )
+ {
+ CHECK(mca->is_space_available());
+ }
+ else
+ {
+ CHECK(!mca->is_space_available());
+ }
+ CHECK(!mca->is_over_capacity());
+ }
+ CHECK(mca->get_allocation_requests() == MAX_ALLOCATIONS_10000);
+ CHECK(mca->get_free_requests() == 0);
+ CHECK(mca->get_no_memory_available() == 0);
+
+ uint8_t* mem = (uint8_t*)mca->allocate();
+ CHECK(!mem);
+ CHECK(mca->get_no_memory_available() == 1);
+ mem = (uint8_t*)mca->allocate();
+ CHECK(!mem);
+ mem = (uint8_t*)mca->allocate();
+ CHECK(!mem);
+ CHECK(mca->get_no_memory_available() == 3);
+ mca->free(mem_blocks[0]);
+ mem_blocks[0] = (uint8_t*)mca->allocate();
+ CHECK(mem_blocks[0]);
+ CHECK(mca->get_free_requests() == 1);
+ CHECK(mca->get_no_memory_available() == 3);
+
+ mca->set_mem_capacity(MEM_CAP_20000);
+ mem = (uint8_t*)mca->allocate();
+ CHECK(mem);
+ mca->set_mem_capacity(MEM_CAP_5000);
+ CHECK(mca->is_over_capacity());
+ mca->free(mem);
+ CHECK(mca->get_free_requests() == 2);
+
+ for ( unsigned i = 0; i < MAX_ALLOCATIONS_5000; i++)
+ {
+ CHECK(!mca->is_space_available());
+ mca->free(mem_blocks[i]);
+ bytes_allocated = (MAX_ALLOCATIONS_10000 - (i + 1)) * ALLOCATION_SIZE;
+ CHECK(mca->get_mem_allocated() == bytes_allocated);
+ }
+
+ CHECK(!mca->is_space_available());
+ CHECK(!mca->is_over_capacity());
+
+ for ( unsigned i = MAX_ALLOCATIONS_5000; i < MAX_ALLOCATIONS_10000; i++)
+ {
+ mca->free(mem_blocks[i]);
+ bytes_allocated = (MAX_ALLOCATIONS_10000 - (i + 1)) * ALLOCATION_SIZE;
+ CHECK(mca->get_mem_allocated() == bytes_allocated);
+ CHECK(mca->is_space_available());
+ CHECK(!mca->is_over_capacity());
+ }
+
+ CHECK(mca->get_mem_allocated() == 0);
+ CHECK(mca->get_free_requests() == MAX_ALLOCATIONS_10000 + 2);
+ CHECK(mca->get_no_memory_available() == 3);
+
+ delete mca;
+}
+
+int main(int argc, char** argv)
+{
+ return CommandLineTestRunner::RunAllTests(argc, argv);
+}