@ -37,21 +37,22 @@
# include "rust.h"
# include <hs.h>
static const char * HSCacheConstructFPath ( const char * folder_path , const char * hs_db_hash )
{
static char hash_file_path [ PATH_MAX ] ;
# define HS_CACHE_FILE_VERSION "2"
# define HS_CACHE_FILE_SUFFIX "_v" HS_CACHE_FILE_VERSION ".hs"
char hash_file_path_suffix [ ] = " _v1.hs " ;
static int16_t HSCacheConstructFPath (
const char * dir_path , const char * db_hash , char * out_path , uint16_t out_path_size )
{
char filename [ NAME_MAX ] ;
uint64_t r = snprintf ( filename , sizeof ( filename ) , " %s%s " , hs_db_hash , hash_file_path_suffix ) ;
if ( r ! = ( uint64_t ) ( strlen ( hs_ db_hash) + strlen ( hash_file_path_suffix ) ) )
return NULL ;
uint64_t r = snprintf ( filename , sizeof ( filename ) , " %s " HS_CACHE_FILE_SUFFIX , db_hash ) ;
if ( r ! = ( uint64_t ) ( strlen ( db_hash) + strlen ( HS_CACHE_FILE_SUFFIX ) ) )
return - 1 ;
r = PathMerge ( hash_file_path, sizeof ( hash_file_path ) , folde r_path, filename ) ;
r = PathMerge ( out_path, out_path_size , di r_path, filename ) ;
if ( r )
return NULL ;
return - 1 ;
return hash_file_path ;
return 0 ;
}
static char * HSReadStream ( const char * file_path , size_t * buffer_sz )
@ -121,8 +122,11 @@ static void SCHSCachePatternHash(const SCHSPattern *p, SCSha256 *sha256)
int HSLoadCache ( hs_database_t * * hs_db , const char * hs_db_hash , const char * dirpath )
{
const char * hash_file_static = HSCacheConstructFPath ( dirpath , hs_db_hash ) ;
if ( hash_file_static = = NULL )
char hash_file_static [ PATH_MAX ] ;
int ret = ( int ) HSCacheConstructFPath (
dirpath , hs_db_hash , hash_file_static , sizeof ( hash_file_static ) ) ;
if ( ret ! = 0 )
return - 1 ;
SCLogDebug ( " Loading the cached HS DB from %s " , hash_file_static ) ;
@ -131,7 +135,6 @@ int HSLoadCache(hs_database_t **hs_db, const char *hs_db_hash, const char *dirpa
FILE * db_cache = fopen ( hash_file_static , " r " ) ;
char * buffer = NULL ;
int ret = 0 ;
if ( db_cache ) {
size_t buffer_size ;
buffer = HSReadStream ( hash_file_static , & buffer_size ) ;
@ -170,15 +173,20 @@ static int HSSaveCache(hs_database_t *hs_db, const char *hs_db_hash, const char
static bool notified = false ;
char * db_stream = NULL ;
size_t db_size ;
int ret = - 1 ;
int ret ;
hs_error_t err = hs_serialize_database ( hs_db , & db_stream , & db_size ) ;
if ( err ! = HS_SUCCESS ) {
SCLogWarning ( " Failed to serialize Hyperscan database: %s " , HSErrorToStr ( err ) ) ;
ret = - 1 ;
goto cleanup ;
}
const char * hash_file_static = HSCacheConstructFPath ( dstpath , hs_db_hash ) ;
char hash_file_static [ PATH_MAX ] ;
ret = ( int ) HSCacheConstructFPath (
dstpath , hs_db_hash , hash_file_static , sizeof ( hash_file_static ) ) ;
if ( ret ! = 0 )
goto cleanup ;
SCLogDebug ( " Caching the compiled HS at %s " , hash_file_static ) ;
if ( SCPathExists ( hash_file_static ) ) {
// potentially signs that it might not work as expected as we got into
@ -198,6 +206,7 @@ static int HSSaveCache(hs_database_t *hs_db, const char *hs_db_hash, const char
hash_file_static ) ;
notified = true ;
}
ret = - 1 ;
goto cleanup ;
}
size_t r = fwrite ( db_stream , sizeof ( db_stream [ 0 ] ) , db_size , db_cache_out ) ;
@ -217,7 +226,6 @@ static int HSSaveCache(hs_database_t *hs_db, const char *hs_db_hash, const char
goto cleanup ;
}
ret = 0 ;
cleanup :
if ( db_stream )
SCFree ( db_stream ) ;
@ -270,4 +278,187 @@ void HSSaveCacheIterator(void *data, void *aux)
}
}
void HSCacheFilenameUsedIterator ( void * data , void * aux )
{
PatternDatabase * pd = ( PatternDatabase * ) data ;
struct HsInUseCacheFilesIteratorData * iter_data = ( struct HsInUseCacheFilesIteratorData * ) aux ;
if ( pd - > no_cache | | ! pd - > cached )
return ;
char hs_db_hash [ SC_SHA256_LEN * 2 + 1 ] ; // * 2 for hex +1 for nul terminator
if ( HSHashDb ( pd , hs_db_hash , ARRAY_SIZE ( hs_db_hash ) ) ! = 0 ) {
return ;
}
char * fpath = SCCalloc ( PATH_MAX , sizeof ( char ) ) ;
if ( fpath = = NULL ) {
SCLogWarning ( " Failed to allocate memory for cache file path " ) ;
return ;
}
if ( HSCacheConstructFPath ( iter_data - > cache_path , hs_db_hash , fpath , PATH_MAX ) ) {
SCFree ( fpath ) ;
return ;
}
int r = HashTableAdd ( iter_data - > tbl , ( void * ) fpath , ( uint16_t ) strlen ( fpath ) ) ;
if ( r < 0 ) {
SCLogWarning ( " Failed to add used cache file path %s to hash table " , fpath ) ;
SCFree ( fpath ) ;
}
}
/**
* \ brief Check if HS cache file is stale by age .
*
* \ param mtime File modification time .
* \ param cutoff Time cutoff ( files older than this will be removed ) .
*
* \ retval true if file should be pruned , false otherwise .
*/
static bool HSPruneFileByAge ( time_t mtime , time_t cutoff )
{
return mtime < cutoff ;
}
/**
* \ brief Check if HS cache file is version - compatible .
*
* \ param filename Cache file name .
*
* \ retval true if file should be pruned , false otherwise .
*/
static bool HSPruneFileByVersion ( const char * filename )
{
if ( strlen ( filename ) < strlen ( HS_CACHE_FILE_SUFFIX ) ) {
return true ;
}
const char * underscore = strrchr ( filename , ' _ ' ) ;
if ( underscore = = NULL | | strcmp ( underscore , HS_CACHE_FILE_SUFFIX ) ! = 0 ) {
return true ;
}
return false ;
}
int SCHSCachePruneEvaluate ( MpmConfig * mpm_conf , HashTable * inuse_caches )
{
if ( mpm_conf = = NULL | | mpm_conf - > cache_dir_path = = NULL )
return - 1 ;
if ( mpm_conf - > cache_max_age_seconds = = 0 )
return 0 ; // disabled
const time_t now = time ( NULL ) ;
if ( now = = ( time_t ) - 1 ) {
return - 1 ;
} else if ( mpm_conf - > cache_max_age_seconds > = ( uint64_t ) now ) {
return 0 ;
}
DIR * dir = opendir ( mpm_conf - > cache_dir_path ) ;
if ( dir = = NULL ) {
return - 1 ;
}
struct dirent * ent ;
char path [ PATH_MAX ] ;
uint32_t considered = 0 , removed = 0 ;
const time_t cutoff = now - ( time_t ) mpm_conf - > cache_max_age_seconds ;
while ( ( ent = readdir ( dir ) ) ! = NULL ) {
const char * name = ent - > d_name ;
size_t namelen = strlen ( name ) ;
if ( namelen < 3 | | strcmp ( name + namelen - 3 , " .hs " ) ! = 0 )
continue ;
if ( PathMerge ( path , ARRAY_SIZE ( path ) , mpm_conf - > cache_dir_path , name ) ! = 0 )
continue ;
struct stat st ;
if ( stat ( path , & st ) ! = 0 | | ! S_ISREG ( st . st_mode ) )
continue ;
considered + + ;
const bool prune_by_age = HSPruneFileByAge ( st . st_mtime , cutoff ) ;
const bool prune_by_version = HSPruneFileByVersion ( name ) ;
if ( ! prune_by_age & & ! prune_by_version )
continue ;
void * cache_inuse = HashTableLookup ( inuse_caches , path , ( uint16_t ) strlen ( path ) ) ;
if ( cache_inuse ! = NULL )
continue ; // in use
if ( unlink ( path ) = = 0 ) {
removed + + ;
SCLogDebug ( " File %s removed because of %s%s%s " , path , prune_by_age ? " age " : " " ,
prune_by_age & & prune_by_version ? " and " : " " ,
prune_by_version ? " incompatible version " : " " ) ;
} else {
SCLogWarning ( " Failed to prune \" %s \" : %s " , path , strerror ( errno ) ) ;
}
}
closedir ( dir ) ;
PatternDatabaseCache * pd_cache_stats = mpm_conf - > cache_stats ;
if ( pd_cache_stats ) {
pd_cache_stats - > hs_dbs_cache_pruned_cnt = removed ;
pd_cache_stats - > hs_dbs_cache_pruned_considered_cnt = considered ;
pd_cache_stats - > hs_dbs_cache_pruned_cutoff = cutoff ;
pd_cache_stats - > cache_max_age_seconds = mpm_conf - > cache_max_age_seconds ;
}
return 0 ;
}
void * SCHSCacheStatsInit ( void )
{
PatternDatabaseCache * pd_cache_stats = SCCalloc ( 1 , sizeof ( PatternDatabaseCache ) ) ;
if ( pd_cache_stats = = NULL ) {
SCLogError ( " Failed to allocate memory for Hyperscan cache stats " ) ;
return NULL ;
}
return pd_cache_stats ;
}
void SCHSCacheStatsPrint ( void * data )
{
if ( data = = NULL ) {
return ;
}
PatternDatabaseCache * pd_cache_stats = ( PatternDatabaseCache * ) data ;
char time_str [ 64 ] ;
struct tm tm_s ;
struct tm * tm_info = SCLocalTime ( pd_cache_stats - > hs_dbs_cache_pruned_cutoff , & tm_s ) ;
if ( tm_info ! = NULL ) {
strftime ( time_str , ARRAY_SIZE ( time_str ) , " %Y-%m-%d %H:%M:%S " , tm_info ) ;
} else {
snprintf ( time_str , ARRAY_SIZE ( time_str ) , " % " PRIu64 " seconds " ,
pd_cache_stats - > cache_max_age_seconds ) ;
}
if ( pd_cache_stats - > hs_cacheable_dbs_cnt ) {
SCLogInfo ( " Rule group caching - loaded: %u newly cached: %u total cacheable: %u " ,
pd_cache_stats - > hs_dbs_cache_loaded_cnt , pd_cache_stats - > hs_dbs_cache_saved_cnt ,
pd_cache_stats - > hs_cacheable_dbs_cnt ) ;
}
if ( pd_cache_stats - > hs_dbs_cache_pruned_considered_cnt ) {
SCLogInfo ( " Rule group cache pruning removed %u/%u of HS caches due to "
" version-incompatibility (not v%s) or "
" age (older than %s) " ,
pd_cache_stats - > hs_dbs_cache_pruned_cnt ,
pd_cache_stats - > hs_dbs_cache_pruned_considered_cnt , HS_CACHE_FILE_VERSION ,
time_str ) ;
}
}
void SCHSCacheStatsDeinit ( void * data )
{
if ( data = = NULL ) {
return ;
}
PatternDatabaseCache * pd_cache_stats = ( PatternDatabaseCache * ) data ;
SCFree ( pd_cache_stats ) ;
}
# endif /* BUILD_HYPERSCAN */