detect-app-layer-protocol: implement prefilter

Introduce 'Protocol detection'-only rules. These rules will only be
fully evaluated when the protocol detection completed. To allow
mixing of the app-layer-protocol keyword with other types of matches
the keyword can also inspect the flow's app-protos per packet.

Implement prefilter for the 'PD-only' rules.
pull/2360/head
Victor Julien 9 years ago
parent 8094b2b12e
commit 0ed119068d

@ -23,6 +23,8 @@
#include "suricata-common.h"
#include "detect-engine.h"
#include "detect-engine-prefilter.h"
#include "detect-engine-prefilter-common.h"
#include "detect-parse.h"
#include "detect-app-layer-protocol.h"
#include "app-layer.h"
@ -42,28 +44,44 @@ static int DetectAppLayerProtocolPacketMatch(ThreadVars *tv,
int r = 0;
const DetectAppLayerProtocolData *data = (const DetectAppLayerProtocolData *)ctx;
if ((p->flags & (PKT_PROTO_DETECT_TS_DONE|PKT_PROTO_DETECT_TC_DONE)) == 0) {
SCLogNotice("packet %u: flags not set", (uint)p->pcap_cnt);
/* if the sig is PD-only we only match when PD packet flags are set */
if ((s->flags & SIG_FLAG_PDONLY) &&
(p->flags & (PKT_PROTO_DETECT_TS_DONE|PKT_PROTO_DETECT_TC_DONE)) == 0)
{
SCLogDebug("packet %"PRIu64": flags not set", p->pcap_cnt);
SCReturnInt(0);
}
const Flow *f = p->flow;
if (f == NULL) {
SCLogNotice("packet %u: no flow", (uint)p->pcap_cnt);
SCLogDebug("packet %"PRIu64": no flow", p->pcap_cnt);
SCReturnInt(0);
}
if ((f->alproto_ts != ALPROTO_UNKNOWN) && (p->flowflags & FLOW_PKT_TOSERVER)) {
SCLogNotice("toserver packet %u: looking for %u/neg %u, got %u", (uint)p->pcap_cnt,
data->alproto, data->negated, f->alproto_ts);
/* unknown means protocol detection isn't ready yet */
if ((f->alproto_ts != ALPROTO_UNKNOWN) && (p->flowflags & FLOW_PKT_TOSERVER))
{
SCLogDebug("toserver packet %"PRIu64": looking for %u/neg %u, got %u",
p->pcap_cnt, data->alproto, data->negated, f->alproto_ts);
r = (data->negated) ? (f->alproto_ts != data->alproto) :
(f->alproto_ts == data->alproto);
} else if ((f->alproto_tc != ALPROTO_UNKNOWN) && (p->flowflags & FLOW_PKT_TOCLIENT)) {
SCLogNotice("toclient packet %u: looking for %u/neg %u, got %u", (uint)p->pcap_cnt,
data->alproto, data->negated, f->alproto_tc);
} else if ((f->alproto_tc != ALPROTO_UNKNOWN) && (p->flowflags & FLOW_PKT_TOCLIENT))
{
SCLogDebug("toclient packet %"PRIu64": looking for %u/neg %u, got %u",
p->pcap_cnt, data->alproto, data->negated, f->alproto_tc);
r = (data->negated) ? (f->alproto_tc != data->alproto) :
(f->alproto_tc == data->alproto);
}
else {
SCLogDebug("packet %"PRIu64": default case: direction %02x, approtos %u/%u/%u",
p->pcap_cnt,
p->flowflags & (FLOW_PKT_TOCLIENT|FLOW_PKT_TOSERVER),
f->alproto, f->alproto_ts, f->alproto_tc);
}
SCReturnInt(r);
}
@ -140,8 +158,6 @@ static int DetectAppLayerProtocolSetup(DetectEngineCtx *de_ctx,
goto error;
}
}
s->alproto = data->alproto;
}
sm = SigMatchAlloc();
@ -151,9 +167,7 @@ static int DetectAppLayerProtocolSetup(DetectEngineCtx *de_ctx,
sm->type = DETECT_AL_APP_LAYER_PROTOCOL;
sm->ctx = (void *)data;
SCLogNotice("DETECT_SM_LIST_MATCH");
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
return 0;
error:
@ -168,6 +182,79 @@ static void DetectAppLayerProtocolFree(void *ptr)
return;
}
/** \internal
* \brief prefilter function for protocol detect matching
*/
static void
PrefilterPacketAppProtoMatch(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
{
const PrefilterPacketHeaderCtx *ctx = pectx;
if (PrefilterPacketHeaderExtraMatch(ctx, p) == FALSE) {
SCLogDebug("packet %"PRIu64": extra match failed", p->pcap_cnt);
SCReturn;
}
if (p->flow == NULL) {
SCLogDebug("packet %"PRIu64": no flow, no alproto", p->pcap_cnt);
SCReturn;
}
if ((p->flags & (PKT_PROTO_DETECT_TS_DONE|PKT_PROTO_DETECT_TC_DONE)) == 0) {
SCLogDebug("packet %"PRIu64": flags not set", p->pcap_cnt);
SCReturn;
}
if ((p->flags & PKT_PROTO_DETECT_TS_DONE) && (p->flowflags & FLOW_PKT_TOSERVER))
{
int r = (ctx->v1.u16[0] == p->flow->alproto_ts) ^ ctx->v1.u8[2];
if (r) {
PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt);
}
} else if ((p->flags & PKT_PROTO_DETECT_TC_DONE) && (p->flowflags & FLOW_PKT_TOCLIENT))
{
int r = (ctx->v1.u16[0] == p->flow->alproto_tc) ^ ctx->v1.u8[2];
if (r) {
PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt);
}
}
}
static void
PrefilterPacketAppProtoSet(PrefilterPacketHeaderValue *v, void *smctx)
{
const DetectAppLayerProtocolData *a = smctx;
v->u16[0] = a->alproto;
v->u8[2] = (uint8_t)a->negated;
}
static _Bool
PrefilterPacketAppProtoCompare(PrefilterPacketHeaderValue v, void *smctx)
{
const DetectAppLayerProtocolData *a = smctx;
if (v.u16[0] == a->alproto &&
v.u8[2] == (uint8_t)a->negated)
return TRUE;
return FALSE;
}
static int PrefilterSetupAppProto(SigGroupHead *sgh)
{
return PrefilterSetupPacketHeader(sgh, DETECT_AL_APP_LAYER_PROTOCOL,
PrefilterPacketAppProtoSet,
PrefilterPacketAppProtoCompare,
PrefilterPacketAppProtoMatch);
}
static _Bool PrefilterAppProtoIsPrefilterable(const Signature *s)
{
if (s->flags & SIG_FLAG_PDONLY) {
SCLogDebug("prefilter on PD %u", s->id);
return TRUE;
}
return FALSE;
}
void DetectAppLayerProtocolRegister(void)
{
sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].name = "app-layer-protocol";
@ -180,6 +267,10 @@ void DetectAppLayerProtocolRegister(void)
sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].RegisterTests =
DetectAppLayerProtocolRegisterTests;
sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].SetupPrefilter =
PrefilterSetupAppProto;
sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].SupportsPrefilter =
PrefilterAppProtoIsPrefilterable;
return;
}

@ -840,6 +840,7 @@ void EngineAnalysisRules(const Signature *s, const char *line)
fprintf(rule_engine_analysis_FD, "%s\n", line);
if (s->flags & SIG_FLAG_IPONLY) fprintf(rule_engine_analysis_FD, " Rule is ip only.\n");
if (s->flags & SIG_FLAG_PDONLY) fprintf(rule_engine_analysis_FD, " Rule is PD only.\n");
if (rule_ipv6_only) fprintf(rule_engine_analysis_FD, " Rule is IPv6 only.\n");
if (rule_ipv4_only) fprintf(rule_engine_analysis_FD, " Rule is IPv4 only.\n");
if (packet_buf) fprintf(rule_engine_analysis_FD, " Rule matches on packets.\n");

@ -1998,6 +1998,103 @@ iponly:
return 1;
}
/** \internal
* \brief Test is a initialized signature is inspecting protocol detection only
* \param de_ctx detection engine ctx
* \param s the signature
* \retval 1 sig is dp only
* \retval 0 sig is not dp only
*/
static int SignatureIsPDOnly(const Signature *s)
{
if (s->alproto != ALPROTO_UNKNOWN)
return 0;
if (s->sm_lists[DETECT_SM_LIST_PMATCH] != NULL)
return 0;
if (s->sm_lists[DETECT_SM_LIST_UMATCH] != NULL)
return 0;
if (s->sm_lists[DETECT_SM_LIST_HCBDMATCH] != NULL)
return 0;
if (s->sm_lists[DETECT_SM_LIST_FILEDATA] != NULL)
return 0;
if (s->sm_lists[DETECT_SM_LIST_HHDMATCH] != NULL)
return 0;
if (s->sm_lists[DETECT_SM_LIST_HRHDMATCH] != NULL)
return 0;
if (s->sm_lists[DETECT_SM_LIST_HMDMATCH] != NULL)
return 0;
if (s->sm_lists[DETECT_SM_LIST_HCDMATCH] != NULL)
return 0;
if (s->sm_lists[DETECT_SM_LIST_HRUDMATCH] != NULL)
return 0;
if (s->sm_lists[DETECT_SM_LIST_HSMDMATCH] != NULL)
return 0;
if (s->sm_lists[DETECT_SM_LIST_HSCDMATCH] != NULL)
return 0;
if (s->sm_lists[DETECT_SM_LIST_HUADMATCH] != NULL)
return 0;
if (s->sm_lists[DETECT_SM_LIST_HHHDMATCH] != NULL)
return 0;
if (s->sm_lists[DETECT_SM_LIST_HRHHDMATCH] != NULL)
return 0;
if (s->sm_lists[DETECT_SM_LIST_AMATCH] != NULL)
return 0;
/* TMATCH list can be ignored, it contains TAGs and
* tags are compatible to DP-only. */
/* match list matches may be compatible to DP only. We follow the same
* logic as IP-only so we can use that flag */
SigMatch *sm = s->sm_lists[DETECT_SM_LIST_MATCH];
if (sm == NULL)
return 0;
int pd = 0;
for ( ; sm != NULL; sm = sm->next) {
if (sm->type == DETECT_AL_APP_LAYER_PROTOCOL) {
pd = 1;
} else {
/* flowbits are supported for dp only sigs, as long
* as the sig only has a "set" flowbits */
if (sm->type == DETECT_FLOWBITS) {
if ((((DetectFlowbitsData *)sm->ctx)->cmd != DETECT_FLOWBITS_CMD_SET) ) {
SCLogDebug("%u: not PD-only: flowbit settings other than 'set'", s->id);
return 0;
}
} else if (sm->type == DETECT_FLOW) {
if (((DetectFlowData *)sm->ctx)->flags & ~(DETECT_FLOW_FLAG_TOSERVER|DETECT_FLOW_FLAG_TOCLIENT)) {
SCLogDebug("%u: not PD-only: flow settings other than toserver/toclient", s->id);
return 0;
}
} else if ( !(sigmatch_table[sm->type].flags & SIGMATCH_IPONLY_COMPAT)) {
SCLogDebug("%u: not PD-only: %s not PD/IP-only compat", s->id, sigmatch_table[sm->type].name);
return 0;
}
}
}
if (pd) {
SCLogDebug("PD-ONLY (%" PRIu32 ")", s->id);
}
return pd;
}
/**
* \internal
* \brief Check if the initialized signature is inspecting the packet payload
@ -3283,8 +3380,13 @@ int SigAddressPrepareStage1(DetectEngineCtx *de_ctx)
SCLogDebug("Signature %" PRIu32 ", internal id %" PRIu32 ", ptrs %p %p ", tmp_s->id, tmp_s->num, tmp_s, de_ctx->sig_array[tmp_s->num]);
/* see if the sig is dp only */
if (SignatureIsPDOnly(tmp_s) == 1) {
tmp_s->flags |= SIG_FLAG_PDONLY;
SCLogDebug("Signature %"PRIu32" is considered \"PD only\"", tmp_s->id);
/* see if the sig is ip only */
if (SignatureIsIPOnly(de_ctx, tmp_s) == 1) {
} else if (SignatureIsIPOnly(de_ctx, tmp_s) == 1) {
tmp_s->flags |= SIG_FLAG_IPONLY;
cnt_iponly++;

@ -285,6 +285,10 @@ typedef struct DetectPort_ {
#define SIG_FLAG_PREFILTER (1<<23) /**< sig is part of a prefilter engine */
/** Proto detect only signature.
* Inspected once per direction when protocol detection is done. */
#define SIG_FLAG_PDONLY (1<<24)
/* signature init flags */
#define SIG_FLAG_INIT_DEONLY 1 /**< decode event only signature */
#define SIG_FLAG_INIT_PACKET (1<<1) /**< signature has matches against a packet (as opposed to app layer) */
@ -1198,6 +1202,7 @@ enum {
/* sorted by prefilter priority. Higher in this list means it will be
* picked over ones lower in the list */
DETECT_AL_APP_LAYER_PROTOCOL,
DETECT_ACK,
DETECT_SEQ,
DETECT_WINDOW,
@ -1296,7 +1301,6 @@ enum {
DETECT_FILE_DATA,
DETECT_PKT_DATA,
DETECT_AL_APP_LAYER_EVENT,
DETECT_AL_APP_LAYER_PROTOCOL,
DETECT_DCE_IFACE,
DETECT_DCE_OPNUM,

Loading…
Cancel
Save