diff --git a/doc/userguide/upgrade.rst b/doc/userguide/upgrade.rst index 3b41905f18..bff924a84b 100644 --- a/doc/userguide/upgrade.rst +++ b/doc/userguide/upgrade.rst @@ -34,6 +34,16 @@ also check all the new features that have been added but are not covered by this guide. Those features are either not enabled by default or require dedicated new configuration. +Upgrading to 7.0.14 +------------------- + +Other Changes +~~~~~~~~~~~~~ +- ``dnp3`` has reduced the default maximum number of outstanding + transactions from 500 down to 32. A ``max-tx`` parameter has been + added to the ``dnp3`` parser for users that need a larger number of + in-flight transactions. + Upgrading to 7.0.12 ------------------- diff --git a/src/app-layer-dnp3.c b/src/app-layer-dnp3.c index 1a8b3cb630..aab462f314 100644 --- a/src/app-layer-dnp3.c +++ b/src/app-layer-dnp3.c @@ -40,9 +40,6 @@ #include "app-layer-dnp3.h" #include "app-layer-dnp3-objects.h" -/* Default number of unreplied requests to be considered a flood. */ -#define DNP3_DEFAULT_REQ_FLOOD_COUNT 500 - #define DNP3_DEFAULT_PORT "20000" /* Expected values for the start bytes. */ @@ -93,6 +90,14 @@ enum { /* Extract the range code from the object qualifier. */ #define DNP3_OBJ_RANGE(x) (x & 0xf) +/* Default number of unreplied requests to be considered a flood. + * + * DNP3 is a request/response SCADA protocol with typically only 1-2 + * transactions in flight. But set a limit high enough to allow for + * some pipelining but reduce the chance of memory exhaustion + * attacks. */ +static uint64_t dnp3_max_tx = 32; + /* Decoder event map. */ SCEnumCharMap dnp3_decoder_event_table[] = { {"FLOODED", DNP3_DECODER_EVENT_FLOODED}, @@ -514,7 +519,7 @@ static DNP3Transaction *DNP3TxAlloc(DNP3State *dnp3, bool request) TAILQ_INSERT_TAIL(&dnp3->tx_list, tx, next); /* Check for flood state. */ - if (dnp3->unreplied > DNP3_DEFAULT_REQ_FLOOD_COUNT) { + if (dnp3->unreplied > dnp3_max_tx && !dnp3->flooded) { DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_FLOODED); dnp3->flooded = 1; } @@ -1386,7 +1391,7 @@ static void DNP3StateTxFree(void *state, uint64_t tx_id) dnp3->unreplied--; /* Check flood state. */ - if (dnp3->flooded && dnp3->unreplied < DNP3_DEFAULT_REQ_FLOOD_COUNT) { + if (dnp3->flooded && dnp3->unreplied < dnp3_max_tx) { dnp3->flooded = 0; } @@ -1432,8 +1437,7 @@ static int DNP3GetAlstateProgress(void *tx, uint8_t direction) int retval = 0; /* If flooded, "ack" old transactions. */ - if (dnp3->flooded && (dnp3->transaction_max - - dnp3tx->tx_num >= DNP3_DEFAULT_REQ_FLOOD_COUNT)) { + if (dnp3->flooded && (dnp3->transaction_max - dnp3tx->tx_num >= dnp3_max_tx)) { SCLogDebug("flooded: returning tx as done."); SCReturnInt(1); } @@ -1606,8 +1610,13 @@ void RegisterDNP3Parsers(void) AppLayerParserRegisterTxDataFunc(IPPROTO_TCP, ALPROTO_DNP3, DNP3GetTxData); AppLayerParserRegisterStateDataFunc(IPPROTO_TCP, ALPROTO_DNP3, DNP3GetStateData); - } - else { + + /* Parse max-tx configuration. */ + intmax_t value = 0; + if (ConfGetInt("app-layer.protocols.dnp3.max-tx", &value)) { + dnp3_max_tx = (uint64_t)value; + } + } else { SCLogConfig("Parser disabled for protocol %s. " "Protocol detection still on.", proto_name); } @@ -2254,7 +2263,7 @@ static int DNP3ParserTestFlooded(void) FAIL_IF_NOT(tx->done); FAIL_IF_NOT(DNP3GetAlstateProgress(tx, STREAM_TOSERVER)); - for (int i = 0; i < DNP3_DEFAULT_REQ_FLOOD_COUNT - 1; i++) { + for (uint64_t i = 0; i < dnp3_max_tx - 1; i++) { SCMutexLock(&flow.m); FAIL_IF(AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, STREAM_TOSERVER, request, sizeof(request))); diff --git a/suricata.yaml.in b/suricata.yaml.in index eab6ca5005..d76f010bdf 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -1179,6 +1179,7 @@ app-layer: enabled: no detection-ports: dp: 20000 + #max-tx: 32 # SCADA EtherNet/IP and CIP protocol support enip: