35 #ifdef BUILD_HYPERSCAN
40 #define HS_CACHE_FILE_VERSION "2"
41 #define HS_CACHE_FILE_SUFFIX "_v" HS_CACHE_FILE_VERSION ".hs"
43 static int16_t HSCacheConstructFPath(
44 const char *dir_path,
const char *db_hash,
char *out_path, uint16_t out_path_size)
46 char filename[NAME_MAX];
47 uint64_t r = snprintf(filename,
sizeof(filename),
"%s" HS_CACHE_FILE_SUFFIX, db_hash);
48 if (r != (uint64_t)(strlen(db_hash) + strlen(HS_CACHE_FILE_SUFFIX)))
51 r =
PathMerge(out_path, out_path_size, dir_path, filename);
58 static char *HSReadStream(
const char *file_path,
size_t *buffer_sz)
60 FILE *file = fopen(file_path,
"rb");
62 SCLogDebug(
"Failed to open file %s: %s", file_path, strerror(errno));
67 fseek(file, 0, SEEK_END);
68 long file_sz = ftell(file);
70 SCLogDebug(
"Failed to determine file size of %s: %s", file_path, strerror(errno));
75 char *buffer = (
char *)
SCCalloc(file_sz,
sizeof(
char));
86 SCLogDebug(
"Failed to rewind file %s: %s", file_path, strerror(errno));
91 size_t bytes_read = fread(buffer, 1, file_sz, file);
92 if (bytes_read != (
size_t)file_sz) {
93 SCLogDebug(
"Failed to read the entire file %s: %s", file_path, strerror(errno));
108 static void SCHSCachePatternHash(
const SCHSPattern *p,
SCSha256 *sha256)
110 BUG_ON(p->original_pat == NULL);
113 SCSha256Update(sha256, (
const uint8_t *)&p->len,
sizeof(p->len));
114 SCSha256Update(sha256, (
const uint8_t *)&p->flags,
sizeof(p->flags));
115 SCSha256Update(sha256, (
const uint8_t *)p->original_pat, p->len);
116 SCSha256Update(sha256, (
const uint8_t *)&p->id,
sizeof(p->id));
117 SCSha256Update(sha256, (
const uint8_t *)&p->offset,
sizeof(p->offset));
118 SCSha256Update(sha256, (
const uint8_t *)&p->depth,
sizeof(p->depth));
119 SCSha256Update(sha256, (
const uint8_t *)&p->sids_size,
sizeof(p->sids_size));
120 SCSha256Update(sha256, (
const uint8_t *)p->sids, p->sids_size *
sizeof(
SigIntId));
123 int HSLoadCache(hs_database_t **hs_db,
const char *hs_db_hash,
const char *dirpath)
125 char hash_file_static[PATH_MAX];
126 int ret = (int)HSCacheConstructFPath(
127 dirpath, hs_db_hash, hash_file_static,
sizeof(hash_file_static));
132 SCLogDebug(
"Loading the cached HS DB from %s", hash_file_static);
136 FILE *db_cache = fopen(hash_file_static,
"r");
140 buffer = HSReadStream(hash_file_static, &buffer_size);
142 SCLogWarning(
"Hyperscan cached DB file %s cannot be read", hash_file_static);
147 hs_error_t error = hs_deserialize_database(buffer, buffer_size, hs_db);
148 if (error != HS_SUCCESS) {
149 SCLogWarning(
"Failed to deserialize Hyperscan database of %s: %s", hash_file_static,
150 HSErrorToStr(error));
158 SCLogDebug(
"Failed to update mtime for %s", hash_file_static);
171 static int HSSaveCache(hs_database_t *hs_db,
const char *hs_db_hash,
const char *dstpath)
173 static bool notified =
false;
174 char *db_stream = NULL;
178 hs_error_t err = hs_serialize_database(hs_db, &db_stream, &db_size);
179 if (err != HS_SUCCESS) {
180 SCLogWarning(
"Failed to serialize Hyperscan database: %s", HSErrorToStr(err));
185 char hash_file_static[PATH_MAX];
186 ret = (int)HSCacheConstructFPath(
187 dstpath, hs_db_hash, hash_file_static,
sizeof(hash_file_static));
190 SCLogDebug(
"Caching the compiled HS at %s", hash_file_static);
196 SCLogWarning(
"Overwriting cache file %s. If the problem persists consider switching off "
201 FILE *db_cache_out = fopen(hash_file_static,
"w");
204 SCLogWarning(
"Failed to create Hyperscan cache file, make sure the folder exist and is "
205 "writable or adjust sgh-mpm-caching-path setting (%s)",
212 size_t r = fwrite(db_stream,
sizeof(db_stream[0]), db_size, db_cache_out);
213 if (r > 0 && (
size_t)r != db_size) {
214 SCLogWarning(
"Failed to write to file: %s", hash_file_static);
217 r = remove(hash_file_static);
219 SCLogWarning(
"Failed to remove corrupted cache file: %s", hash_file_static);
223 ret = fclose(db_cache_out);
225 SCLogWarning(
"Failed to close file: %s", hash_file_static);
235 int HSHashDb(
const PatternDatabase *pd,
char *hash,
size_t hash_len)
238 if (hasher == NULL) {
242 SCSha256Update(hasher, (
const uint8_t *)&pd->pattern_cnt,
sizeof(pd->pattern_cnt));
243 for (uint32_t i = 0; i < pd->pattern_cnt; i++) {
244 SCHSCachePatternHash(pd->parray[i], hasher);
247 if (!SCSha256FinalizeToHex(hasher, hash, hash_len)) {
257 void HSSaveCacheIterator(
void *data,
void *aux)
259 PatternDatabase *pd = (PatternDatabase *)data;
260 struct HsIteratorData *iter_data = (
struct HsIteratorData *)aux;
265 iter_data->pd_stats->hs_cacheable_dbs_cnt++;
267 iter_data->pd_stats->hs_dbs_cache_loaded_cnt++;
272 if (HSHashDb(pd, hs_db_hash,
ARRAY_SIZE(hs_db_hash)) != 0) {
275 if (HSSaveCache(pd->hs_db, hs_db_hash, iter_data->cache_path) == 0) {
277 iter_data->pd_stats->hs_dbs_cache_saved_cnt++;
281 void HSCacheFilenameUsedIterator(
void *data,
void *aux)
283 PatternDatabase *pd = (PatternDatabase *)data;
284 struct HsInUseCacheFilesIteratorData *iter_data = (
struct HsInUseCacheFilesIteratorData *)aux;
285 if (pd->no_cache || !pd->cached)
289 if (HSHashDb(pd, hs_db_hash,
ARRAY_SIZE(hs_db_hash)) != 0) {
293 char *fpath =
SCCalloc(PATH_MAX,
sizeof(
char));
295 SCLogWarning(
"Failed to allocate memory for cache file path");
298 if (HSCacheConstructFPath(iter_data->cache_path, hs_db_hash, fpath, PATH_MAX)) {
303 int r =
HashTableAdd(iter_data->tbl, (
void *)fpath, (uint16_t)strlen(fpath));
305 SCLogWarning(
"Failed to add used cache file path %s to hash table", fpath);
318 static bool HSPruneFileByAge(time_t mtime, time_t cutoff)
320 return mtime < cutoff;
330 static bool HSPruneFileByVersion(
const char *filename)
332 if (strlen(filename) < strlen(HS_CACHE_FILE_SUFFIX)) {
336 const char *underscore = strrchr(filename,
'_');
337 if (underscore == NULL || strcmp(underscore, HS_CACHE_FILE_SUFFIX) != 0) {
351 const time_t now = time(NULL);
352 if (now == (time_t)-1) {
365 uint32_t considered = 0, removed = 0;
367 while ((ent = readdir(dir)) != NULL) {
368 const char *
name = ent->d_name;
369 size_t namelen = strlen(
name);
370 if (namelen < 3 || strcmp(
name + namelen - 3,
".hs") != 0)
385 if (
SCStatFn(path, &st) != 0 || !S_ISREG(st.st_mode))
390 const bool prune_by_age = HSPruneFileByAge(st.st_mtime, cutoff);
391 const bool prune_by_version = HSPruneFileByVersion(
name);
392 if (!prune_by_age && !prune_by_version)
395 void *cache_inuse =
HashTableLookup(inuse_caches, path, (uint16_t)strlen(path));
396 if (cache_inuse != NULL)
400 int ret = unlink(path);
401 if (ret == 0 || (ret == -1 && errno == ENOENT)) {
403 SCLogDebug(
"File %s removed because of %s%s%s", path, prune_by_age ?
"age" :
"",
404 prune_by_age && prune_by_version ?
" and " :
"",
405 prune_by_version ?
"incompatible version" :
"");
407 SCLogWarning(
"Failed to prune \"%s\": %s", path, strerror(errno));
412 PatternDatabaseCache *pd_cache_stats = mpm_conf->
cache_stats;
413 if (pd_cache_stats) {
414 pd_cache_stats->hs_dbs_cache_pruned_cnt = removed;
415 pd_cache_stats->hs_dbs_cache_pruned_considered_cnt = considered;
416 pd_cache_stats->hs_dbs_cache_pruned_cutoff = cutoff;
422 void *SCHSCacheStatsInit(
void)
424 PatternDatabaseCache *pd_cache_stats =
SCCalloc(1,
sizeof(PatternDatabaseCache));
425 if (pd_cache_stats == NULL) {
426 SCLogError(
"Failed to allocate memory for Hyperscan cache stats");
429 return pd_cache_stats;
432 void SCHSCacheStatsPrint(
void *data)
438 PatternDatabaseCache *pd_cache_stats = (PatternDatabaseCache *)data;
442 struct tm *tm_info =
SCLocalTime(pd_cache_stats->hs_dbs_cache_pruned_cutoff, &tm_s);
443 if (tm_info != NULL) {
444 strftime(time_str,
ARRAY_SIZE(time_str),
"%Y-%m-%d %H:%M:%S", tm_info);
446 snprintf(time_str,
ARRAY_SIZE(time_str),
"%" PRIu64
" seconds",
447 pd_cache_stats->cache_max_age_seconds);
450 if (pd_cache_stats->hs_cacheable_dbs_cnt) {
451 SCLogInfo(
"Rule group caching - loaded: %u newly cached: %u total cacheable: %u",
452 pd_cache_stats->hs_dbs_cache_loaded_cnt, pd_cache_stats->hs_dbs_cache_saved_cnt,
453 pd_cache_stats->hs_cacheable_dbs_cnt);
455 if (pd_cache_stats->hs_dbs_cache_pruned_considered_cnt) {
456 SCLogInfo(
"Rule group cache pruning removed %u/%u of HS caches due to "
457 "version-incompatibility (not v%s) or "
458 "age (older than %s)",
459 pd_cache_stats->hs_dbs_cache_pruned_cnt,
460 pd_cache_stats->hs_dbs_cache_pruned_considered_cnt, HS_CACHE_FILE_VERSION,
465 void SCHSCacheStatsDeinit(
void *data)
470 PatternDatabaseCache *pd_cache_stats = (PatternDatabaseCache *)data;