35 #ifdef BUILD_HYPERSCAN
40 #define HS_CACHE_FILE_VERSION "2"
41 #define HS_CACHE_FILE_SUFFIX "_v" HS_CACHE_FILE_VERSION ".hs"
44 static char *g_hs_ref_info = NULL;
46 static int16_t HSCacheConstructFPath(
47 const char *dir_path,
const char *db_hash,
char *out_path, uint16_t out_path_size)
49 char filename[NAME_MAX];
50 uint64_t r = snprintf(filename,
sizeof(filename),
"%s" HS_CACHE_FILE_SUFFIX, db_hash);
51 if (r != (uint64_t)(strlen(db_hash) + strlen(HS_CACHE_FILE_SUFFIX)))
54 r =
PathMerge(out_path, out_path_size, dir_path, filename);
61 static char *HSReadStream(
const char *file_path,
size_t *buffer_sz)
63 FILE *file = fopen(file_path,
"rb");
65 SCLogDebug(
"Failed to open file %s: %s", file_path, strerror(errno));
70 fseek(file, 0, SEEK_END);
71 long file_sz = ftell(file);
73 SCLogDebug(
"Failed to determine file size of %s: %s", file_path, strerror(errno));
78 char *buffer = (
char *)
SCCalloc(file_sz,
sizeof(
char));
89 SCLogDebug(
"Failed to rewind file %s: %s", file_path, strerror(errno));
94 size_t bytes_read = fread(buffer, 1, file_sz, file);
95 if (bytes_read != (
size_t)file_sz) {
96 SCLogDebug(
"Failed to read the entire file %s: %s", file_path, strerror(errno));
102 *buffer_sz = file_sz;
111 static void SCHSCachePatternHash(
const SCHSPattern *p,
SCSha256 *sha256)
113 BUG_ON(p->original_pat == NULL);
116 SCSha256Update(sha256, (
const uint8_t *)&p->len,
sizeof(p->len));
117 SCSha256Update(sha256, (
const uint8_t *)&p->flags,
sizeof(p->flags));
118 SCSha256Update(sha256, (
const uint8_t *)p->original_pat, p->len);
119 SCSha256Update(sha256, (
const uint8_t *)&p->id,
sizeof(p->id));
120 SCSha256Update(sha256, (
const uint8_t *)&p->offset,
sizeof(p->offset));
121 SCSha256Update(sha256, (
const uint8_t *)&p->depth,
sizeof(p->depth));
122 SCSha256Update(sha256, (
const uint8_t *)&p->sids_size,
sizeof(p->sids_size));
123 SCSha256Update(sha256, (
const uint8_t *)p->sids, p->sids_size *
sizeof(
SigIntId));
134 static const char *HSGetReferenceDbInfo(
void)
137 if (g_hs_ref_info != NULL) {
139 return g_hs_ref_info;
142 hs_database_t *ref_db = NULL;
143 hs_compile_error_t *compile_err = NULL;
144 hs_error_t err = hs_compile(
"Suricata suricatta is the scientific name for the meerkat",
145 HS_FLAG_SINGLEMATCH, HS_MODE_BLOCK, NULL, &ref_db, &compile_err);
146 if (err == HS_SUCCESS && ref_db != NULL) {
147 if (hs_database_info(ref_db, &g_hs_ref_info) != HS_SUCCESS) {
150 g_hs_ref_info = NULL;
152 hs_free_database(ref_db);
154 if (compile_err != NULL) {
155 SCLogInfo(
"Failed to compile reference Hyperscan database: %s", compile_err->message);
156 hs_free_compile_error(compile_err);
160 return g_hs_ref_info;
163 int HSLoadCache(hs_database_t **hs_db,
const char *hs_db_hash,
const char *dirpath)
165 char hash_file_static[PATH_MAX];
166 int ret = (int)HSCacheConstructFPath(
167 dirpath, hs_db_hash, hash_file_static,
sizeof(hash_file_static));
172 SCLogDebug(
"Loading the cached HS DB from %s", hash_file_static);
176 char *db_info = NULL;
179 buffer = HSReadStream(hash_file_static, &buffer_size);
181 SCLogWarning(
"Hyperscan cached DB file %s cannot be read", hash_file_static);
185 hs_error_t error = hs_deserialize_database(buffer, buffer_size, hs_db);
186 if (error != HS_SUCCESS) {
187 SCLogWarning(
"Failed to deserialize Hyperscan database of %s: %s", hash_file_static,
188 HSErrorToStr(error));
195 const char *ref_info = HSGetReferenceDbInfo();
196 if (ref_info != NULL) {
197 if (hs_database_info(*hs_db, &db_info) != HS_SUCCESS || db_info == NULL) {
198 SCLogDebug(
"Failed to query info for loaded Hyperscan database %s: %s",
199 hash_file_static, HSErrorToStr(error));
203 if (strcmp(db_info, ref_info) != 0) {
204 SCLogDebug(
"Loaded Hyperscan database %s is incompatible with the current "
205 "Hyperscan installation and will be ignored",
215 SCLogDebug(
"Failed to update mtime for %s", hash_file_static);
219 if (ret != 0 && *hs_db != NULL) {
220 hs_free_database(*hs_db);
229 static int HSSaveCache(hs_database_t *hs_db,
const char *hs_db_hash,
const char *dstpath)
231 static bool notified =
false;
232 char *db_stream = NULL;
236 hs_error_t err = hs_serialize_database(hs_db, &db_stream, &db_size);
237 if (err != HS_SUCCESS) {
238 SCLogWarning(
"Failed to serialize Hyperscan database: %s", HSErrorToStr(err));
243 char hash_file_static[PATH_MAX];
244 ret = (int)HSCacheConstructFPath(
245 dstpath, hs_db_hash, hash_file_static,
sizeof(hash_file_static));
248 SCLogDebug(
"Caching the compiled HS at %s", hash_file_static);
254 SCLogWarning(
"Overwriting cache file %s. If the problem persists consider switching off "
259 FILE *db_cache_out = fopen(hash_file_static,
"wb");
262 SCLogWarning(
"Failed to create Hyperscan cache file, make sure the folder exist and is "
263 "writable or adjust sgh-mpm-caching-path setting (%s)",
270 size_t r = fwrite(db_stream,
sizeof(db_stream[0]), db_size, db_cache_out);
272 SCLogWarning(
"Failed to write to file: %s", hash_file_static);
274 if (remove(hash_file_static) != 0) {
275 SCLogWarning(
"Failed to remove corrupted cache file: %s", hash_file_static);
278 ret = fclose(db_cache_out);
280 SCLogWarning(
"Failed to close file: %s", hash_file_static);
290 int HSHashDb(
const PatternDatabase *pd,
char *hash,
size_t hash_len)
293 if (hasher == NULL) {
298 const char *ref_info = HSGetReferenceDbInfo();
299 if (ref_info != NULL) {
300 SCSha256Update(hasher, (
const uint8_t *)ref_info, strlen(ref_info));
303 SCSha256Update(hasher, (
const uint8_t *)&pd->pattern_cnt,
sizeof(pd->pattern_cnt));
304 for (uint32_t i = 0; i < pd->pattern_cnt; i++) {
305 SCHSCachePatternHash(pd->parray[i], hasher);
308 if (!SCSha256FinalizeToHex(hasher, hash, hash_len)) {
318 void HSSaveCacheIterator(
void *data,
void *aux)
320 PatternDatabase *pd = (PatternDatabase *)data;
321 struct HsIteratorData *iter_data = (
struct HsIteratorData *)aux;
326 iter_data->pd_stats->hs_cacheable_dbs_cnt++;
328 iter_data->pd_stats->hs_dbs_cache_loaded_cnt++;
333 if (HSHashDb(pd, hs_db_hash,
ARRAY_SIZE(hs_db_hash)) != 0) {
336 if (HSSaveCache(pd->hs_db, hs_db_hash, iter_data->cache_path) == 0) {
338 iter_data->pd_stats->hs_dbs_cache_saved_cnt++;
342 void HSCacheFilenameUsedIterator(
void *data,
void *aux)
344 PatternDatabase *pd = (PatternDatabase *)data;
345 struct HsInUseCacheFilesIteratorData *iter_data = (
struct HsInUseCacheFilesIteratorData *)aux;
346 if (pd->no_cache || !pd->cached)
350 if (HSHashDb(pd, hs_db_hash,
ARRAY_SIZE(hs_db_hash)) != 0) {
354 char *fpath =
SCCalloc(PATH_MAX,
sizeof(
char));
356 SCLogWarning(
"Failed to allocate memory for cache file path");
359 if (HSCacheConstructFPath(iter_data->cache_path, hs_db_hash, fpath, PATH_MAX)) {
364 int r =
HashTableAdd(iter_data->tbl, (
void *)fpath, (uint16_t)strlen(fpath));
366 SCLogWarning(
"Failed to add used cache file path %s to hash table", fpath);
379 static bool HSPruneFileByAge(time_t mtime, time_t cutoff)
381 return mtime < cutoff;
391 static bool HSPruneFileByVersion(
const char *filename)
393 if (strlen(filename) < strlen(HS_CACHE_FILE_SUFFIX)) {
397 const char *underscore = strrchr(filename,
'_');
398 if (underscore == NULL || strcmp(underscore, HS_CACHE_FILE_SUFFIX) != 0) {
412 const time_t now = time(NULL);
413 if (now == (time_t)-1) {
426 uint32_t considered = 0, removed_by_age = 0, removed_by_version = 0;
428 while ((ent = readdir(dir)) != NULL) {
429 const char *
name = ent->d_name;
430 size_t namelen = strlen(
name);
431 if (namelen < 3 || strcmp(
name + namelen - 3,
".hs") != 0)
446 if (
SCStatFn(path, &st) != 0 || !S_ISREG(st.st_mode))
451 const bool prune_by_age = HSPruneFileByAge(st.st_mtime, cutoff);
452 const bool prune_by_version = HSPruneFileByVersion(
name);
453 if (!prune_by_age && !prune_by_version)
456 void *cache_inuse =
HashTableLookup(inuse_caches, path, (uint16_t)strlen(path));
457 if (cache_inuse != NULL)
461 int ret = unlink(path);
462 if (ret == 0 || (ret == -1 && errno == ENOENT)) {
463 if (prune_by_version)
464 removed_by_version++;
465 else if (prune_by_age)
467 SCLogDebug(
"File %s removed because of %s%s%s", path, prune_by_age ?
"age" :
"",
468 prune_by_age && prune_by_version ?
" and " :
"",
469 prune_by_version ?
"incompatible version" :
"");
471 SCLogWarning(
"Failed to prune \"%s\": %s", path, strerror(errno));
476 PatternDatabaseCache *pd_cache_stats = mpm_conf->
cache_stats;
477 if (pd_cache_stats) {
478 pd_cache_stats->hs_dbs_cache_pruned_by_age_cnt = removed_by_age;
479 pd_cache_stats->hs_dbs_cache_pruned_by_version_cnt = removed_by_version;
480 pd_cache_stats->hs_dbs_cache_pruned_considered_cnt = considered;
481 pd_cache_stats->hs_dbs_cache_pruned_cutoff = cutoff;
487 void *SCHSCacheStatsInit(
void)
489 PatternDatabaseCache *pd_cache_stats =
SCCalloc(1,
sizeof(PatternDatabaseCache));
490 if (pd_cache_stats == NULL) {
491 SCLogError(
"Failed to allocate memory for Hyperscan cache stats");
494 return pd_cache_stats;
497 void SCHSCacheStatsPrint(
void *data)
503 PatternDatabaseCache *pd_cache_stats = (PatternDatabaseCache *)data;
507 struct tm *tm_info =
SCLocalTime(pd_cache_stats->hs_dbs_cache_pruned_cutoff, &tm_s);
508 if (tm_info != NULL) {
509 strftime(time_str,
ARRAY_SIZE(time_str),
"%Y-%m-%d %H:%M:%S", tm_info);
511 snprintf(time_str,
ARRAY_SIZE(time_str),
"%" PRIu64
" seconds",
512 pd_cache_stats->cache_max_age_seconds);
515 if (pd_cache_stats->hs_cacheable_dbs_cnt) {
516 SCLogPerf(
"rule group caching - loaded: %u newly cached: %u total cacheable: %u",
517 pd_cache_stats->hs_dbs_cache_loaded_cnt, pd_cache_stats->hs_dbs_cache_saved_cnt,
518 pd_cache_stats->hs_cacheable_dbs_cnt);
520 if (pd_cache_stats->hs_dbs_cache_pruned_considered_cnt) {
521 if (pd_cache_stats->hs_dbs_cache_pruned_by_version_cnt) {
522 SCLogInfo(
"rule group cache pruning removed %u/%u of HS caches due to "
523 "version-incompatibility (not v%s)",
524 pd_cache_stats->hs_dbs_cache_pruned_by_version_cnt,
525 pd_cache_stats->hs_dbs_cache_pruned_considered_cnt, HS_CACHE_FILE_VERSION);
527 if (pd_cache_stats->hs_dbs_cache_pruned_by_age_cnt) {
528 SCLogInfo(
"rule group cache pruning removed %u/%u of HS caches due to "
529 "age (older than %s)",
530 pd_cache_stats->hs_dbs_cache_pruned_by_age_cnt,
531 pd_cache_stats->hs_dbs_cache_pruned_considered_cnt, time_str);
536 void SCHSCacheStatsDeinit(
void *data)
541 PatternDatabaseCache *pd_cache_stats = (PatternDatabaseCache *)data;
545 void SCHSCacheDeinit(
void)
548 if (g_hs_ref_info != NULL) {
550 g_hs_ref_info = NULL;