The directional arrow indicates which way the signature will be evaluated.
In most signatures an arrow to the right (``->``) is used. This means that only
-packets with the same direction can match. However, it is also possible to
-have a rule match both directions (``<>``)::
+packets with the same direction can match.
+There is also the double arrow (``=>``), which respects the directionality as ``->``,
+but allows matching on bidirectional transactions, used with keywords matching each direction.
+Finally, it is also possible to have a rule match either directions (``<>``)::
source -> destination
- source <> destination (both directions)
+ source => destination
+ source <> destination (either directions)
The following example illustrates direction. In this example there is a client
with IP address 1.2.3.4 using port 1024. A server with IP address 5.6.7.8,
Only the traffic from the client to the server will be matched by this rule,
as the direction specifies that we do not want to evaluate the response packet.
+Now, if we have a rule with the following header::
+
+ alert tcp 1.2.3.4 any <> 5.6.7.8 80
+
+Suricata will duplicate it and use the same rule with headers in both directions :
+
+ alert tcp 1.2.3.4 any -> 5.6.7.8 80
+ alert tcp 5.6.7.8 80 -> 1.2.3.4 any
+
.. warning::
There is no 'reverse' style direction, i.e. there is no ``<-``.
+Transactional rules
+~~~~~~~~~~~~~~~~~~~
+
+Here is an example of a transactional rule:
+
+.. container:: example-rule
+
+ alert http any any :example-rule-emphasis:`=>` 5.6.7.8 80 (msg:"matching both uri and status"; sid: 1; http.uri; content: "/download"; http.stat_code; content: "200";)
+
+It will match on flows to 5.6.7.8 and port 80.
+And it will match on a full transaction, using both the uri from the request,
+and the stat_code from the response.
+As such, it will match only when Suricata got both request and response.
+
+Transactional rules can use direction-ambiguous keywords, by specifying the direction.
+
+.. container:: example-rule
+
+ alert http any any => 5.6.7.8 80 (msg:"matching json to server and xml to client"; sid: 1; http.content_type: :example-rule-emphasis:`to_server`; content: "json"; http.content_type: :example-rule-emphasis:`to_client`; content: "xml";)
+
+Transactional rules have some limitations :
+
+* They cannot use direction-ambiguous keywords
+* They are only meant to work on transactions with first a request to the server,
+ and then a response to the client, and not the other way around (not tested).
+* They cannot have ``fast_pattern`` or ``prefilter`` the direction to client
+ if they also have a streaming buffer on the direction to server, see example below.
+* They will refuse to load if a single directional rule is enough.
+
+This rule cannot have the ``fast_pattern`` to client, as ``file.data`` is a streaming buffer and will refuse to load.
+
+.. container:: example-rule
+
+ alert http any any => any any (file.data: to_server; content: "123"; http.stat_code; content: "500"; fast_patten;)
+
+If not explicit, a transactional rule will choose a fast_pattern to server by default
+
Rule options
------------
The rest of the rule consists of options. These are enclosed by parenthesis
int g_skip_prefilter = 0;
+// tells if a buffer id is only used to client
+bool DetectBufferToClient(const DetectEngineCtx *de_ctx, int buf_id, AppProto alproto)
+{
+ bool r = false;
+ const DetectEngineAppInspectionEngine *app = de_ctx->app_inspect_engines;
+ for (; app != NULL; app = app->next) {
+ if (app->sm_list == buf_id &&
+ (AppProtoEquals(alproto, app->alproto) || alproto == ALPROTO_UNKNOWN)) {
+ if (app->dir == 1) {
+ // do not return yet in case we have app engines on both sides
+ r = true;
+ } else {
+ // ambiguous keywords have a app-engine to server
+ return false;
+ }
+ }
+ }
+ return r;
+}
+
void RetrieveFPForSig(const DetectEngineCtx *de_ctx, Signature *s)
{
if (g_skip_prefilter)
memset(&final_sm_list, 0, (nlists * sizeof(int)));
int count_final_sm_list = 0;
+ int count_txbidir_toclient_sm_list = 0;
int priority;
const SCFPSupportSMList *tmp = de_ctx->fp_support_smlist_list;
continue;
if (curr_sm_list[tmp->list_id] == 0)
continue;
+ if (s->flags & SIG_FLAG_TXBOTHDIR) {
+ // prefer to choose a fast_pattern to server by default
+ if (DetectBufferToClient(de_ctx, tmp->list_id, s->alproto)) {
+ if (count_final_sm_list == 0) {
+ // still put it in in the case we do not have toserver buffer
+ final_sm_list[count_txbidir_toclient_sm_list++] = tmp->list_id;
+ }
+ continue;
+ }
+ }
+ // we may erase tx bidir toclient buffers here as intended if we have a better choice
final_sm_list[count_final_sm_list++] = tmp->list_id;
SCLogDebug("tmp->list_id %d", tmp->list_id);
}
break;
}
+ if ((s->flags & SIG_FLAG_TXBOTHDIR) && count_final_sm_list == 0) {
+ // forced to pick a fast_pattern to client for tx bidir signature
+ count_final_sm_list = count_txbidir_toclient_sm_list;
+ }
BUG_ON(count_final_sm_list == 0);
SCLogDebug("count_final_sm_list %d skip_negated_content %d", count_final_sm_list,
skip_negated_content);
}
} else {
for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+ if (s->init_data->buffers[x].only_tc) {
+ // prefer to choose a fast_pattern to server by default
+ continue;
+ }
const int list_id = s->init_data->buffers[x].id;
+
if (final_sm_list[i] == list_id) {
SCLogDebug("%u: list_id %d: %s", s->id, list_id,
DetectEngineBufferTypeGetNameById(de_ctx, list_id));
void EngineAnalysisAddAllRulePatterns(DetectEngineCtx *de_ctx, const Signature *s);
+bool DetectBufferToClient(const DetectEngineCtx *de_ctx, int buf_id, AppProto alproto);
+
#endif /* SURICATA_DETECT_ENGINE_MPM_H */
SCLogDebug("destate created for %"PRIu64, tx_id);
}
DeStateSignatureAppend(tx_data->de_state, s, inspect_flags, flow_flags);
+ if (s->flags & SIG_FLAG_TXBOTHDIR) {
+ // add also in the other DetectEngineStateDirection
+ DeStateSignatureAppend(tx_data->de_state, s, inspect_flags,
+ flow_flags ^ (STREAM_TOSERVER | STREAM_TOCLIENT));
+ }
StoreStateTxHandleFiles(sgh, f, tx_data->de_state, flow_flags, tx, tx_id, file_no_match);
SCLogDebug("Stored for TX %"PRIu64, tx_id);
for (const DetectEngineAppInspectionEngine *t = de_ctx->app_inspect_engines; t != NULL;
t = t->next) {
if (t->sm_list == s->init_data->buffers[x].id) {
+ if (s->flags & SIG_FLAG_TXBOTHDIR) {
+ // ambiguous keywords have app engines in both directions
+ // so we skip the wrong direction for this buffer
+ if (s->init_data->buffers[x].only_tc && t->dir == 0) {
+ continue;
+ } else if (s->init_data->buffers[x].only_ts && t->dir == 1) {
+ continue;
+ }
+ }
AppendAppInspectEngine(
de_ctx, t, s, smd, mpm_list, files_id, &last_id, &head_is_mpm);
}
} else if (DetectEngineBufferTypeSupportsMultiInstanceGetById(de_ctx, list)) {
// fall through
+ } else if (!b->only_ts && (s->init_data->init_flags & SIG_FLAG_INIT_FORCE_TOSERVER)) {
+ // fall through
+ } else if (!b->only_tc && (s->init_data->init_flags & SIG_FLAG_INIT_FORCE_TOCLIENT)) {
+ // fall through
} else {
+ // we create a new buffer for the same id but forced different direction
SCLogWarning("duplicate instance for %s in '%s'",
DetectEngineBufferTypeGetNameById(de_ctx, list), s->sig_str);
s->init_data->curbuf = b;
s->init_data->curbuf->tail = NULL;
s->init_data->curbuf->multi_capable =
DetectEngineBufferTypeSupportsMultiInstanceGetById(de_ctx, list);
+ if (s->init_data->init_flags & SIG_FLAG_INIT_FORCE_TOCLIENT) {
+ s->init_data->curbuf->only_tc = true;
+ }
+ if (s->init_data->init_flags & SIG_FLAG_INIT_FORCE_TOSERVER) {
+ s->init_data->curbuf->only_ts = true;
+ }
+
SCLogDebug("new: idx %u list %d set up curbuf %p", s->init_data->buffer_index - 1, list,
s->init_data->curbuf);
pm = pm2;
}
+ if (s->flags & SIG_FLAG_TXBOTHDIR && s->init_data->curbuf != NULL) {
+ if (DetectBufferToClient(de_ctx, s->init_data->curbuf->id, s->alproto)) {
+ if (s->init_data->init_flags & SIG_FLAG_INIT_TXDIR_STREAMING_TOSERVER) {
+ SCLogError("fast_pattern cannot be used on to_client keyword for "
+ "transactional rule with a streaming buffer to server %u",
+ s->id);
+ goto error;
+ }
+ s->init_data->init_flags |= SIG_FLAG_INIT_TXDIR_FAST_TOCLIENT;
+ }
+ }
+
cd = (DetectContentData *)pm->ctx;
if ((cd->flags & DETECT_CONTENT_NEGATED) &&
((cd->flags & DETECT_CONTENT_DISTANCE) ||
#ifdef UNITTESTS
sigmatch_table[DETECT_FILE_DATA].RegisterTests = DetectFiledataRegisterTests;
#endif
- sigmatch_table[DETECT_FILE_DATA].flags = SIGMATCH_NOOPT;
+ sigmatch_table[DETECT_FILE_DATA].flags = SIGMATCH_OPTIONAL_OPT;
filehandler_table[DETECT_FILE_DATA].name = "file_data";
filehandler_table[DETECT_FILE_DATA].priority = 2;
return -1;
}
+ if (DetectSetupDirection(s, str) < 0) {
+ SCLogError("file.data failed to setup direction");
+ return -1;
+ }
+
if (s->alproto == ALPROTO_SMTP && (s->init_data->init_flags & SIG_FLAG_INIT_FLOW) &&
!(s->flags & SIG_FLAG_TOSERVER) && (s->flags & SIG_FLAG_TOCLIENT)) {
SCLogError("The 'file-data' keyword cannot be used with SMTP flow:to_client or "
return -1;
s->init_data->init_flags |= SIG_FLAG_INIT_FILEDATA;
+ if ((s->init_data->init_flags & SIG_FLAG_INIT_FORCE_TOCLIENT) == 0) {
+ // we cannot use a transactional rule with a fast pattern to client and this
+ if (s->init_data->init_flags & SIG_FLAG_INIT_TXDIR_FAST_TOCLIENT) {
+ SCLogError("fast_pattern cannot be used on to_client keyword for "
+ "transactional rule with a streaming buffer to server %u",
+ s->id);
+ return -1;
+ }
+ s->init_data->init_flags |= SIG_FLAG_INIT_TXDIR_STREAMING_TOSERVER;
+ }
+ s->init_data->init_flags &= ~SIG_FLAG_INIT_FORCE_TOSERVER;
+ s->init_data->init_flags &= ~SIG_FLAG_INIT_FORCE_TOCLIENT;
+
SetupDetectEngineConfig(de_ctx);
return 0;
}
sigmatch_table[DETECT_FILE_MAGIC].desc = "sticky buffer to match on the file magic";
sigmatch_table[DETECT_FILE_MAGIC].url = "/rules/file-keywords.html#filemagic";
sigmatch_table[DETECT_FILE_MAGIC].Setup = DetectFilemagicSetupSticky;
- sigmatch_table[DETECT_FILE_MAGIC].flags = SIGMATCH_NOOPT|SIGMATCH_INFO_STICKY_BUFFER;
+ sigmatch_table[DETECT_FILE_MAGIC].flags = SIGMATCH_OPTIONAL_OPT | SIGMATCH_INFO_STICKY_BUFFER;
filehandler_table[DETECT_FILE_MAGIC].name = "file.magic",
filehandler_table[DETECT_FILE_MAGIC].priority = 2;
*/
static int DetectFilemagicSetupSticky(DetectEngineCtx *de_ctx, Signature *s, const char *str)
{
+ if (DetectSetupDirection(s, str) < 0) {
+ SCLogError("file.magic failed to setup direction");
+ return -1;
+ }
if (DetectBufferSetActiveList(de_ctx, s, g_file_magic_buffer_id) < 0)
return -1;
sigmatch_table[DETECT_FILE_NAME].desc = "sticky buffer to match on the file name";
sigmatch_table[DETECT_FILE_NAME].url = "/rules/file-keywords.html#filename";
sigmatch_table[DETECT_FILE_NAME].Setup = DetectFilenameSetupSticky;
- sigmatch_table[DETECT_FILE_NAME].flags = SIGMATCH_NOOPT|SIGMATCH_INFO_STICKY_BUFFER;
+ sigmatch_table[DETECT_FILE_NAME].flags = SIGMATCH_OPTIONAL_OPT | SIGMATCH_INFO_STICKY_BUFFER;
DetectBufferTypeSetDescriptionByName("file.name", "file name");
*/
static int DetectFilenameSetupSticky(DetectEngineCtx *de_ctx, Signature *s, const char *str)
{
+ if (DetectSetupDirection(s, str) < 0) {
+ SCLogError("file.name failed to setup direction");
+ return -1;
+ }
if (DetectBufferSetActiveList(de_ctx, s, g_file_name_buffer_id) < 0)
return -1;
s->file_flags |= (FILE_SIG_NEED_FILE | FILE_SIG_NEED_FILENAME);
bool appendsm = true;
/* set the signature direction flags */
if (fd->flags & DETECT_FLOW_FLAG_TOSERVER) {
+ if (s->flags & SIG_FLAG_TXBOTHDIR) {
+ SCLogError(
+ "rule %u means to use both directions, cannot specify a flow direction", s->id);
+ goto error;
+ }
s->flags |= SIG_FLAG_TOSERVER;
} else if (fd->flags & DETECT_FLOW_FLAG_TOCLIENT) {
+ if (s->flags & SIG_FLAG_TXBOTHDIR) {
+ SCLogError(
+ "rule %u means to use both directions, cannot specify a flow direction", s->id);
+ goto error;
+ }
s->flags |= SIG_FLAG_TOCLIENT;
} else {
s->flags |= SIG_FLAG_TOSERVER;
return -1;
if (DetectSignatureSetAppProto(s, ALPROTO_HTTP) < 0)
return -1;
+ // we cannot use a transactional rule with a fast pattern to client and this
+ if (s->init_data->init_flags & SIG_FLAG_INIT_TXDIR_FAST_TOCLIENT) {
+ SCLogError("fast_pattern cannot be used on to_client keyword for "
+ "transactional rule with a streaming buffer to server %u",
+ s->id);
+ return -1;
+ }
+ s->init_data->init_flags |= SIG_FLAG_INIT_TXDIR_STREAMING_TOSERVER;
return 0;
}
*/
static int DetectHttpHeadersSetupSticky(DetectEngineCtx *de_ctx, Signature *s, const char *str)
{
+ if (DetectSetupDirection(s, str) < 0) {
+ SCLogError(KEYWORD_NAME " failed to setup direction");
+ return -1;
+ }
+
if (DetectBufferSetActiveList(de_ctx, s, g_buffer_id) < 0)
return -1;
+ s->init_data->init_flags &= ~SIG_FLAG_INIT_FORCE_TOSERVER;
+ s->init_data->init_flags &= ~SIG_FLAG_INIT_FORCE_TOCLIENT;
+
if (DetectSignatureSetAppProto(s, ALPROTO_HTTP) < 0)
return -1;
sigmatch_table[KEYWORD_ID].desc = KEYWORD_NAME " sticky buffer for the " BUFFER_DESC;
sigmatch_table[KEYWORD_ID].url = "/rules/" KEYWORD_DOC;
sigmatch_table[KEYWORD_ID].Setup = DetectHttpHeadersSetupSticky;
+#if defined(KEYWORD_TOSERVER) && defined(KEYWORD_TOSERVER)
+ sigmatch_table[KEYWORD_ID].flags |= SIGMATCH_OPTIONAL_OPT | SIGMATCH_INFO_STICKY_BUFFER;
+#else
sigmatch_table[KEYWORD_ID].flags |= SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER;
+#endif
#ifdef KEYWORD_TOSERVER
DetectAppLayerMpmRegister(BUFFER_NAME, SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister,
if (strcmp(parser->direction, "<>") == 0) {
s->init_data->init_flags |= SIG_FLAG_INIT_BIDIREC;
+ } else if (strcmp(parser->direction, "=>") == 0) {
+ s->flags |= SIG_FLAG_TXBOTHDIR;
} else if (strcmp(parser->direction, "->") != 0) {
SCLogError("\"%s\" is not a valid direction modifier, "
"\"->\" and \"<>\" are supported.",
} bufdir[nlists + 1];
memset(&bufdir, 0, (nlists + 1) * sizeof(struct BufferVsDir));
+ int ts_excl = 0;
+ int tc_excl = 0;
+
for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
SignatureInitDataBuffer *b = &s->init_data->buffers[x];
const DetectBufferType *bt = DetectEngineBufferTypeGetById(de_ctx, b->id);
DetectEngineBufferTypeGetNameById(de_ctx, app->sm_list), app->dir,
app->alproto);
SCLogDebug("b->id %d nlists %d", b->id, nlists);
- bufdir[b->id].ts += (app->dir == 0);
- bufdir[b->id].tc += (app->dir == 1);
+ if (b->only_tc) {
+ if (app->dir == 1)
+ tc_excl++;
+ } else if (b->only_ts) {
+ if (app->dir == 0)
+ ts_excl++;
+ } else {
+ bufdir[b->id].ts += (app->dir == 0);
+ bufdir[b->id].tc += (app->dir == 1);
+ }
}
}
}
}
- int ts_excl = 0;
- int tc_excl = 0;
int dir_amb = 0;
for (int x = 0; x < nlists; x++) {
if (bufdir[x].ts == 0 && bufdir[x].tc == 0)
SCLogDebug("%s/%d: %d/%d", DetectEngineBufferTypeGetNameById(de_ctx, x), x, bufdir[x].ts,
bufdir[x].tc);
}
- if (ts_excl && tc_excl) {
- SCLogError("rule %u mixes keywords with conflicting directions", s->id);
+ if (s->flags & SIG_FLAG_TXBOTHDIR) {
+ if (!ts_excl || !tc_excl) {
+ SCLogError("rule %u should use both directions, but does not", s->id);
+ SCReturnInt(0);
+ }
+ if (dir_amb) {
+ SCLogError("rule %u means to use both directions, cannot have keywords ambiguous about "
+ "directions",
+ s->id);
+ SCReturnInt(0);
+ }
+ } else if (ts_excl && tc_excl) {
+ SCLogError(
+ "rule %u mixes keywords with conflicting directions, a transactional rule with => "
+ "should be used",
+ s->id);
SCReturnInt(0);
} else if (ts_excl) {
SCLogDebug("%u: implied rule direction is toserver", s->id);
}
}
+/**
+ * \brief Parse and setup a direction
+ *
+ * \param s siganture
+ * \param str argument to the keyword
+ *
+ * \retval 0 on success, -1 on failure
+ */
+int DetectSetupDirection(Signature *s, const char *str)
+{
+ if (str) {
+ if (strcmp(str, "to_client") == 0) {
+ s->init_data->init_flags |= SIG_FLAG_INIT_FORCE_TOCLIENT;
+ if ((s->flags & SIG_FLAG_TXBOTHDIR) == 0) {
+ if (s->flags & SIG_FLAG_TOSERVER) {
+ SCLogError("contradictory directions");
+ return -1;
+ }
+ s->flags |= SIG_FLAG_TOCLIENT;
+ }
+ } else if (strcmp(str, "to_server") == 0) {
+ s->init_data->init_flags |= SIG_FLAG_INIT_FORCE_TOSERVER;
+ if ((s->flags & SIG_FLAG_TXBOTHDIR) == 0) {
+ if (s->flags & SIG_FLAG_TOCLIENT) {
+ SCLogError("contradictory directions");
+ return -1;
+ }
+ s->flags |= SIG_FLAG_TOSERVER;
+ }
+ } else {
+ SCLogError("unknown option: only accepts to_server or to_client");
+ return -1;
+ }
+ }
+ return 0;
+}
/*
* TESTS
int SC_Pcre2SubstringGet(pcre2_match_data *match_data, uint32_t number, PCRE2_UCHAR **bufferptr,
PCRE2_SIZE *bufflen);
+int DetectSetupDirection(Signature *s, const char *str);
+
#endif /* SURICATA_DETECT_PARSE_H */
#include "detect.h"
#include "detect-parse.h"
#include "detect-content.h"
+#include "detect-engine-mpm.h"
#include "detect-prefilter.h"
#include "util-debug.h"
/* if the sig match is content, prefilter should act like
* 'fast_pattern' w/o options. */
if (sm->type == DETECT_CONTENT) {
+ if (s->flags & SIG_FLAG_TXBOTHDIR && s->init_data->curbuf != NULL) {
+ if (s->init_data->init_flags & SIG_FLAG_INIT_TXDIR_STREAMING_TOSERVER) {
+ if (DetectBufferToClient(de_ctx, s->init_data->curbuf->id, s->alproto)) {
+ SCLogError("prefilter cannot be used on to_client keyword for "
+ "transactional rule %u",
+ s->id);
+ SCReturnInt(-1);
+ } else {
+ s->init_data->init_flags |= SIG_FLAG_INIT_TXDIR_FAST_TOCLIENT;
+ }
+ }
+ }
+
DetectContentData *cd = (DetectContentData *)sm->ctx;
if ((cd->flags & DETECT_CONTENT_NEGATED) &&
((cd->flags & DETECT_CONTENT_DISTANCE) ||
const DetectEngineAppInspectionEngine *engine = s->app_inspect;
do {
TRACE_SID_TXS(s->id, tx, "engine %p inspect_flags %x", engine, inspect_flags);
+ // also if it is not the same direction, but
+ // this is a transactional signature, and we are toclient
if (!(inspect_flags & BIT_U32(engine->id)) &&
- direction == engine->dir)
- {
+ (direction == engine->dir || ((s->flags & SIG_FLAG_TXBOTHDIR) && direction == 1))) {
+
void *tx_ptr = DetectGetInnerTx(tx->tx_ptr, f->alproto, engine->alproto, flow_flags);
if (tx_ptr == NULL) {
if (engine->alproto != ALPROTO_UNKNOWN) {
}
}
+ uint8_t engine_flags = flow_flags;
+ if (direction != engine->dir) {
+ engine_flags = flow_flags ^ (STREAM_TOCLIENT | STREAM_TOSERVER);
+ }
/* run callback: but bypass stream callback if we can */
uint8_t match;
if (unlikely(engine->stream && can->stream_stored)) {
KEYWORD_PROFILING_SET_LIST(det_ctx, engine->sm_list);
DEBUG_VALIDATE_BUG_ON(engine->v2.Callback == NULL);
match = engine->v2.Callback(
- de_ctx, det_ctx, engine, s, f, flow_flags, alstate, tx_ptr, tx->tx_id);
+ de_ctx, det_ctx, engine, s, f, engine_flags, alstate, tx_ptr, tx->tx_id);
TRACE_SID_TXS(s->id, tx, "engine %p match %d", engine, match);
if (engine->stream) {
can->stream_stored = true;
inspect_flags |= BIT_U32(engine->id);
}
break;
+ } else if (!(inspect_flags & BIT_U32(engine->id)) && s->flags & SIG_FLAG_TXBOTHDIR &&
+ direction != engine->dir) {
+ // for transactional rules, the engines on the opposite direction
+ // are ordered by progress on the different side
+ // so we have a two mixed-up lists, and we skip the elements
+ if (direction == 0 && engine->next == NULL) {
+ // do not match yet on request only
+ break;
+ }
+ engine = engine->next;
+ continue;
}
+
engine = engine->next;
} while (engine != NULL);
TRACE_SID_TXS(s->id, tx, "inspect_flags %x, total_matches %u, engine %p",
#define SIG_FLAG_DSIZE BIT_U32(5) /**< signature has a dsize setting */
#define SIG_FLAG_APPLAYER BIT_U32(6) /**< signature applies to app layer instead of packets */
+#define SIG_FLAG_TXBOTHDIR BIT_U32(7) /**< signature needs tx with both directions to match */
// vacancy
#define SIG_FLAG_INIT_NEED_FLUSH BIT_U32(7)
#define SIG_FLAG_INIT_PRIO_EXPLICIT \
BIT_U32(8) /**< priority is explicitly set by the priority keyword */
-#define SIG_FLAG_INIT_FILEDATA BIT_U32(9) /**< signature has filedata keyword */
+#define SIG_FLAG_INIT_FILEDATA BIT_U32(9) /**< signature has filedata keyword */
+#define SIG_FLAG_INIT_FORCE_TOCLIENT BIT_U32(10) /**< signature now takes keywords toclient */
+#define SIG_FLAG_INIT_FORCE_TOSERVER BIT_U32(11) /**< signature now takes keywords toserver */
+// Two following flags are meant to be mutually exclusive
+#define SIG_FLAG_INIT_TXDIR_STREAMING_TOSERVER \
+ BIT_U32(12) /**< transactional signature uses a streaming buffer to server */
+#define SIG_FLAG_INIT_TXDIR_FAST_TOCLIENT \
+ BIT_U32(13) /**< transactional signature uses a fast pattern to client */
/* signature mask flags */
/** \note: additions should be added to the rule analyzer as well */
set up. */
bool multi_capable; /**< true if we can have multiple instances of this buffer, so e.g. for
http.uri. */
+ bool only_tc; /**< true if we can only used toclient. */
+ bool only_ts; /**< true if we can only used toserver. */
/* sig match list */
SigMatch *head;
SigMatch *tail;