modbus: set stream depth

Some protocol like modbus requires
a infinite stream depth because session
are kept open and we want to analyze everything.

Since we have a stream reassembly depth per stream,
we can also set a stream reassembly depth per proto.
pull/2319/head
Giuseppe Longo 10 years ago committed by Victor Julien
parent b160c49e9e
commit 9ab1194f68

@ -2050,3 +2050,21 @@ in which two threads have to wait for each other . When using two
threads, the time threads might have to wait for each other will be
taken in account when/during profiling packets. For more information
see :doc:`../performance/packet-profiling`.
Application layers
------------------
Modbus
~~~~~~
According to MODBUS Messaging on TCP/IP Implementation Guide V1.0b, it
is recommended to keep the TCP connection opened with a remote device
and not to open and close it for each MODBUS/TCP transaction.
In that case, it is important to set the stream-depth of the modbus as
unlimited.
::
modbus:
# Stream reassembly size for modbus, default is 0
stream-depth: 0

@ -43,6 +43,7 @@
#include "util-misc.h"
#include "stream.h"
#include "stream-tcp.h"
#include "app-layer-protos.h"
#include "app-layer-parser.h"
@ -51,6 +52,7 @@
#include "app-layer-detect-proto.h"
#include "conf.h"
#include "conf-yaml-loader.h"
#include "decode.h"
SCEnumCharMap modbus_decoder_event_table[ ] = {
@ -163,7 +165,11 @@ typedef struct ModbusHeader_ ModbusHeader;
/* Modbus Default unreplied Modbus requests are considered a flood */
#define MODBUS_CONFIG_DEFAULT_REQUEST_FLOOD 500
/* Modbus default stream reassembly depth */
#define MODBUS_CONFIG_DEFAULT_STREAM_DEPTH 0
static uint32_t request_flood = MODBUS_CONFIG_DEFAULT_REQUEST_FLOOD;
static uint32_t stream_depth = MODBUS_CONFIG_DEFAULT_STREAM_DEPTH;
int ModbusStateGetEventInfo(const char *event_name, int *event_id, AppLayerEventType *event_type) {
*event_id = SCMapEnumNameToValue(event_name, modbus_decoder_event_table);
@ -1463,7 +1469,8 @@ void RegisterModbusParsers(void)
}
}
ConfNode *p = ConfGetNode("app-layer.protocols.modbus.request-flood");
ConfNode *p = NULL;
p = ConfGetNode("app-layer.protocols.modbus.request-flood");
if (p != NULL) {
uint32_t value;
if (ParseSizeStringU32(p->val, &value) < 0) {
@ -1473,6 +1480,17 @@ void RegisterModbusParsers(void)
}
}
SCLogConfig("Modbus request flood protection level: %u", request_flood);
p = ConfGetNode("app-layer.protocols.modbus.stream-depth");
if (p != NULL) {
uint32_t value;
if (ParseSizeStringU32(p->val, &value) < 0) {
SCLogError(SC_ERR_MODBUS_CONFIG, "invalid value for stream-depth %s", p->val);
} else {
stream_depth = value;
}
}
SCLogInfo("Modbus stream depth: %u", stream_depth);
} else {
#ifndef AFLFUZZ_APPLAYER
SCLogConfig("Protocol detection and parser disabled for %s protocol.", proto_name);
@ -1506,6 +1524,8 @@ void RegisterModbusParsers(void)
AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_MODBUS, ModbusStateGetEventInfo);
AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_MODBUS, STREAM_TOSERVER);
AppLayerParserSetStreamDepth(IPPROTO_TCP, ALPROTO_MODBUS, stream_depth);
} else {
SCLogConfig("Parsed disabled for %s protocol. Protocol detection" "still on.", proto_name);
}
@ -3107,6 +3127,111 @@ end:
UTHFreePackets(&p, 1);
return result;
}
/** \test Checks if stream_depth is correct */
static int ModbusParserTest17(void) {
AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
Flow f;
TcpSession ssn;
FAIL_IF(alp_tctx == NULL);
memset(&f, 0, sizeof(f));
memset(&ssn, 0, sizeof(ssn));
FLOW_INITIALIZE(&f);
f.protoctx = (void *)&ssn;
f.proto = IPPROTO_TCP;
StreamTcpInitConfig(TRUE);
FLOWLOCK_WRLOCK(&f);
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
readCoilsReq, sizeof(readCoilsReq));
FAIL_IF(r != 0);
FLOWLOCK_UNLOCK(&f);
FAIL_IF(f.alstate == NULL);
FAIL_IF(((TcpSession *)(f.protoctx))->reassembly_depth != MODBUS_CONFIG_DEFAULT_STREAM_DEPTH);
FLOWLOCK_WRLOCK(&f);
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT,
readCoilsRsp, sizeof(readCoilsRsp));
FAIL_IF(r != 0);
FLOWLOCK_UNLOCK(&f);
FAIL_IF(((TcpSession *)(f.protoctx))->reassembly_depth != MODBUS_CONFIG_DEFAULT_STREAM_DEPTH);
AppLayerParserThreadCtxFree(alp_tctx);
StreamTcpFreeConfig(TRUE);
FLOW_DESTROY(&f);
PASS;
}
/*/ \test Checks if stream depth is correct over 2 TCP packets */
static int ModbusParserTest18(void) {
AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
Flow f;
TcpSession ssn;
uint32_t input_len = sizeof(readCoilsReq), part2_len = 3;
uint8_t *input = readCoilsReq;
FAIL_IF(alp_tctx == NULL);
memset(&f, 0, sizeof(f));
memset(&ssn, 0, sizeof(ssn));
FLOW_INITIALIZE(&f);
f.protoctx = (void *)&ssn;
f.proto = IPPROTO_TCP;
StreamTcpInitConfig(TRUE);
FLOWLOCK_WRLOCK(&f);
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
input, input_len - part2_len);
FAIL_IF(r != 0);
FLOWLOCK_UNLOCK(&f);
FAIL_IF(((TcpSession *)(f.protoctx))->reassembly_depth != MODBUS_CONFIG_DEFAULT_STREAM_DEPTH);
FLOWLOCK_WRLOCK(&f);
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
input, input_len);
FAIL_IF(r != 0);
FLOWLOCK_UNLOCK(&f);
FAIL_IF(((TcpSession *)(f.protoctx))->reassembly_depth != MODBUS_CONFIG_DEFAULT_STREAM_DEPTH);
FAIL_IF(f.alstate == NULL);
input_len = sizeof(readCoilsRsp);
part2_len = 10;
input = readCoilsRsp;
FLOWLOCK_WRLOCK(&f);
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT,
input, input_len - part2_len);
FAIL_IF(r != 0);
FLOWLOCK_UNLOCK(&f);
FAIL_IF(((TcpSession *)(f.protoctx))->reassembly_depth != MODBUS_CONFIG_DEFAULT_STREAM_DEPTH);
FLOWLOCK_WRLOCK(&f);
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT,
input, input_len);
FAIL_IF(r != 0);
FLOWLOCK_UNLOCK(&f);
FAIL_IF(((TcpSession *)(f.protoctx))->reassembly_depth != MODBUS_CONFIG_DEFAULT_STREAM_DEPTH);
AppLayerParserThreadCtxFree(alp_tctx);
StreamTcpFreeConfig(TRUE);
FLOW_DESTROY(&f);
PASS;
}
#endif /* UNITTESTS */
void ModbusParserRegisterTests(void) {
@ -3143,5 +3268,9 @@ void ModbusParserRegisterTests(void) {
ModbusParserTest15);
UtRegisterTest("ModbusParserTest16 - Modbus invalid Write single register request",
ModbusParserTest16);
UtRegisterTest("ModbusParserTest17 - Modbus stream depth",
ModbusParserTest17);
UtRegisterTest("ModbusParserTest18 - Modbus stream depth in 2 TCP packets",
ModbusParserTest18);
#endif /* UNITTESTS */
}

@ -700,6 +700,9 @@ app-layer:
# If the limit is reached, app-layer-event:modbus.flooded; will match.
#request-flood: 500
# Stream reassembly size for modbus, default is 0
stream-depth: 0
enabled: no
detection-ports:
dp: 502

Loading…
Cancel
Save