Implement `xbits:set,mybit,track tx;` and `xbits:isset,mybit,track tx;`.
Store these in the AppLayerTxData.
Ticket: #6455.
"AppLayerParserState",
"CLuaState",
"DetectEngineState",
+ "GenericVar",
"Flow",
"StreamingBufferConfig",
"HttpRangeContainerBlock",
//! Parser registration functions and common interface module.
use std;
-use crate::core::{self,DetectEngineState,AppLayerEventType};
+use crate::core::{self,DetectEngineState,AppLayerEventType, GenericVar};
use crate::direction::Direction;
use crate::filecontainer::FileContainer;
use crate::flow::Flow;
de_state: *mut DetectEngineState,
pub events: *mut core::AppLayerDecoderEvents,
+ txbits: *mut GenericVar,
}
impl Default for AppLayerTxData {
if !self.events.is_null() {
core::sc_app_layer_decoder_events_free_events(&mut self.events);
}
+ if !self.txbits.is_null() {
+ core::sc_generic_var_free(self.txbits);
+ }
}
/// Create new AppLayerTxData for a transaction that covers both
detect_progress_tc: 0,
de_state: std::ptr::null_mut(),
events: std::ptr::null_mut(),
+ txbits: std::ptr::null_mut(),
}
}
flags,
de_state: std::ptr::null_mut(),
events: std::ptr::null_mut(),
+ txbits: std::ptr::null_mut(),
}
}
/// Opaque C types.
pub enum DetectEngineState {}
pub enum AppLayerDecoderEvents {}
+pub enum GenericVar {}
#[repr(C)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
file_container: &FileContainer,
sbcfg: &StreamingBufferConfig);
+pub type GenericVarFreeFunc =
+ extern "C" fn(gvar: *mut GenericVar);
+
// A Suricata context that is passed in from C. This is alternative to
// using functions from Suricata directly, so they can be wrapped so
// Rust unit tests will still compile when they are not linked
pub FileAppendData: SCFileAppendDataById,
pub FileAppendGAP: SCFileAppendGAPById,
pub FileContainerRecycle: SCFileContainerRecycle,
+
+ GenericVarFree: GenericVarFreeFunc,
}
#[allow(non_snake_case)]
}
}
+/// GenericVarFree wrapper.
+pub fn sc_generic_var_free(gvar: *mut GenericVar)
+{
+ unsafe {
+ if let Some(c) = SC {
+ (c.GenericVarFree)(gvar);
+ }
+ }
+}
+
/// AppLayerParserTriggerRawStreamReassembly wrapper
pub fn sc_app_layer_parser_trigger_raw_stream_reassembly(flow: *const Flow, direction: i32) {
unsafe {
tm-threads-common.h \
tm-threads.h \
tree.h \
+ tx-bit.h \
interval-tree.h \
unix-manager.h \
util-action.h \
tm-queuehandlers.c \
tm-queues.c \
tm-threads.c \
+ tx-bit.c \
unix-manager.c \
util-action.c \
util-affinity.c \
#include "flow-bit.h"
#include "host-bit.h"
#include "ippair-bit.h"
+#include "tx-bit.h"
+
#include "util-var-name.h"
#include "util-unittest.h"
#include "util-debug.h"
#define PARSE_REGEX "^([a-z]+)" "(?:,\\s*([^,]+))?" "(?:,\\s*(?:track\\s+([^,]+)))" "(?:,\\s*(?:expire\\s+([^,]+)))?"
static DetectParseRegex parse_regex;
+static int DetectXbitTxMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state,
+ void *txv, const Signature *s, const SigMatchCtx *ctx);
static int DetectXbitMatch (DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *);
static int DetectXbitSetup (DetectEngineCtx *, Signature *, const char *);
#ifdef UNITTESTS
sigmatch_table[DETECT_XBITS].name = "xbits";
sigmatch_table[DETECT_XBITS].desc = "operate on bits";
sigmatch_table[DETECT_XBITS].url = "/rules/xbits.html";
+ sigmatch_table[DETECT_XBITS].AppLayerTxMatch = DetectXbitTxMatch;
sigmatch_table[DETECT_XBITS].Match = DetectXbitMatch;
sigmatch_table[DETECT_XBITS].Setup = DetectXbitSetup;
sigmatch_table[DETECT_XBITS].Free = DetectXbitFree;
return 0;
}
+static int DetectXbitPostMatchTx(
+ DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const DetectXbitsData *xd)
+{
+ if (p->flow == NULL)
+ return 0;
+ if (!det_ctx->tx_id_set)
+ return 0;
+ Flow *f = p->flow;
+ void *txv = AppLayerParserGetTx(f->proto, f->alproto, f->alstate, det_ctx->tx_id);
+ if (txv == NULL)
+ return 0;
+ AppLayerTxData *txd = AppLayerParserGetTxData(f->proto, f->alproto, txv);
+ if (txd == NULL) {
+ return 0;
+ }
+
+ if (xd->cmd != DETECT_XBITS_CMD_SET)
+ return 0;
+
+ SCLogDebug("sid %u: post-match SET for bit %u on tx:%" PRIu64 ", txd:%p", s->id, xd->idx,
+ det_ctx->tx_id, txd);
+
+ return TxBitSet(txd, xd->idx);
+}
+
/*
* returns 0: no match
* 1: match
case VAR_TYPE_IPPAIR_BIT:
return DetectXbitMatchIPPair(p, (const DetectXbitsData *)fd);
break;
+ case VAR_TYPE_TX_BIT:
+ // TODO this is for PostMatch only. Can we validate somehow?
+ return DetectXbitPostMatchTx(det_ctx, p, s, fd);
+ break;
default:
break;
}
return 0;
}
+static int DetectXbitTxMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state,
+ void *txv, const Signature *s, const SigMatchCtx *ctx)
+{
+ const DetectXbitsData *xd = (const DetectXbitsData *)ctx;
+ BUG_ON(xd == NULL);
+
+ AppLayerTxData *txd = AppLayerParserGetTxData(f->proto, f->alproto, txv);
+ if (txd == NULL) {
+ return 0;
+ }
+
+ SCLogDebug("sid:%u: tx:%" PRIu64 ", txd->txbits:%p", s->id, det_ctx->tx_id, txd->txbits);
+ int r = TxBitIsset(txd, xd->idx);
+ if (r == 1) {
+ return DETECT_ENGINE_INSPECT_SIG_MATCH;
+ }
+ return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+}
+
/** \internal
* \brief parse xbits rule options
* \retval 0 ok
} else if (strcmp(hb_dir_str, "ip_pair") == 0) {
hb_dir = DETECT_XBITS_TRACK_IPPAIR;
var_type = VAR_TYPE_IPPAIR_BIT;
+ } else if (strcmp(hb_dir_str, "tx") == 0) {
+ hb_dir = DETECT_XBITS_TRACK_TX;
+ var_type = VAR_TYPE_TX_BIT;
} else {
// TODO
pcre2_match_data_free(match);
break;
}
+ if (hb_dir == DETECT_XBITS_TRACK_TX) {
+ if (fb_cmd != DETECT_XBITS_CMD_ISSET && fb_cmd != DETECT_XBITS_CMD_SET) {
+ SCLogError("tx xbits only support set and isset");
+ return -1;
+ }
+ }
+
cd = SCMalloc(sizeof(DetectXbitsData));
if (unlikely(cd == NULL))
return -1;
cd->type = var_type;
cd->expire = expire;
- SCLogDebug("idx %" PRIu32 ", cmd %s, name %s",
- cd->idx, fb_cmd_str, strlen(fb_name) ? fb_name : "(none)");
+ SCLogDebug("idx %" PRIu32 ", cmd %s, name %s", cd->idx, fb_cmd_str,
+ strlen(fb_name) ? fb_name : "(none)");
*cdout = cd;
return 0;
switch (cd->cmd) {
/* case DETECT_XBITS_CMD_NOALERT can't happen here */
case DETECT_XBITS_CMD_ISNOTSET:
- case DETECT_XBITS_CMD_ISSET:
+ case DETECT_XBITS_CMD_ISSET: {
+ int list = DETECT_SM_LIST_MATCH;
+ if (cd->tracker == DETECT_XBITS_TRACK_TX) {
+ SCLogDebug("tx xbit isset");
+ if (s->init_data->hook.type != SIGNATURE_HOOK_TYPE_APP) {
+ SCLogError("tx xbits require an explicit rule hook");
+ SCFree(cd);
+ return -1;
+ }
+ list = s->init_data->hook.sm_list;
+ SCLogDebug("setting list %d", list);
+
+ if (list == -1) {
+ SCLogError("tx xbits failed to set up"); // TODO how would we get here?
+ SCFree(cd);
+ return -1;
+ }
+ }
+
+ SCLogDebug("adding match/txmatch");
/* checks, so packet list */
- if (SigMatchAppendSMToList(
- de_ctx, s, DETECT_XBITS, (SigMatchCtx *)cd, DETECT_SM_LIST_MATCH) == NULL) {
+ if (SigMatchAppendSMToList(de_ctx, s, DETECT_XBITS, (SigMatchCtx *)cd, list) == NULL) {
SCFree(cd);
return -1;
}
break;
-
+ }
// all other cases
// DETECT_XBITS_CMD_SET, DETECT_XBITS_CMD_UNSET, DETECT_XBITS_CMD_TOGGLE:
default:
+ SCLogDebug("adding post-match");
/* modifiers, only run when entire sig has matched */
if (SigMatchAppendSMToList(de_ctx, s, DETECT_XBITS, (SigMatchCtx *)cd,
DETECT_SM_LIST_POSTMATCH) == NULL) {
#define DETECT_XBITS_TRACK_IPSRC 0
#define DETECT_XBITS_TRACK_IPDST 1
#define DETECT_XBITS_TRACK_IPPAIR 2
+#define DETECT_XBITS_TRACK_TX 3
#define DETECT_XBITS_EXPIRE_DEFAULT 30
#include "app-layer-register.h"
#include "app-layer-htp-range.h"
#include "app-layer-htp-file.h"
+#include "util-var.h"
const SuricataContext suricata_context = {
SCLogMessage,
FileAppendDataById,
FileAppendGAPById,
FileContainerRecycle,
+
+ GenericVarFree,
};
const SuricataContext *SCGetContext(void)
#include "util-debug.h"
#include "util-file.h"
+#include "util-var.h"
// hack for include orders cf SCSha256
typedef struct HttpRangeContainerBlock HttpRangeContainerBlock;
int (*FileAppendGAPById)(FileContainer *, const StreamingBufferConfig *, uint32_t track_id,
const uint8_t *data, uint32_t data_len);
void (*FileContainerRecycle)(FileContainer *ffc, const StreamingBufferConfig *);
+
+ void (*GenericVarFree)(GenericVar *);
} SuricataContext;
extern const SuricataContext suricata_context;
--- /dev/null
+/* Copyright (C) 2014-2021 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements per ippair bits. Actually, not a bit,
+ * but called that way because of Snort's flowbits.
+ * It's a binary storage.
+ *
+ * \todo move away from a linked list implementation
+ * \todo use different datatypes, such as string, int, etc.
+ */
+
+#include "suricata-common.h"
+#include "threads.h"
+#include "tx-bit.h"
+#include "detect.h"
+#include "util-var.h"
+#include "util-debug.h"
+#include "rust.h"
+
+static XBit *TxBitGet(AppLayerTxData *txd, uint32_t idx)
+{
+ for (GenericVar *gv = txd->txbits; gv != NULL; gv = gv->next) {
+ if (gv->type == DETECT_XBITS && gv->idx == idx) {
+ return (XBit *)gv;
+ }
+ }
+
+ return NULL;
+}
+
+static int TxBitAdd(AppLayerTxData *txd, uint32_t idx)
+{
+ XBit *xb = TxBitGet(txd, idx);
+ if (xb == NULL) {
+ xb = SCMalloc(sizeof(XBit));
+ if (unlikely(xb == NULL))
+ return -1;
+
+ xb->type = DETECT_XBITS;
+ xb->idx = idx;
+ xb->next = NULL;
+ xb->expire = 0; // not used by tx bits
+
+ GenericVarAppend(&txd->txbits, (GenericVar *)xb);
+ return 1;
+ }
+ return 0;
+}
+
+int TxBitIsset(AppLayerTxData *txd, uint32_t idx)
+{
+ XBit *xb = TxBitGet(txd, idx);
+ if (xb != NULL) {
+ SCLogDebug("isset %u return 1", idx);
+ return 1;
+ }
+ SCLogDebug("isset %u r 0", idx);
+ return 0;
+}
+
+int TxBitSet(AppLayerTxData *txd, uint32_t idx)
+{
+ int r = TxBitAdd(txd, idx);
+ SCLogDebug("set %u r %d", idx, r);
+ return (r == 1);
+}
--- /dev/null
+/* Copyright (C) 2007-2025 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef SURICATA_TX_BIT_H
+#define SURICATA_TX_BIT_H
+
+#include "rust.h"
+
+int TxBitIsset(AppLayerTxData *txd, uint32_t idx);
+int TxBitSet(AppLayerTxData *txd, uint32_t idx);
+
+#endif /* SURICATA_TX_BIT_H */
VAR_TYPE_IPPAIR_BIT,
VAR_TYPE_IPPAIR_INT,
VAR_TYPE_IPPAIR_VAR,
+
+ VAR_TYPE_TX_BIT,
};
typedef struct GenericVar_ {