/** convert enum to string */
#define CASE_CODE(E) case E: return #E
+/** The DetectEngineThreadCtx::de_state_sig_array contains 2 separate values:
+ * 1. the first bit tells the prefilter engine to bypass the rule (or not)
+ * 2. the other bits allow 'ContinueDetect' to specify an offset again the
+ * base tx id. This offset will then be used by 'StartDetect' to not
+ * inspect transactions again for the same signature.
+ *
+ * The offset in (2) has a max value due to the limited data type. If it is
+ * set to max the code will fall back to a slower path that validates that
+ * we're not adding duplicate rules to the detection state.
+ */
+#define MAX_STORED_TXID_OFFSET 127
+
/******** static internal helpers *********/
static inline int StateIsValid(uint16_t alproto, void *alstate)
return d;
}
-#ifdef DEBUG_VALIDATION
static int DeStateSearchState(DetectEngineState *state, uint8_t direction, SigIntId num)
{
DetectEngineStateDirection *dir_state = &state->dir_state[direction & STREAM_TOSERVER ? 0 : 1];
{
DeStateStoreItem *item = &tx_store->store[store_cnt];
if (item->sid == num) {
- SCLogDebug("BUG! sid %u already in state: %p %p %p %u %u, direction %s",
+ SCLogDebug("sid %u already in state: %p %p %p %u %u, direction %s",
num, state, dir_state, tx_store, state_cnt,
store_cnt, direction & STREAM_TOSERVER ? "toserver" : "toclient");
return 1;
}
return 0;
}
-#endif
static void DeStateSignatureAppend(DetectEngineState *state, Signature *s, uint32_t inspect_flags, uint8_t direction)
{
}
}
+/**
+ * \param check_before_add check for duplicates before adding the sig
+ */
static void StoreStateTx(DetectEngineThreadCtx *det_ctx,
Flow *f, const uint8_t flags, const uint16_t alversion,
const uint64_t tx_id, void *tx,
Signature *s, SigMatch *sm,
- const uint32_t inspect_flags, const uint16_t file_no_match)
+ const uint32_t inspect_flags, const uint16_t file_no_match, int check_before_add)
{
if (AppLayerParserSupportsTxDetectState(f->proto, f->alproto)) {
DetectEngineState *destate = AppLayerParserGetTxDetectState(f->proto, f->alproto, tx);
SCLogDebug("destate created for %"PRIu64, tx_id);
}
- DeStateSignatureAppend(destate, s, inspect_flags, flags);
+ if (check_before_add == 0 || DeStateSearchState(destate, flags, s->num) == 0)
+ DeStateSignatureAppend(destate, s, inspect_flags, flags);
DeStateStoreStateVersion(f, alversion, flags);
StoreStateTxHandleFiles(det_ctx, f, destate, flags, tx_id, file_no_match);
uint32_t inspect_flags = 0;
uint8_t direction = (flags & STREAM_TOSERVER) ? 0 : 1;
int alert_cnt = 0;
+ int check_before_add = 0;
FLOWLOCK_WRLOCK(f);
/* TX based matches (inspect engines) */
goto end;
}
+ /* if continue detection already inspected this rule for this tx,
+ * continue with the first not-inspected tx */
+ uint8_t offset = det_ctx->de_state_sig_array[s->num] & 0xef;
tx_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
+ if (offset > 0) {
+ SCLogDebug("using stored_tx_id %u instead of %u", (uint)tx_id+offset, (uint)tx_id);
+ tx_id += offset;
+ }
+ if (offset == MAX_STORED_TXID_OFFSET) {
+ check_before_add = 1;
+ }
+
total_txs = AppLayerParserGetTxCnt(f->proto, alproto, alstate);
SCLogDebug("total_txs %"PRIu64, total_txs);
/* store */
StoreStateTx(det_ctx, f, flags, alversion, tx_id, tx,
- s, sm, inspect_flags, file_no_match);
+ s, sm, inspect_flags, file_no_match, check_before_add);
} else {
StoreStateTxFileOnly(det_ctx, f, flags, tx_id, tx, file_no_match);
}
SCLogDebug("skip and bypass: tx %u packet %u", (uint)inspect_tx_id, (uint)p->pcap_cnt);
} else {
SCLogDebug("just skip: tx %u packet %u", (uint)inspect_tx_id, (uint)p->pcap_cnt);
+
+ /* make sure that if we reinspect this right now from
+ * start detection, we skip this tx we just matched on */
+ uint64_t base_tx_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
+ uint64_t offset = (inspect_tx_id + 1) - base_tx_id;
+ if (offset > MAX_STORED_TXID_OFFSET)
+ offset = MAX_STORED_TXID_OFFSET;
+ det_ctx->de_state_sig_array[item->sid] = (uint8_t)offset;
+ BUG_ON(det_ctx->de_state_sig_array[item->sid] & DE_STATE_MATCH_NO_NEW_STATE); // check that we don't set the bit
+ SCLogDebug("storing tx_id %u for this sid", (uint)inspect_tx_id + 1);
}
return 0;
}
SCLogDebug("skip and bypass: tx %u packet %u", (uint)inspect_tx_id, (uint)p->pcap_cnt);
} else {
SCLogDebug("just skip: tx %u packet %u", (uint)inspect_tx_id, (uint)p->pcap_cnt);
+
+ /* make sure that if we reinspect this right now from
+ * start detection, we skip this tx we just matched on */
+ uint64_t base_tx_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
+ uint64_t offset = (inspect_tx_id + 1) - base_tx_id;
+ if (offset > MAX_STORED_TXID_OFFSET)
+ offset = MAX_STORED_TXID_OFFSET;
+ det_ctx->de_state_sig_array[item->sid] = (uint8_t)offset;
+ BUG_ON(det_ctx->de_state_sig_array[item->sid] & DE_STATE_MATCH_NO_NEW_STATE); // check that we don't set the bit
+ SCLogDebug("storing tx_id %u for this sid", (uint)inspect_tx_id + 1);
}
return 0;
}
if (TxIsLast(inspect_tx_id, total_txs) || inprogress || next_tx_no_progress) {
det_ctx->de_state_sig_array[item->sid] = DE_STATE_MATCH_NO_NEW_STATE;
SCLogDebug("inspected, now bypass: tx %u packet %u", (uint)inspect_tx_id, (uint)p->pcap_cnt);
+ } else {
+ /* make sure that if we reinspect this right now from
+ * start detection, we skip this tx we just matched on */
+ uint64_t base_tx_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
+ uint64_t offset = (inspect_tx_id + 1) - base_tx_id;
+ if (offset > MAX_STORED_TXID_OFFSET)
+ offset = MAX_STORED_TXID_OFFSET;
+ det_ctx->de_state_sig_array[item->sid] = (uint8_t)offset;
+ BUG_ON(det_ctx->de_state_sig_array[item->sid] & DE_STATE_MATCH_NO_NEW_STATE); // check that we don't set the bit
+
+ SCLogDebug("storing tx_id %u for this sid", (uint)inspect_tx_id + 1);
}
RULE_PROFILING_END(det_ctx, s, (alert == 1), p);
}
}
-/** \brief get string for match enum */
-const char *DeStateMatchResultToString(DeStateMatchResult res)
-{
- switch (res) {
- CASE_CODE (DE_STATE_MATCH_NO_NEW_STATE);
- CASE_CODE (DE_STATE_MATCH_HAS_NEW_STATE);
- }
-
- return NULL;
-}
-
/*********Unittests*********/
#ifdef UNITTESTS