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)
136 if (g_hs_ref_info != NULL) {
137 return g_hs_ref_info;
140 hs_database_t *ref_db = NULL;
141 hs_compile_error_t *compile_err = NULL;
142 hs_error_t err = hs_compile(
"Suricata suricatta is the scientific name for the meerkat",
143 HS_FLAG_SINGLEMATCH, HS_MODE_BLOCK, NULL, &ref_db, &compile_err);
144 if (err == HS_SUCCESS && ref_db != NULL) {
145 if (hs_database_info(ref_db, &g_hs_ref_info) != HS_SUCCESS) {
148 g_hs_ref_info = NULL;
150 hs_free_database(ref_db);
152 if (compile_err != NULL) {
153 SCLogInfo(
"Failed to compile reference Hyperscan database: %s", compile_err->message);
154 hs_free_compile_error(compile_err);
157 return g_hs_ref_info;
160 int HSLoadCache(hs_database_t **hs_db,
const char *hs_db_hash,
const char *dirpath)
162 char hash_file_static[PATH_MAX];
163 int ret = (int)HSCacheConstructFPath(
164 dirpath, hs_db_hash, hash_file_static,
sizeof(hash_file_static));
169 SCLogDebug(
"Loading the cached HS DB from %s", hash_file_static);
173 char *db_info = NULL;
176 buffer = HSReadStream(hash_file_static, &buffer_size);
178 SCLogWarning(
"Hyperscan cached DB file %s cannot be read", hash_file_static);
182 hs_error_t error = hs_deserialize_database(buffer, buffer_size, hs_db);
183 if (error != HS_SUCCESS) {
184 SCLogWarning(
"Failed to deserialize Hyperscan database of %s: %s", hash_file_static,
185 HSErrorToStr(error));
193 const char *ref_info = HSGetReferenceDbInfo();
194 if (ref_info != NULL) {
195 if (hs_database_info(*hs_db, &db_info) != HS_SUCCESS || db_info == NULL) {
196 SCLogDebug(
"Failed to query info for loaded Hyperscan database %s: %s",
197 hash_file_static, HSErrorToStr(error));
202 if (strcmp(db_info, ref_info) != 0) {
203 SCLogDebug(
"Loaded Hyperscan database %s is incompatible with the current "
204 "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) {
299 const char *ref_info = HSGetReferenceDbInfo();
300 if (ref_info != NULL) {
301 SCSha256Update(hasher, (
const uint8_t *)ref_info, strlen(ref_info));
305 SCSha256Update(hasher, (
const uint8_t *)&pd->pattern_cnt,
sizeof(pd->pattern_cnt));
306 for (uint32_t i = 0; i < pd->pattern_cnt; i++) {
307 SCHSCachePatternHash(pd->parray[i], hasher);
310 if (!SCSha256FinalizeToHex(hasher, hash, hash_len)) {
320 void HSSaveCacheIterator(
void *data,
void *aux)
322 PatternDatabase *pd = (PatternDatabase *)data;
323 struct HsIteratorData *iter_data = (
struct HsIteratorData *)aux;
328 iter_data->pd_stats->hs_cacheable_dbs_cnt++;
330 iter_data->pd_stats->hs_dbs_cache_loaded_cnt++;
335 if (HSHashDb(pd, hs_db_hash,
ARRAY_SIZE(hs_db_hash)) != 0) {
338 if (HSSaveCache(pd->hs_db, hs_db_hash, iter_data->cache_path) == 0) {
340 iter_data->pd_stats->hs_dbs_cache_saved_cnt++;
344 void HSCacheFilenameUsedIterator(
void *data,
void *aux)
346 PatternDatabase *pd = (PatternDatabase *)data;
347 struct HsInUseCacheFilesIteratorData *iter_data = (
struct HsInUseCacheFilesIteratorData *)aux;
348 if (pd->no_cache || !pd->cached)
352 if (HSHashDb(pd, hs_db_hash,
ARRAY_SIZE(hs_db_hash)) != 0) {
356 char *fpath =
SCCalloc(PATH_MAX,
sizeof(
char));
358 SCLogWarning(
"Failed to allocate memory for cache file path");
361 if (HSCacheConstructFPath(iter_data->cache_path, hs_db_hash, fpath, PATH_MAX)) {
366 int r =
HashTableAdd(iter_data->tbl, (
void *)fpath, (uint16_t)strlen(fpath));
368 SCLogWarning(
"Failed to add used cache file path %s to hash table", fpath);
381 static bool HSPruneFileByAge(time_t mtime, time_t cutoff)
383 return mtime < cutoff;
393 static bool HSPruneFileByVersion(
const char *filename)
395 if (strlen(filename) < strlen(HS_CACHE_FILE_SUFFIX)) {
399 const char *underscore = strrchr(filename,
'_');
400 if (underscore == NULL || strcmp(underscore, HS_CACHE_FILE_SUFFIX) != 0) {
414 const time_t now = time(NULL);
415 if (now == (time_t)-1) {
428 uint32_t considered = 0, removed_by_age = 0, removed_by_version = 0;
430 while ((ent = readdir(dir)) != NULL) {
431 const char *
name = ent->d_name;
432 size_t namelen = strlen(
name);
433 if (namelen < 3 || strcmp(
name + namelen - 3,
".hs") != 0)
448 if (
SCStatFn(path, &st) != 0 || !S_ISREG(st.st_mode))
453 const bool prune_by_age = HSPruneFileByAge(st.st_mtime, cutoff);
454 const bool prune_by_version = HSPruneFileByVersion(
name);
455 if (!prune_by_age && !prune_by_version)
458 void *cache_inuse =
HashTableLookup(inuse_caches, path, (uint16_t)strlen(path));
459 if (cache_inuse != NULL)
463 int ret = unlink(path);
464 if (ret == 0 || (ret == -1 && errno == ENOENT)) {
465 if (prune_by_version)
466 removed_by_version++;
467 else if (prune_by_age)
469 SCLogDebug(
"File %s removed because of %s%s%s", path, prune_by_age ?
"age" :
"",
470 prune_by_age && prune_by_version ?
" and " :
"",
471 prune_by_version ?
"incompatible version" :
"");
473 SCLogWarning(
"Failed to prune \"%s\": %s", path, strerror(errno));
478 PatternDatabaseCache *pd_cache_stats = mpm_conf->
cache_stats;
479 if (pd_cache_stats) {
480 pd_cache_stats->hs_dbs_cache_pruned_by_age_cnt = removed_by_age;
481 pd_cache_stats->hs_dbs_cache_pruned_by_version_cnt = removed_by_version;
482 pd_cache_stats->hs_dbs_cache_pruned_considered_cnt = considered;
483 pd_cache_stats->hs_dbs_cache_pruned_cutoff = cutoff;
489 void *SCHSCacheStatsInit(
void)
491 PatternDatabaseCache *pd_cache_stats =
SCCalloc(1,
sizeof(PatternDatabaseCache));
492 if (pd_cache_stats == NULL) {
493 SCLogError(
"Failed to allocate memory for Hyperscan cache stats");
496 return pd_cache_stats;
499 void SCHSCacheStatsPrint(
void *data)
505 PatternDatabaseCache *pd_cache_stats = (PatternDatabaseCache *)data;
509 struct tm *tm_info =
SCLocalTime(pd_cache_stats->hs_dbs_cache_pruned_cutoff, &tm_s);
510 if (tm_info != NULL) {
511 strftime(time_str,
ARRAY_SIZE(time_str),
"%Y-%m-%d %H:%M:%S", tm_info);
513 snprintf(time_str,
ARRAY_SIZE(time_str),
"%" PRIu64
" seconds",
514 pd_cache_stats->cache_max_age_seconds);
517 if (pd_cache_stats->hs_cacheable_dbs_cnt) {
518 SCLogPerf(
"rule group caching - loaded: %u newly cached: %u total cacheable: %u",
519 pd_cache_stats->hs_dbs_cache_loaded_cnt, pd_cache_stats->hs_dbs_cache_saved_cnt,
520 pd_cache_stats->hs_cacheable_dbs_cnt);
522 if (pd_cache_stats->hs_dbs_cache_pruned_considered_cnt) {
523 if (pd_cache_stats->hs_dbs_cache_pruned_by_version_cnt) {
524 SCLogInfo(
"rule group cache pruning removed %u/%u of HS caches due to "
525 "version-incompatibility (not v%s)",
526 pd_cache_stats->hs_dbs_cache_pruned_by_version_cnt,
527 pd_cache_stats->hs_dbs_cache_pruned_considered_cnt, HS_CACHE_FILE_VERSION);
529 if (pd_cache_stats->hs_dbs_cache_pruned_by_age_cnt) {
530 SCLogInfo(
"rule group cache pruning removed %u/%u of HS caches due to "
531 "age (older than %s)",
532 pd_cache_stats->hs_dbs_cache_pruned_by_age_cnt,
533 pd_cache_stats->hs_dbs_cache_pruned_considered_cnt, time_str);
538 void SCHSCacheStatsDeinit(
void *data)
543 PatternDatabaseCache *pd_cache_stats = (PatternDatabaseCache *)data;
547 void SCHSCacheDeinit(
void)
550 if (g_hs_ref_info != NULL) {
552 g_hs_ref_info = NULL;