Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hyperscan: add caching mechanism for hyperscan contexts v5 #11811

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ install-conf:
install -d "$(DESTDIR)$(e_rundir)"
install -m 770 -d "$(DESTDIR)$(e_localstatedir)"
install -m 770 -d "$(DESTDIR)$(e_datadir)"
install -m 660 -d "$(DESTDIR)$(e_hscachedir)"

install-rules:
if INSTALL_SURICATA_UPDATE
Expand Down
4 changes: 4 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -2436,6 +2436,8 @@ if test "$WINDOWS_PATH" = "yes"; then

e_sysconfdir="${e_winbase}\\\\"
e_defaultruledir="$e_winbase\\\\rules\\\\"
e_hscachedir="$e_winbase\\\\cache\\\\hs\\\\"

e_magic_file="$e_winbase\\\\magic.mgc"
e_logdir="$e_winbase\\\\log"
e_logfilesdir="$e_logdir\\\\files"
Expand All @@ -2457,6 +2459,7 @@ else
EXPAND_VARIABLE(sysconfdir, e_sysconfdir, "/suricata/")
EXPAND_VARIABLE(localstatedir, e_localstatedir, "/run/suricata")
EXPAND_VARIABLE(datadir, e_datarulesdir, "/suricata/rules")
EXPAND_VARIABLE(localstatedir, e_hscachedir, "/lib/suricata/cache/hs")
EXPAND_VARIABLE(localstatedir, e_datadir, "/lib/suricata/data")
EXPAND_VARIABLE(localstatedir, e_defaultruledir, "/lib/suricata/rules")

Expand All @@ -2470,6 +2473,7 @@ AC_SUBST(e_logcertsdir)
AC_SUBST(e_sysconfdir)
AC_DEFINE_UNQUOTED([CONFIG_DIR],["$e_sysconfdir"],[Our CONFIG_DIR])
AC_SUBST(e_localstatedir)
AC_SUBST(e_hscachedir)
AC_SUBST(e_datadir)
AC_DEFINE_UNQUOTED([DATA_DIR],["$e_datadir"],[Our DATA_DIR])
AC_SUBST(e_magic_file)
Expand Down
26 changes: 25 additions & 1 deletion doc/userguide/performance/hyperscan.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,28 @@ if it is present on the system in case of the "auto" setting.


If the current suricata installation does not have hyperscan
support, refer to :ref:`installation`
support, refer to :ref:`installation`

Hyperscan caching
~~~~~~~~~~~~~~~~~

Upon startup, Hyperscan compiles and optimizes the ruleset into its own
internal structure. Suricata optimizes the startup process by saving
the Hyperscan internal structures to disk and loading them on the next start.
This prevents the recompilation of the ruleset and results in faster
initialization. If the ruleset is changed, new necessary cache files are
automatically created.

To enable this function, in `suricata.yaml` configure:

::

# Cache MPM contexts to the disk to avoid rule compilation at the startup.
# Cache files are created in the default logging directory.
sgh-mpm-caching: yes
sgh-mpm-caching-path: /var/lib/suricata/cache/hs


**Note**:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
You might need create and adjust permissions to the default caching folder path,
You might need to create and adjust permissions to the default caching folder path,

You might need create and adjust permissions to the default caching folder path,
especially if you are running Suricata as a non-root user.
23 changes: 16 additions & 7 deletions src/detect-engine.c
Original file line number Diff line number Diff line change
Expand Up @@ -2989,14 +2989,23 @@ static int DetectEngineCtxLoadConf(DetectEngineCtx *de_ctx)
return 0;
}

/*
* getting & (re)setting the internal sig i
*/
bool DetectEngineMpmCachingEnabled(void)
{
const char *strval = NULL;
if (ConfGet("detect.sgh-mpm-caching", &strval) != 1)
return false;

//inline uint32_t DetectEngineGetMaxSigId(DetectEngineCtx *de_ctx)
//{
// return de_ctx->signum;
//}
int sgh_mpm_caching = 0;
(void)ConfGetBool("detect.sgh-mpm-caching", &sgh_mpm_caching);
return (bool)sgh_mpm_caching;
}

const char *DetectEngineMpmCachingGetPath(void)
{
const char *strval = NULL;
ConfGet("detect.sgh-mpm-caching-path", &strval);
return strval;
}

void DetectEngineResetMaxSigId(DetectEngineCtx *de_ctx)
{
Expand Down
3 changes: 2 additions & 1 deletion src/detect-engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ void *DetectThreadCtxGetGlobalKeywordThreadCtx(DetectEngineThreadCtx *det_ctx, i

TmEcode DetectEngineThreadCtxInit(ThreadVars *, void *, void **);
TmEcode DetectEngineThreadCtxDeinit(ThreadVars *, void *);
//inline uint32_t DetectEngineGetMaxSigId(DetectEngineCtx *);
bool DetectEngineMpmCachingEnabled(void);
const char *DetectEngineMpmCachingGetPath(void);
/* faster as a macro than a inline function on my box -- VJ */
#define DetectEngineGetMaxSigId(de_ctx) ((de_ctx)->signum)
void DetectEngineResetMaxSigId(DetectEngineCtx *);
Expand Down
204 changes: 204 additions & 0 deletions src/util-hash-lookup3.c
Original file line number Diff line number Diff line change
Expand Up @@ -805,7 +805,211 @@ void hashlittle2(
*pc=c; *pb=b;
}

/*
* hashlittle2: return 2 32-bit hash values
*
* This is identical to hashlittle(), except it returns two 32-bit hash
* values instead of just one. This is good enough for hash table
* lookup with 2^^64 buckets, or if you want a second hash if you're not
* happy with the first, or if you want a probably-unique 64-bit ID for
* the key. *pc is better mixed than *pb, so use *pc first. If you want
* a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)".
*/
void hashlittle2_safe(const void *key, /* the key to hash */
size_t length, /* length of the key */
uint32_t *pc, /* IN: primary initval, OUT: primary hash */
uint32_t *pb) /* IN: secondary initval, OUT: secondary hash */
{
uint32_t a, b, c; /* internal state */
union {
const void *ptr;
size_t i;
} u; /* needed for Mac Powerbook G4 */

/* Set up the internal state */
a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc;
c += *pb;

u.ptr = key;
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */

/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
while (length > 12) {
a += k[0];
b += k[1];
c += k[2];
mix(a, b, c);
length -= 12;
k += 3;
}

/*----------------------------- handle the last (probably partial) block */
/*
* Note that unlike hashlittle() above, we use the "safe" version of this
* block that is #ifdef VALGRIND above, in order to avoid warnings from
* Valgrind or Address Sanitizer.
*/
const uint8_t *k8 = (const uint8_t *)k;
switch (length) {
case 12:
c += k[2];
b += k[1];
a += k[0];
break;
case 11:
c += ((uint32_t)k8[10]) << 16; /* fall through */
case 10:
c += ((uint32_t)k8[9]) << 8; /* fall through */
case 9:
c += k8[8]; /* fall through */
case 8:
b += k[1];
a += k[0];
break;
case 7:
b += ((uint32_t)k8[6]) << 16; /* fall through */
case 6:
b += ((uint32_t)k8[5]) << 8; /* fall through */
case 5:
b += k8[4]; /* fall through */
case 4:
a += k[0];
break;
case 3:
a += ((uint32_t)k8[2]) << 16; /* fall through */
case 2:
a += ((uint32_t)k8[1]) << 8; /* fall through */
case 1:
a += k8[0];
break;
case 0:
*pc = c;
*pb = b;
return; /* zero length strings require no mixing */
}

} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
const uint8_t *k8;

/*--------------- all but last block: aligned reads and different mixing */
while (length > 12) {
a += k[0] + (((uint32_t)k[1]) << 16);
b += k[2] + (((uint32_t)k[3]) << 16);
c += k[4] + (((uint32_t)k[5]) << 16);
mix(a, b, c);
length -= 12;
k += 6;
}

/*----------------------------- handle the last (probably partial) block */
k8 = (const uint8_t *)k;
switch (length) {
case 12:
c += k[4] + (((uint32_t)k[5]) << 16);
b += k[2] + (((uint32_t)k[3]) << 16);
a += k[0] + (((uint32_t)k[1]) << 16);
break;
case 11:
c += ((uint32_t)k8[10]) << 16; /* fall through */
case 10:
c += k[4];
b += k[2] + (((uint32_t)k[3]) << 16);
a += k[0] + (((uint32_t)k[1]) << 16);
break;
case 9:
c += k8[8]; /* fall through */
case 8:
b += k[2] + (((uint32_t)k[3]) << 16);
a += k[0] + (((uint32_t)k[1]) << 16);
break;
case 7:
b += ((uint32_t)k8[6]) << 16; /* fall through */
case 6:
b += k[2];
a += k[0] + (((uint32_t)k[1]) << 16);
break;
case 5:
b += k8[4]; /* fall through */
case 4:
a += k[0] + (((uint32_t)k[1]) << 16);
break;
case 3:
a += ((uint32_t)k8[2]) << 16; /* fall through */
case 2:
a += k[0];
break;
case 1:
a += k8[0];
break;
case 0:
*pc = c;
*pb = b;
return; /* zero length strings require no mixing */
}

} else { /* need to read the key one byte at a time */
const uint8_t *k = (const uint8_t *)key;

/*--------------- all but the last block: affect some 32 bits of (a,b,c) */
while (length > 12) {
a += k[0];
a += ((uint32_t)k[1]) << 8;
a += ((uint32_t)k[2]) << 16;
a += ((uint32_t)k[3]) << 24;
b += k[4];
b += ((uint32_t)k[5]) << 8;
b += ((uint32_t)k[6]) << 16;
b += ((uint32_t)k[7]) << 24;
c += k[8];
c += ((uint32_t)k[9]) << 8;
c += ((uint32_t)k[10]) << 16;
c += ((uint32_t)k[11]) << 24;
mix(a, b, c);
length -= 12;
k += 12;
}

/*-------------------------------- last block: affect all 32 bits of (c) */
switch (length) /* all the case statements fall through */
{
case 12:
c += ((uint32_t)k[11]) << 24; /* fall through */
case 11:
c += ((uint32_t)k[10]) << 16; /* fall through */
case 10:
c += ((uint32_t)k[9]) << 8; /* fall through */
case 9:
c += k[8]; /* fall through */
case 8:
b += ((uint32_t)k[7]) << 24; /* fall through */
case 7:
b += ((uint32_t)k[6]) << 16; /* fall through */
case 6:
b += ((uint32_t)k[5]) << 8; /* fall through */
case 5:
b += k[4]; /* fall through */
case 4:
a += ((uint32_t)k[3]) << 24; /* fall through */
case 3:
a += ((uint32_t)k[2]) << 16; /* fall through */
case 2:
a += ((uint32_t)k[1]) << 8; /* fall through */
case 1:
a += k[0];
break;
case 0:
*pc = c;
*pb = b;
return; /* zero length strings require no mixing */
}
}

final(a, b, c);
*pc = c;
*pb = b;
}

/*
* hashbig():
Expand Down
5 changes: 5 additions & 0 deletions src/util-hash-lookup3.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ void hashlittle2(const void *key, /* the key to hash */
uint32_t *pc, /* IN: primary initval, OUT: primary hash */
uint32_t *pb); /* IN: secondary initval, OUT: secondary hash */

/* A variant of hashlittle2() that ensures avoids accesses beyond the last byte
* of the string, which will cause warnings from tools like Valgrind or Address
* Sanitizer. */
void hashlittle2_safe(const void *key, size_t length, uint32_t *pc, uint32_t *pb);

uint32_t hashbig( const void *key, size_t length, uint32_t initval);

#endif /* SURICATA_UTIL_HASH_LOOKUP3_H */
Loading
Loading