app-layer-protocol: improve detection

Add negated matches to match list instead of amatch.

Allow matching on 'failed'.

Introduce per packet flags for proto detection. Flags are used to
only inspect once per direction. Flag packet on PD-failure too.
pull/2359/head
Victor Julien 9 years ago
parent ac2cf526f1
commit 7d7ec78cc3

@ -115,6 +115,38 @@ void AppLayerIncTxCounter(ThreadVars *tv, Flow *f, uint64_t step)
}
}
/* in IDS mode protocol detection is done in reverse order:
* when TCP data is ack'd. We want to flag the correct packet,
* so in this case we set a flag in the flow so that the first
* packet in the correct direction can be tagged.
*
* For IPS things are much simpler, and we don't use the flow
* flag. We just tag the packet directly. */
static inline void FlagPacketFlow(Packet *p, Flow *f, uint8_t dir)
{
if (EngineModeIsIPS()) {
if (dir == STREAM_TOSERVER) {
if (p->flowflags & FLOW_PKT_TOSERVER) {
p->flags |= PKT_PROTO_DETECT_TS_DONE;
} else {
f->flags |= FLOW_PROTO_DETECT_TS_DONE;
}
} else {
if (p->flowflags & FLOW_PKT_TOCLIENT) {
p->flags |= PKT_PROTO_DETECT_TC_DONE;
} else {
f->flags |= FLOW_PROTO_DETECT_TC_DONE;
}
}
} else {
if (dir == STREAM_TOSERVER) {
f->flags |= FLOW_PROTO_DETECT_TS_DONE;
} else {
f->flags |= FLOW_PROTO_DETECT_TC_DONE;
}
}
}
/* See if we're going to have to give up:
*
* If we're getting a lot of data in one direction and the
@ -150,24 +182,21 @@ static void TCPProtoDetectCheckBailConditions(Flow *f, TcpSession *ssn, Packet *
if (ProtoDetectDone(f, ssn, STREAM_TOSERVER) &&
ProtoDetectDone(f, ssn, STREAM_TOCLIENT))
{
DisableAppLayer(f);
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
goto failure;
} else if (FLOW_IS_PM_DONE(f, STREAM_TOSERVER) && FLOW_IS_PP_DONE(f, STREAM_TOSERVER) &&
size_ts > 100000 && size_tc == 0)
{
DisableAppLayer(f);
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
APPLAYER_PROTO_DETECTION_SKIPPED);
goto failure;
} else if (FLOW_IS_PM_DONE(f, STREAM_TOCLIENT) && FLOW_IS_PP_DONE(f, STREAM_TOCLIENT) &&
size_tc > 100000 && size_ts == 0)
{
DisableAppLayer(f);
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
APPLAYER_PROTO_DETECTION_SKIPPED);
goto failure;
/* little data in ts direction, pp done, pm not done (max
* depth not reached), ts direction done, lots of data in
@ -176,10 +205,9 @@ static void TCPProtoDetectCheckBailConditions(Flow *f, TcpSession *ssn, Packet *
FLOW_IS_PP_DONE(f, STREAM_TOSERVER) && !(FLOW_IS_PM_DONE(f, STREAM_TOSERVER)) &&
FLOW_IS_PM_DONE(f, STREAM_TOCLIENT) && FLOW_IS_PP_DONE(f, STREAM_TOCLIENT))
{
DisableAppLayer(f);
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
APPLAYER_PROTO_DETECTION_SKIPPED);
goto failure;
/* little data in tc direction, pp done, pm not done (max
* depth not reached), tc direction done, lots of data in
@ -188,10 +216,9 @@ static void TCPProtoDetectCheckBailConditions(Flow *f, TcpSession *ssn, Packet *
FLOW_IS_PP_DONE(f, STREAM_TOCLIENT) && !(FLOW_IS_PM_DONE(f, STREAM_TOCLIENT)) &&
FLOW_IS_PM_DONE(f, STREAM_TOSERVER) && FLOW_IS_PP_DONE(f, STREAM_TOSERVER))
{
DisableAppLayer(f);
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
APPLAYER_PROTO_DETECTION_SKIPPED);
goto failure;
/* in case of really low TS data (e.g. 4 bytes) we can have
* the PP complete, PM not complete (depth not reached) and
@ -200,11 +227,20 @@ static void TCPProtoDetectCheckBailConditions(Flow *f, TcpSession *ssn, Packet *
FLOW_IS_PP_DONE(f, STREAM_TOSERVER) && !(FLOW_IS_PM_DONE(f, STREAM_TOSERVER)) &&
(!FLOW_IS_PM_DONE(f, STREAM_TOCLIENT) && !FLOW_IS_PP_DONE(f, STREAM_TOCLIENT)))
{
DisableAppLayer(f);
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
APPLAYER_PROTO_DETECTION_SKIPPED);
goto failure;
}
return;
failure:
DisableAppLayer(f);
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
f->alproto = f->alproto_tc = f->alproto_ts = ALPROTO_FAILED;
FlagPacketFlow(p, f, STREAM_TOSERVER);
FlagPacketFlow(p, f, STREAM_TOCLIENT);
return;
}
/** \todo modifying p this way seems too hacky */
@ -315,6 +351,10 @@ static int TCPProtoDetect(ThreadVars *tv,
StreamTcpSetStreamFlagAppProtoDetectionCompleted(stream);
TcpSessionSetReassemblyDepth(ssn,
AppLayerParserGetStreamDepth(f->proto, *alproto));
if (flags & STREAM_TOCLIENT)
FlagPacketFlow(p, f, STREAM_TOCLIENT);
else
FlagPacketFlow(p, f, STREAM_TOSERVER);
/* account flow if we have both sides */
if (*alproto_otherdir != ALPROTO_UNKNOWN) {
@ -469,6 +509,12 @@ static int TCPProtoDetect(ThreadVars *tv,
StreamTcpSetStreamFlagAppProtoDetectionCompleted(stream);
TcpSessionSetReassemblyDepth(ssn,
AppLayerParserGetStreamDepth(f->proto, *alproto));
if (flags & STREAM_TOCLIENT)
FlagPacketFlow(p, f, STREAM_TOCLIENT);
else
FlagPacketFlow(p, f, STREAM_TOSERVER);
if (r < 0)
goto failure;
}

@ -1096,6 +1096,10 @@ int DecoderParseDataFromFile(char *filename, DecoderFunc Decoder);
* flow engine: Packet::flow_hash will be set */
#define PKT_WANTS_FLOW (1<<22)
/** protocol detection done */
#define PKT_PROTO_DETECT_TS_DONE (1<<23)
#define PKT_PROTO_DETECT_TC_DONE (1<<24)
/** \brief return 1 if the packet is a pseudo packet */
#define PKT_IS_PSEUDOPKT(p) ((p)->flags & PKT_PSEUDO_STREAM_END)

@ -33,6 +33,41 @@
static void DetectAppLayerProtocolRegisterTests(void);
static int DetectAppLayerProtocolPacketMatch(ThreadVars *tv,
DetectEngineThreadCtx *det_ctx,
Packet *p, Signature *s, const SigMatchCtx *ctx)
{
SCEnter();
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);
SCReturnInt(0);
}
const Flow *f = p->flow;
if (f == NULL) {
SCLogNotice("packet %u: no flow", (uint)p->pcap_cnt);
SCReturnInt(0);
}
if ((p->flags & PKT_PROTO_DETECT_TS_DONE) && (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);
r = (data->negated) ? (f->alproto_ts != data->alproto) :
(f->alproto_ts == data->alproto);
} else if ((p->flags & PKT_PROTO_DETECT_TC_DONE) && (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);
r = (data->negated) ? (f->alproto_tc != data->alproto) :
(f->alproto_tc == data->alproto);
}
SCReturnInt(r);
}
static int DetectAppLayerProtocolMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
Flow *f, uint8_t flags, void *state,
Signature *s, SigMatch *m)
@ -40,7 +75,7 @@ static int DetectAppLayerProtocolMatch(ThreadVars *t, DetectEngineThreadCtx *det
SCEnter();
int r = 0;
DetectAppLayerProtocolData *data = (DetectAppLayerProtocolData *)m->ctx;
const DetectAppLayerProtocolData *data = (const DetectAppLayerProtocolData *)m->ctx;
r = (data->negated) ? (f->alproto != data->alproto) :
(f->alproto == data->alproto);
@ -72,11 +107,15 @@ static DetectAppLayerProtocolData *DetectAppLayerProtocolParse(const char *arg)
while (*arg != '\0' && isspace((unsigned char)*arg))
arg++;
alproto = AppLayerGetProtoByName((char *)arg);
if (alproto == ALPROTO_UNKNOWN) {
SCLogError(SC_ERR_INVALID_SIGNATURE, "app-layer-protocol "
"keyword supplied with unknown protocol \"%s\"", arg);
return NULL;
if (strcmp(arg, "failed") == 0) {
alproto = ALPROTO_FAILED;
} else {
alproto = AppLayerGetProtoByName((char *)arg);
if (alproto == ALPROTO_UNKNOWN) {
SCLogError(SC_ERR_INVALID_SIGNATURE, "app-layer-protocol "
"keyword supplied with unknown protocol \"%s\"", arg);
return NULL;
}
}
data = SCMalloc(sizeof(DetectAppLayerProtocolData));
@ -116,8 +155,14 @@ static int DetectAppLayerProtocolSetup(DetectEngineCtx *de_ctx,
sm->type = DETECT_AL_APP_LAYER_PROTOCOL;
sm->ctx = (void *)data;
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);
s->flags |= SIG_FLAG_APPLAYER;
if (data->negated || data->alproto == ALPROTO_FAILED) {
SCLogNotice("DETECT_SM_LIST_MATCH");
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
} else {
SCLogNotice("DETECT_SM_LIST_AMATCH");
SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH);
s->flags |= SIG_FLAG_APPLAYER;
}
return 0;
@ -136,7 +181,8 @@ static void DetectAppLayerProtocolFree(void *ptr)
void DetectAppLayerProtocolRegister(void)
{
sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].name = "app-layer-protocol";
sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].Match = NULL;
sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].Match =
DetectAppLayerProtocolPacketMatch;
sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].AppLayerMatch =
DetectAppLayerProtocolMatch;
sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].Setup =

@ -248,6 +248,11 @@ void FlowHandlePacketUpdate(Flow *f, Packet *p)
p->flowflags |= FLOW_PKT_TOSERVER_FIRST;
}
}
/* xfer proto detect ts flag to first packet in ts dir */
if (f->flags & FLOW_PROTO_DETECT_TS_DONE) {
f->flags &= ~FLOW_PROTO_DETECT_TS_DONE;
p->flags |= PKT_PROTO_DETECT_TS_DONE;
}
} else {
f->tosrcpktcnt++;
f->tosrcbytecnt += GET_PKT_LEN(p);
@ -258,6 +263,11 @@ void FlowHandlePacketUpdate(Flow *f, Packet *p)
p->flowflags |= FLOW_PKT_TOCLIENT_FIRST;
}
}
/* xfer proto detect tc flag to first packet in tc dir */
if (f->flags & FLOW_PROTO_DETECT_TC_DONE) {
f->flags &= ~FLOW_PROTO_DETECT_TC_DONE;
p->flags |= PKT_PROTO_DETECT_TC_DONE;
}
}
if ((f->flags & (FLOW_TO_DST_SEEN|FLOW_TO_SRC_SEEN)) == (FLOW_TO_DST_SEEN|FLOW_TO_SRC_SEEN)) {

@ -89,6 +89,9 @@ typedef struct AppLayerParserState_ AppLayerParserState;
/** flow is ipv6 */
#define FLOW_IPV6 BIT_U32(19)
#define FLOW_PROTO_DETECT_TS_DONE BIT_U32(20)
#define FLOW_PROTO_DETECT_TC_DONE BIT_U32(21)
/* File flags */

Loading…
Cancel
Save