diff --git a/src/detect-app-layer-protocol.c b/src/detect-app-layer-protocol.c index ca7fe7b92e..1378b817ed 100644 --- a/src/detect-app-layer-protocol.c +++ b/src/detect-app-layer-protocol.c @@ -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; } diff --git a/src/detect-engine-analyzer.c b/src/detect-engine-analyzer.c index 23775349f9..a2f7f9b7c4 100644 --- a/src/detect-engine-analyzer.c +++ b/src/detect-engine-analyzer.c @@ -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"); diff --git a/src/detect.c b/src/detect.c index bf26a97f81..cd0d226d03 100644 --- a/src/detect.c +++ b/src/detect.c @@ -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++; diff --git a/src/detect.h b/src/detect.h index 2a84d51f0a..823a490b7f 100644 --- a/src/detect.h +++ b/src/detect.h @@ -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,