From: David DIALLO Date: Wed, 21 Feb 2018 23:29:33 +0000 (+0100) Subject: modbus: Support Unit Identifier X-Git-Tag: suricata-4.1.0-beta1~55 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c2236ea2b31deb683319481cf9312abdaf42764b;p=thirdparty%2Fsuricata.git modbus: Support Unit Identifier When destination IP address does not suffice to uniquely identify the Modbus/TCP device. Some Modbus/TCP devices act as gateways to other Modbus/TCP devices that are behind this gateways. --- diff --git a/doc/userguide/rules/modbus-keyword.rst b/doc/userguide/rules/modbus-keyword.rst index 38a4a26b7b..a338bb42ca 100644 --- a/doc/userguide/rules/modbus-keyword.rst +++ b/doc/userguide/rules/modbus-keyword.rst @@ -4,10 +4,11 @@ Modbus Keyword The modbus keyword can be used for matching on various properties of Modbus requests. -There are two ways of using this keyword: +There are three ways of using this keyword: * matching on functions properties with the setting "function"; -* matching on directly on data access with the setting "access". +* matching on directly on data access with the setting "access"; +* matching on unit identifier with the setting "unit" only or with the previous setting "function" or "access". With the setting **function**, you can match on: @@ -72,6 +73,43 @@ Examples:: modbus: access read discretes, address <100 # Read access at address smaller than 100 of Discretes Input table modbus: access write holding, address 500, value >200 # Write value greather than 200 at address 500 of Holding Registers table +With the setting **unit**, you can match on: + +* a MODBUS slave address of a remote device connected on the sub-network behind a bridge or a gateway. The destination IP address identifies the bridge itself and the bridge uses the MODBUS unit identifier to forward the request to the right slave device. + +Syntax:: + + modbus: unit + modbus: unit , function + modbus: unit , function , subfunction + modbus: unit , function [!] + modbus: unit , access + modbus: unit , access read + modbus: unit , access read , address + modbus: unit , access write < coils | holding> + modbus: unit , access write < coils | holding>, address + modbus: unit , access write < coils | holding>, address , value + +With __ setting matches on the address or value as it is being +accessed or written as follows:: + + unit 10 # exactly unit identifier 10 + unit 10<>20 # greater than unit identifier 10 and smaller than unit identifier 20 + unit >10 # greater than unit identifier 10 + unit <10 # smaller than unit identifier 10 + +Examples:: + + modbus: unit 10 # Unit identifier 10 + modbus: unit 10, function 21 # Unit identifier 10 and write File record function + modbus: unit 10, function 4, subfunction 4 # Unit identifier 10 and force Listen Only Mode (Diagnostics) function + modbus: unit 10, function assigned # Unit identifier 10 and assigned function + modbus: unit 10, function !reserved # Unit identifier 10 and every function but reserved function + modbus: unit 10, access read # Unit identifier 10 and Read access + modbus: unit 10, access write coils # Unit identifier 10 and Write access to Coils table + modbus: unit >10, access read discretes, address <100 # Greater than unit identifier 10 and Read access at address smaller than 100 of Discretes Input table + modbus: unit 10<>20, access write holding, address 500, value >200 # Greater than unit identifier 10 and smaller than unit identifier 20 and Write value greather than 200 at address 500 of Holding Registers table + (cf. http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf) **Note:** Address of read and write are starting at 1. So if your system @@ -83,6 +121,10 @@ remote device and not to open and close it for each MODBUS/TCP transaction. In that case, it is important to set the depth of the stream reassembling as unlimited (stream.reassembly.depth: 0) +**Note:** According to MODBUS Messaging on TCP/IP Implementation Guide +V1.0b, the MODBUS slave device addresses on serial line are assigned from 1 to +247 (decimal). Address 0 is used as broadcast address. + (cf. http://www.modbus.org/docs/Modbus_Messaging_Implementation_Guide_V1_0b.pdf) Paper and presentation (in french) on Modbus support are available : diff --git a/src/app-layer-modbus.c b/src/app-layer-modbus.c index 51ada113f7..b39d527d21 100644 --- a/src/app-layer-modbus.c +++ b/src/app-layer-modbus.c @@ -94,7 +94,6 @@ SCEnumCharMap modbus_decoder_event_table[ ] = { #define MODBUS_MAX_COUNT 250 /* Modbus Function Code. */ -#define MODBUS_FUNC_NONE 0x00 #define MODBUS_FUNC_READCOILS 0x01 #define MODBUS_FUNC_READDISCINPUTS 0x02 #define MODBUS_FUNC_READHOLDREGS 0x03 @@ -1301,7 +1300,8 @@ static int ModbusParseRequest(Flow *f, /* Check MODBUS Header */ ModbusCheckHeader(modbus, &header); - /* Store Transaction ID & PDU length */ + /* Store Unit ID, Transaction ID & PDU length */ + tx->unit_id = header.unitId; tx->transactionId = header.transactionId; tx->length = header.length; @@ -1563,6 +1563,13 @@ void RegisterModbusParsers(void) #include "stream-tcp.h" #include "stream-tcp-private.h" +/* Modbus Application Protocol Specification V1.1b3 6.1: Read Coils */ +static uint8_t invalidFunctionCode[] = {/* Transaction ID */ 0x00, 0x00, + /* Protocol ID */ 0x00, 0x01, + /* Length */ 0x00, 0x02, + /* Unit ID */ 0x00, + /* Function code */ 0x00}; + /* Modbus Application Protocol Specification V1.1b3 6.1: Read Coils */ /* Example of a request to read discrete outputs 20-38 */ static uint8_t readCoilsReq[] = {/* Transaction ID */ 0x00, 0x00, @@ -1746,7 +1753,7 @@ static int ModbusParserTest01(void) { Flow f; TcpSession ssn; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&f, 0, sizeof(f)); memset(&ssn, 0, sizeof(ssn)); @@ -1762,50 +1769,30 @@ static int ModbusParserTest01(void) { int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, readCoilsReq, sizeof(readCoilsReq)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); ModbusTransaction *tx = ModbusGetTx(modbus_state, 0); - if ((tx->function != 1) || (tx->read.address != 0x7890) || (tx->read.quantity != 19)) { - printf("expected function %d, got %" PRIu8 ": ", 1, tx->function); - printf("expected address %d, got %" PRIu16 ": ", 0x7890, tx->read.address); - printf("expected quantity %d, got %" PRIu16 ": ", 19, tx->read.quantity); - goto end; - } + FAIL_IF_NOT(tx->function == 1); + FAIL_IF_NOT(tx->read.address == 0x7890); + FAIL_IF_NOT(tx->read.quantity == 19); FLOWLOCK_WRLOCK(&f); r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT, readCoilsRsp, sizeof(readCoilsRsp)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); - if (modbus_state->transaction_max !=1) { - printf("expected transaction_max %d, got %" PRIu64 ": ", 1, modbus_state->transaction_max); - goto end; - } + FAIL_IF_NOT(modbus_state->transaction_max == 1); - result = 1; -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); + AppLayerParserThreadCtxFree(alp_tctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); - return result; + PASS; } /** \test Send Modbus Write Multiple registers request/response. */ @@ -1814,7 +1801,7 @@ static int ModbusParserTest02(void) { Flow f; TcpSession ssn; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&f, 0, sizeof(f)); memset(&ssn, 0, sizeof(ssn)); @@ -1830,55 +1817,34 @@ static int ModbusParserTest02(void) { int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, writeMultipleRegistersReq, sizeof(writeMultipleRegistersReq)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); ModbusTransaction *tx = ModbusGetTx(modbus_state, 0); - if ((tx->function != 16) || (tx->write.address != 0x01) || (tx->write.quantity != 2) || - (tx->write.count != 4) || (tx->data[0] != 0x000A) || (tx->data[1] != 0x0102)) { - printf("expected function %d, got %" PRIu8 ": ", 16, tx->function); - printf("expected write address %d, got %" PRIu16 ": ", 0x01, tx->write.address); - printf("expected write quantity %d, got %" PRIu16 ": ", 2, tx->write.quantity); - printf("expected write count %d, got %" PRIu8 ": ", 4, tx->write.count); - printf("expected data %d, got %" PRIu16 ": ", 0x000A, tx->data[0]); - printf("expected data %d, got %" PRIu16 ": ", 0x0102, tx->data[1]); - goto end; - } + FAIL_IF_NOT(tx->function == 16); + FAIL_IF_NOT(tx->write.address == 0x01); + FAIL_IF_NOT(tx->write.quantity == 2); + FAIL_IF_NOT(tx->write.count == 4); + FAIL_IF_NOT(tx->data[0] == 0x000A); + FAIL_IF_NOT(tx->data[1] == 0x0102); FLOWLOCK_WRLOCK(&f); r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT, writeMultipleRegistersRsp, sizeof(writeMultipleRegistersRsp)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); - if (modbus_state->transaction_max !=1) { - printf("expected transaction_max %d, got %" PRIu64 ": ", 1, modbus_state->transaction_max); - goto end; - } + FAIL_IF_NOT(modbus_state->transaction_max == 1); - result = 1; -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); + AppLayerParserThreadCtxFree(alp_tctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); - return result; + PASS; } /** \test Send Modbus Read/Write Multiple registers request/response with mismatch value. */ @@ -1891,7 +1857,7 @@ static int ModbusParserTest03(void) { TcpSession ssn; ThreadVars tv; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&tv, 0, sizeof(ThreadVars)); memset(&f, 0, sizeof(Flow)); @@ -1913,8 +1879,7 @@ static int ModbusParserTest03(void) { StreamTcpInitConfig(TRUE); DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " @@ -1922,8 +1887,7 @@ static int ModbusParserTest03(void) { "app-layer-event: " "modbus.value_mismatch; " "sid:1;)"); - if (s == NULL) - goto end; + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); @@ -1933,74 +1897,49 @@ static int ModbusParserTest03(void) { STREAM_TOSERVER, readWriteMultipleRegistersReq, sizeof(readWriteMultipleRegistersReq)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); ModbusTransaction *tx = ModbusGetTx(modbus_state, 0); - if ((tx->function != 23) || (tx->read.address != 0x03) || (tx->read.quantity != 6) || - (tx->write.address != 0x0E) || (tx->write.quantity != 3) || (tx->write.count != 6) || - (tx->data[0] != 0x1234) || (tx->data[1] != 0x5678) || (tx->data[2] != 0x9ABC)) { - printf("expected function %d, got %" PRIu8 ": ", 23, tx->function); - printf("expected read address %d, got %" PRIu16 ": ", 0x03, tx->read.address); - printf("expected read quantity %d, got %" PRIu16 ": ", 6, tx->read.quantity); - printf("expected write address %d, got %" PRIu16 ": ", 0x0E, tx->write.address); - printf("expected write quantity %d, got %" PRIu16 ": ", 3, tx->write.quantity); - printf("expected write count %d, got %" PRIu8 ": ", 6, tx->write.count); - printf("expected data %d, got %" PRIu16 ": ", 0x1234, tx->data[0]); - printf("expected data %d, got %" PRIu16 ": ", 0x5678, tx->data[1]); - printf("expected data %d, got %" PRIu16 ": ", 0x9ABC, tx->data[2]); - goto end; - } + FAIL_IF_NOT(tx->function == 23); + FAIL_IF_NOT(tx->read.address == 0x03); + FAIL_IF_NOT(tx->read.quantity == 6); + FAIL_IF_NOT(tx->write.address == 0x0E); + FAIL_IF_NOT(tx->write.quantity == 3); + FAIL_IF_NOT(tx->write.count == 6); + FAIL_IF_NOT(tx->data[0] == 0x1234); + FAIL_IF_NOT(tx->data[1] == 0x5678); + FAIL_IF_NOT(tx->data[2] == 0x9ABC); FLOWLOCK_WRLOCK(&f); r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT, readWriteMultipleRegistersRsp, sizeof(readWriteMultipleRegistersRsp)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); - if (modbus_state->transaction_max !=1) { - printf("expected transaction_max %d, got %" PRIu64 ": ", 1, modbus_state->transaction_max); - goto end; - } + FAIL_IF_NOT(modbus_state->transaction_max == 1); /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); - if (!PacketAlertCheck(p, 1)) { - printf("sid 1 didn't match. Should have matched: "); - goto end; - } + FAIL_IF_NOT(PacketAlertCheck(p, 1)); - result = 1; -end: SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx); DetectEngineCtxFree(de_ctx); - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); + AppLayerParserThreadCtxFree(alp_tctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); UTHFreePackets(&p, 1); - return result; + PASS; } /** \test Send Modbus Force Listen Only Mode request. */ @@ -2009,7 +1948,7 @@ static int ModbusParserTest04(void) { Flow f; TcpSession ssn; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&f, 0, sizeof(f)); memset(&ssn, 0, sizeof(ssn)); @@ -2025,34 +1964,21 @@ static int ModbusParserTest04(void) { int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, forceListenOnlyMode, sizeof(forceListenOnlyMode)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); ModbusTransaction *tx = ModbusGetTx(modbus_state, 0); - if ((tx->function != 8) || (tx->subFunction != 4)) { - printf("expected function %d, got %" PRIu8 ": ", 8, tx->function); - printf("expected sub-function %d, got %" PRIu16 ": ", 0x04, tx->subFunction); - goto end; - } + FAIL_IF_NOT(tx->function == 8); + FAIL_IF_NOT(tx->subFunction == 4); - result = 1; -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); + AppLayerParserThreadCtxFree(alp_tctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); - return result; + PASS; } /** \test Send Modbus invalid Protocol version in request. */ @@ -2065,7 +1991,7 @@ static int ModbusParserTest05(void) { TcpSession ssn; ThreadVars tv; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&tv, 0, sizeof(ThreadVars)); memset(&f, 0, sizeof(Flow)); @@ -2087,8 +2013,7 @@ static int ModbusParserTest05(void) { StreamTcpInitConfig(TRUE); DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " @@ -2096,8 +2021,7 @@ static int ModbusParserTest05(void) { "app-layer-event: " "modbus.invalid_protocol_id; " "sid:1;)"); - if (s == NULL) - goto end; + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); @@ -2106,41 +2030,28 @@ static int ModbusParserTest05(void) { int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, invalidProtocolIdReq, sizeof(invalidProtocolIdReq)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); - if (!PacketAlertCheck(p, 1)) { - printf("sid 1 didn't match. Should have matched: "); - goto end; - } + FAIL_IF_NOT(PacketAlertCheck(p, 1)); - result = 1; -end: SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx); DetectEngineCtxFree(de_ctx); - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); + AppLayerParserThreadCtxFree(alp_tctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); UTHFreePackets(&p, 1); - return result; + PASS; } /** \test Send Modbus unsolicited response. */ @@ -2153,7 +2064,7 @@ static int ModbusParserTest06(void) { TcpSession ssn; ThreadVars tv; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&tv, 0, sizeof(ThreadVars)); memset(&f, 0, sizeof(Flow)); @@ -2175,8 +2086,7 @@ static int ModbusParserTest06(void) { StreamTcpInitConfig(TRUE); DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " @@ -2184,8 +2094,7 @@ static int ModbusParserTest06(void) { "app-layer-event: " "modbus.unsolicited_response; " "sid:1;)"); - if (s == NULL) - goto end; + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); @@ -2194,41 +2103,28 @@ static int ModbusParserTest06(void) { int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT, readCoilsRsp, sizeof(readCoilsRsp)); - if (r != 0) { - printf("toclient chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); - if (!PacketAlertCheck(p, 1)) { - printf("sid 1 didn't match. Should have matched: "); - goto end; - } + FAIL_IF_NOT(PacketAlertCheck(p, 1)); - result = 1; -end: SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx); DetectEngineCtxFree(de_ctx); - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); + AppLayerParserThreadCtxFree(alp_tctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); UTHFreePackets(&p, 1); - return result; + PASS; } /** \test Send Modbus invalid Length request. */ @@ -2241,7 +2137,7 @@ static int ModbusParserTest07(void) { TcpSession ssn; ThreadVars tv; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&tv, 0, sizeof(ThreadVars)); memset(&f, 0, sizeof(Flow)); @@ -2263,8 +2159,7 @@ static int ModbusParserTest07(void) { StreamTcpInitConfig(TRUE); DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " @@ -2272,8 +2167,7 @@ static int ModbusParserTest07(void) { "app-layer-event: " "modbus.invalid_length; " "sid:1;)"); - if (s == NULL) - goto end; + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); @@ -2283,41 +2177,28 @@ static int ModbusParserTest07(void) { STREAM_TOSERVER, invalidLengthWriteMultipleRegistersReq, sizeof(invalidLengthWriteMultipleRegistersReq)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); - if (!PacketAlertCheck(p, 1)) { - printf("sid 1 didn't match. Should have matched: "); - goto end; - } + FAIL_IF_NOT(PacketAlertCheck(p, 1)); - result = 1; -end: SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx); DetectEngineCtxFree(de_ctx); - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); + AppLayerParserThreadCtxFree(alp_tctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); UTHFreePackets(&p, 1); - return result; + PASS; } /** \test Send Modbus Read Coils request and error response with Exception code invalid. */ @@ -2330,7 +2211,7 @@ static int ModbusParserTest08(void) { TcpSession ssn; ThreadVars tv; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&tv, 0, sizeof(ThreadVars)); memset(&f, 0, sizeof(Flow)); @@ -2352,8 +2233,7 @@ static int ModbusParserTest08(void) { StreamTcpInitConfig(TRUE); DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " @@ -2361,8 +2241,7 @@ static int ModbusParserTest08(void) { "app-layer-event: " "modbus.invalid_exception_code; " "sid:1;)"); - if (s == NULL) - goto end; + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); @@ -2371,66 +2250,43 @@ static int ModbusParserTest08(void) { int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, readCoilsReq, sizeof(readCoilsReq)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); ModbusTransaction *tx = ModbusGetTx(modbus_state, 0); - if ((tx->function != 1) || (tx->read.address != 0x7890) || (tx->read.quantity != 19)) { - printf("expected function %d, got %" PRIu8 ": ", 1, tx->function); - printf("expected address %d, got %" PRIu16 ": ", 0x7890, tx->read.address); - printf("expected quantity %d, got %" PRIu16 ": ", 19, tx->read.quantity); - goto end; - } + FAIL_IF_NOT(tx->function == 1); + FAIL_IF_NOT(tx->read.address == 0x7890); + FAIL_IF_NOT(tx->read.quantity == 19); FLOWLOCK_WRLOCK(&f); r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT, readCoilsErrorRsp, sizeof(readCoilsErrorRsp)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); - if (modbus_state->transaction_max !=1) { - printf("expected transaction_max %d, got %" PRIu64 ": ", 1, modbus_state->transaction_max); - goto end; - } + FAIL_IF_NOT(modbus_state->transaction_max == 1); /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); - if (!PacketAlertCheck(p, 1)) { - printf("sid 1 didn't match. Should have matched: "); - goto end; - } + FAIL_IF_NOT(PacketAlertCheck(p, 1)); - result = 1; -end: SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx); DetectEngineCtxFree(de_ctx); - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); + AppLayerParserThreadCtxFree(alp_tctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); UTHFreePackets(&p, 1); - return result; + PASS; } /** \test Modbus fragmentation - 1 ADU over 2 TCP packets. */ @@ -2442,7 +2298,7 @@ static int ModbusParserTest09(void) { uint32_t input_len = sizeof(readCoilsReq), part2_len = 3; uint8_t *input = readCoilsReq; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&f, 0, sizeof(f)); memset(&ssn, 0, sizeof(ssn)); @@ -2457,35 +2313,21 @@ static int ModbusParserTest09(void) { FLOWLOCK_WRLOCK(&f); int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, input, input_len - part2_len); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, input, input_len); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); ModbusTransaction *tx = ModbusGetTx(modbus_state, 0); - if ((tx->function != 1) || (tx->read.address != 0x7890) || (tx->read.quantity != 19)) { - printf("expected function %d, got %" PRIu8 ": ", 1, tx->function); - printf("expected address %d, got %" PRIu16 ": ", 0x7890, tx->read.address); - printf("expected quantity %d, got %" PRIu16 ": ", 19, tx->read.quantity); - goto end; - } + FAIL_IF_NOT(tx->function == 1); + FAIL_IF_NOT(tx->read.address == 0x7890); + FAIL_IF_NOT(tx->read.quantity == 19); input_len = sizeof(readCoilsRsp); part2_len = 10; @@ -2494,33 +2336,19 @@ static int ModbusParserTest09(void) { FLOWLOCK_WRLOCK(&f); r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT, input, input_len - part2_len); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT, input, input_len); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); - if (modbus_state->transaction_max !=1) { - printf("expected transaction_max %d, got %" PRIu64 ": ", 1, modbus_state->transaction_max); - goto end; - } + FAIL_IF_NOT(modbus_state->transaction_max ==1); - result = 1; -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); + AppLayerParserThreadCtxFree(alp_tctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); - return result; + PASS; } /** \test Modbus fragmentation - 2 ADU in 1 TCP packet. */ @@ -2532,11 +2360,10 @@ static int ModbusParserTest10(void) { Flow f; TcpSession ssn; - int result = 0; + FAIL_IF_NULL(alp_tctx); input = (uint8_t *) SCMalloc (input_len * sizeof(uint8_t)); - if (unlikely(input == NULL)) - goto end; + FAIL_IF_NULL(input); memcpy(input, readCoilsReq, sizeof(readCoilsReq)); memcpy(input + sizeof(readCoilsReq), writeMultipleRegistersReq, sizeof(writeMultipleRegistersReq)); @@ -2554,42 +2381,27 @@ static int ModbusParserTest10(void) { FLOWLOCK_WRLOCK(&f); int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, input, input_len); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); - if (modbus_state->transaction_max !=2) { - printf("expected transaction_max %d, got %" PRIu64 ": ", 2, modbus_state->transaction_max); - goto end; - } + FAIL_IF_NOT(modbus_state->transaction_max == 2); ModbusTransaction *tx = ModbusGetTx(modbus_state, 1); - if ((tx->function != 16) || (tx->write.address != 0x01) || (tx->write.quantity != 2) || - (tx->write.count != 4) || (tx->data[0] != 0x000A) || (tx->data[1] != 0x0102)) { - printf("expected function %d, got %" PRIu8 ": ", 16, tx->function); - printf("expected write address %d, got %" PRIu16 ": ", 0x01, tx->write.address); - printf("expected write quantity %d, got %" PRIu16 ": ", 2, tx->write.quantity); - printf("expected write count %d, got %" PRIu8 ": ", 4, tx->write.count); - printf("expected data %d, got %" PRIu16 ": ", 0x000A, tx->data[0]); - printf("expected data %d, got %" PRIu16 ": ", 0x0102, tx->data[1]); - goto end; - } + FAIL_IF_NOT(tx->function == 16); + FAIL_IF_NOT(tx->write.address == 0x01); + FAIL_IF_NOT(tx->write.quantity == 2); + FAIL_IF_NOT(tx->write.count == 4); + FAIL_IF_NOT(tx->data[0] == 0x000A); + FAIL_IF_NOT(tx->data[1] == 0x0102); input_len = sizeof(readCoilsRsp) + sizeof(writeMultipleRegistersRsp); ptr = (uint8_t *) SCRealloc (input, input_len * sizeof(uint8_t)); - if (unlikely(ptr == NULL)) - goto end; + FAIL_IF_NULL(ptr); input = ptr; memcpy(input, readCoilsRsp, sizeof(readCoilsRsp)); @@ -2598,22 +2410,14 @@ static int ModbusParserTest10(void) { FLOWLOCK_WRLOCK(&f); r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT, input, sizeof(input_len)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); - result = 1; -end: - if (input != NULL) - SCFree(input); - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); + SCFree(input); + AppLayerParserThreadCtxFree(alp_tctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); - return result; + PASS; } /** \test Send Modbus exceed Length request. */ @@ -2626,7 +2430,7 @@ static int ModbusParserTest11(void) { TcpSession ssn; ThreadVars tv; - int result = 0; + FAIL_IF(alp_tctx == NULL); memset(&tv, 0, sizeof(ThreadVars)); memset(&f, 0, sizeof(Flow)); @@ -2648,8 +2452,7 @@ static int ModbusParserTest11(void) { StreamTcpInitConfig(TRUE); DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " @@ -2657,8 +2460,7 @@ static int ModbusParserTest11(void) { "app-layer-event: " "modbus.invalid_length; " "sid:1;)"); - if (s == NULL) - goto end; + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); @@ -2668,41 +2470,28 @@ static int ModbusParserTest11(void) { STREAM_TOSERVER, exceededLengthWriteMultipleRegistersReq, sizeof(exceededLengthWriteMultipleRegistersReq) + 65523 * sizeof(uint8_t)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); - if (!PacketAlertCheck(p, 1)) { - printf("sid 1 didn't match. Should have matched: "); - goto end; - } + FAIL_IF_NOT(PacketAlertCheck(p, 1)); - result = 1; -end: SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx); DetectEngineCtxFree(de_ctx); - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); + AppLayerParserThreadCtxFree(alp_tctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); UTHFreePackets(&p, 1); - return result; + PASS; } /** \test Send Modbus invalid PDU Length. */ @@ -2715,7 +2504,7 @@ static int ModbusParserTest12(void) { TcpSession ssn; ThreadVars tv; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&tv, 0, sizeof(ThreadVars)); memset(&f, 0, sizeof(Flow)); @@ -2737,8 +2526,7 @@ static int ModbusParserTest12(void) { StreamTcpInitConfig(TRUE); DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " @@ -2746,8 +2534,7 @@ static int ModbusParserTest12(void) { "app-layer-event: " "modbus.invalid_length; " "sid:1;)"); - if (s == NULL) - goto end; + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); @@ -2757,41 +2544,28 @@ static int ModbusParserTest12(void) { STREAM_TOSERVER, invalidLengthPDUWriteMultipleRegistersReq, sizeof(invalidLengthPDUWriteMultipleRegistersReq)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); - if (!PacketAlertCheck(p, 1)) { - printf("sid 1 didn't match. Should have matched: "); - goto end; - } + FAIL_IF_NOT(PacketAlertCheck(p, 1)); - result = 1; -end: SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx); DetectEngineCtxFree(de_ctx); - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); + AppLayerParserThreadCtxFree(alp_tctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); UTHFreePackets(&p, 1); - return result; + PASS; } /** \test Send Modbus Mask Write register request/response. */ @@ -2800,7 +2574,7 @@ static int ModbusParserTest13(void) { Flow f; TcpSession ssn; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&f, 0, sizeof(f)); memset(&ssn, 0, sizeof(ssn)); @@ -2816,51 +2590,31 @@ static int ModbusParserTest13(void) { int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, maskWriteRegisterReq, sizeof(maskWriteRegisterReq)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); ModbusTransaction *tx = ModbusGetTx(modbus_state, 0); - if ((tx->function != 22) || (tx->data[0] != 0x00F2) || (tx->data[1] != 0x0025)) { - printf("expected function %d, got %" PRIu8 ": ", 16, tx->function); - printf("expected And_Mask %d, got %" PRIu16 ": ", 0x00F2, tx->data[0]); - printf("expected Or_Mask %d, got %" PRIu16 ": ", 0x0025, tx->data[1]); - goto end; - } + FAIL_IF_NOT(tx->function == 22); + FAIL_IF_NOT(tx->data[0] == 0x00F2); + FAIL_IF_NOT(tx->data[1] == 0x0025); FLOWLOCK_WRLOCK(&f); r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT, maskWriteRegisterRsp, sizeof(maskWriteRegisterRsp)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); - if (modbus_state->transaction_max !=1) { - printf("expected transaction_max %d, got %" PRIu64 ": ", 1, modbus_state->transaction_max); - goto end; - } + FAIL_IF_NOT(modbus_state->transaction_max == 1); - result = 1; -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); + AppLayerParserThreadCtxFree(alp_tctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); - return result; + PASS; } /** \test Send Modbus Write single register request/response. */ @@ -2869,7 +2623,7 @@ static int ModbusParserTest14(void) { Flow f; TcpSession ssn; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&f, 0, sizeof(f)); memset(&ssn, 0, sizeof(ssn)); @@ -2885,51 +2639,31 @@ static int ModbusParserTest14(void) { int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, writeSingleRegisterReq, sizeof(writeSingleRegisterReq)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); ModbusTransaction *tx = ModbusGetTx(modbus_state, 0); - if ((tx->function != 6) || (tx->write.address != 0x0001) || (tx->data[0] != 0x0003)) { - printf("expected function %d, got %" PRIu8 ": ", 16, tx->function); - printf("expected write address %d, got %" PRIu16 ": ", 0x01, tx->write.address); - printf("expected data %d, got %" PRIu16 ": ", 0x03, tx->data[0]); - goto end; - } + FAIL_IF_NOT(tx->function == 6); + FAIL_IF_NOT(tx->write.address == 0x0001); + FAIL_IF_NOT(tx->data[0] == 0x0003); FLOWLOCK_WRLOCK(&f); r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT, writeSingleRegisterRsp, sizeof(writeSingleRegisterRsp)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); - if (modbus_state->transaction_max !=1) { - printf("expected transaction_max %d, got %" PRIu64 ": ", 1, modbus_state->transaction_max); - goto end; - } + FAIL_IF_NOT(modbus_state->transaction_max == 1); - result = 1; -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); + AppLayerParserThreadCtxFree(alp_tctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); - return result; + PASS; } /** \test Send invalid Modbus Mask Write register request. */ @@ -2942,7 +2676,7 @@ static int ModbusParserTest15(void) { TcpSession ssn; ThreadVars tv; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&tv, 0, sizeof(ThreadVars)); memset(&f, 0, sizeof(f)); @@ -2964,8 +2698,7 @@ static int ModbusParserTest15(void) { StreamTcpInitConfig(TRUE); DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " @@ -2973,8 +2706,7 @@ static int ModbusParserTest15(void) { "app-layer-event: " "modbus.invalid_length; " "sid:1;)"); - if (s == NULL) - goto end; + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); @@ -2983,64 +2715,41 @@ static int ModbusParserTest15(void) { int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, invalidMaskWriteRegisterReq, sizeof(invalidMaskWriteRegisterReq)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); ModbusTransaction *tx = ModbusGetTx(modbus_state, 0); - if (tx->function != 22) { - printf("expected function %d, got %" PRIu8 ": ", 16, tx->function); - goto end; - } + FAIL_IF_NOT(tx->function == 22); /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); - if (!PacketAlertCheck(p, 1)) { - printf("sid 1 didn't match. Should have matched: "); - goto end; - } + FAIL_IF_NOT(PacketAlertCheck(p, 1)); FLOWLOCK_WRLOCK(&f); r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT, maskWriteRegisterRsp, sizeof(maskWriteRegisterRsp)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); - if (modbus_state->transaction_max !=1) { - printf("expected transaction_max %d, got %" PRIu64 ": ", 1, modbus_state->transaction_max); - goto end; - } + FAIL_IF_NOT(modbus_state->transaction_max == 1); - result = 1; -end: SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx); DetectEngineCtxFree(de_ctx); - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); + AppLayerParserThreadCtxFree(alp_tctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); UTHFreePackets(&p, 1); - return result; + PASS; } /** \test Send invalid Modbus Mask Write register request. */ @@ -3053,7 +2762,7 @@ static int ModbusParserTest16(void) { TcpSession ssn; ThreadVars tv; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&tv, 0, sizeof(ThreadVars)); memset(&f, 0, sizeof(f)); @@ -3075,8 +2784,7 @@ static int ModbusParserTest16(void) { StreamTcpInitConfig(TRUE); DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " @@ -3084,8 +2792,7 @@ static int ModbusParserTest16(void) { "app-layer-event: " "modbus.invalid_length; " "sid:1;)"); - if (s == NULL) - goto end; + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); @@ -3095,66 +2802,42 @@ static int ModbusParserTest16(void) { STREAM_TOSERVER, invalidWriteSingleRegisterReq, sizeof(invalidWriteSingleRegisterReq)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); ModbusTransaction *tx = ModbusGetTx(modbus_state, 0); - if ((tx->function != 6) || (tx->write.address != 0x0001)) { - printf("expected function %d, got %" PRIu8 ": ", 16, tx->function); - printf("expected write address %d, got %" PRIu16 ": ", 0x01, tx->write.address); - goto end; - } + FAIL_IF_NOT(tx->function == 6); + FAIL_IF_NOT(tx->write.address == 0x0001); /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); - if (!PacketAlertCheck(p, 1)) { - printf("sid 1 didn't match. Should have matched: "); - goto end; - } + FAIL_IF_NOT(PacketAlertCheck(p, 1)); FLOWLOCK_WRLOCK(&f); r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT, writeSingleRegisterRsp, sizeof(writeSingleRegisterRsp)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); - if (modbus_state->transaction_max !=1) { - printf("expected transaction_max %d, got %" PRIu64 ": ", 1, modbus_state->transaction_max); - goto end; - } + FAIL_IF_NOT(modbus_state->transaction_max == 1); - result = 1; -end: SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx); DetectEngineCtxFree(de_ctx); - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); + AppLayerParserThreadCtxFree(alp_tctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); UTHFreePackets(&p, 1); - return result; -} + PASS;} /** \test Checks if stream_depth is correct */ static int ModbusParserTest17(void) { @@ -3162,7 +2845,7 @@ static int ModbusParserTest17(void) { Flow f; TcpSession ssn; - FAIL_IF(alp_tctx == NULL); + FAIL_IF_NULL(alp_tctx); memset(&f, 0, sizeof(f)); memset(&ssn, 0, sizeof(ssn)); @@ -3207,7 +2890,7 @@ static int ModbusParserTest18(void) { uint32_t input_len = sizeof(readCoilsReq), part2_len = 3; uint8_t *input = readCoilsReq; - FAIL_IF(alp_tctx == NULL); + FAIL_IF_NULL(alp_tctx); memset(&f, 0, sizeof(f)); memset(&ssn, 0, sizeof(ssn)); @@ -3262,6 +2945,80 @@ static int ModbusParserTest18(void) { FLOW_DESTROY(&f); PASS; } + +/** \test Send Modbus invalid function. */ +static int ModbusParserTest19(void) { + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + DetectEngineThreadCtx *det_ctx = NULL; + Flow f; + Packet *p = NULL; + Signature *s = NULL; + TcpSession ssn; + ThreadVars tv; + + FAIL_IF_NULL(alp_tctx); + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&f, 0, sizeof(Flow)); + memset(&ssn, 0, sizeof(TcpSession)); + + p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + + FLOW_INITIALIZE(&f); + f.alproto = ALPROTO_MODBUS; + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.alproto = ALPROTO_MODBUS; + f.flags |= FLOW_IPV4; + + p->flow = &f; + p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; + + StreamTcpInitConfig(TRUE); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + + de_ctx->flags |= DE_QUIET; + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Modbus invalid Function code\"; " + "app-layer-event: " + "modbus.invalid_function_code; " + "sid:1;)"); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); + + FLOWLOCK_WRLOCK(&f); + int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, + STREAM_TOSERVER, + invalidFunctionCode, + sizeof(invalidFunctionCode)); + FAIL_IF_NOT(r == 0); + FLOWLOCK_UNLOCK(&f); + + ModbusState *modbus_state = f.alstate; + FAIL_IF_NULL(modbus_state); + + /* do detect */ + SigMatchSignatures(&tv, de_ctx, det_ctx, p); + + FAIL_IF_NOT(PacketAlertCheck(p, 1)); + + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + + DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx); + DetectEngineCtxFree(de_ctx); + + AppLayerParserThreadCtxFree(alp_tctx); + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePackets(&p, 1); + PASS; +} #endif /* UNITTESTS */ void ModbusParserRegisterTests(void) { @@ -3302,5 +3059,7 @@ void ModbusParserRegisterTests(void) { ModbusParserTest17); UtRegisterTest("ModbusParserTest18 - Modbus stream depth in 2 TCP packets", ModbusParserTest18); + UtRegisterTest("ModbusParserTest19 - Modbus invalid Function code", + ModbusParserTest19); #endif /* UNITTESTS */ } diff --git a/src/app-layer-modbus.h b/src/app-layer-modbus.h index a8545dfbdc..61b66e2a3f 100644 --- a/src/app-layer-modbus.h +++ b/src/app-layer-modbus.h @@ -78,6 +78,9 @@ enum { #define MODBUS_TYP_WRITE_MULTIPLE (MODBUS_TYP_WRITE | MODBUS_TYP_MULTIPLE) #define MODBUS_TYP_READ_WRITE_MULTIPLE (MODBUS_TYP_READ | MODBUS_TYP_WRITE | MODBUS_TYP_MULTIPLE) +/* Modbus Function Code. */ +#define MODBUS_FUNC_NONE 0x00 + /* Modbus Transaction Structure, request/response. */ typedef struct ModbusTransaction_ { struct ModbusState_ *modbus; @@ -86,6 +89,7 @@ typedef struct ModbusTransaction_ { uint32_t logged; /**< flags indicating which loggers have logged */ uint16_t transactionId; uint16_t length; + uint8_t unit_id; uint8_t function; uint8_t category; uint8_t type; diff --git a/src/detect-engine-modbus.c b/src/detect-engine-modbus.c index 9e1f8537c4..dc6dbe9bc0 100644 --- a/src/detect-engine-modbus.c +++ b/src/detect-engine-modbus.c @@ -218,16 +218,28 @@ int DetectEngineInspectModbus(ThreadVars *tv, SCReturnInt(0); } + if (modbus->unit_id != NULL) { + if (DetectEngineInspectModbusValueMatch(modbus->unit_id, tx->unit_id, 0) == 0) { + SCReturnInt(0); + } else { + ret = 1; + } + } + if (modbus->type == MODBUS_TYP_NONE) { if (modbus->category == MODBUS_CAT_NONE) { - if (modbus->function == tx->function) { - if (modbus->subfunction != NULL) { - SCLogDebug("looking for Modbus server function %d and subfunction %d", - modbus->function, *(modbus->subfunction)); - ret = (*(modbus->subfunction) == (tx->subFunction))? 1 : 0; + if (modbus->function != MODBUS_FUNC_NONE) { + if (modbus->function == tx->function) { + if (modbus->subfunction != NULL) { + SCLogDebug("looking for Modbus server function %d and subfunction %d", + modbus->function, *(modbus->subfunction)); + ret = (*(modbus->subfunction) == (tx->subFunction))? 1 : 0; + } else { + SCLogDebug("looking for Modbus server function %d", modbus->function); + ret = 1; + } } else { - SCLogDebug("looking for Modbus server function %d", modbus->function); - ret = 1; + ret = 0; } } } else { @@ -238,16 +250,20 @@ int DetectEngineInspectModbus(ThreadVars *tv, uint8_t access = modbus->type & MODBUS_TYP_ACCESS_MASK; uint8_t function = modbus->type & MODBUS_TYP_ACCESS_FUNCTION_MASK; - if ((access & tx->type) && ((function == MODBUS_TYP_NONE) || (function & tx->type))) { - if (modbus->address != NULL) { - ret = DetectEngineInspectModbusAddress(tx, modbus->address, access); + if (access != MODBUS_TYP_NONE) { + if ((access & tx->type) && ((function == MODBUS_TYP_NONE) || (function & tx->type))) { + if (modbus->address != NULL) { + ret = DetectEngineInspectModbusAddress(tx, modbus->address, access); - if (ret && (modbus->data != NULL)) { - ret = DetectEngineInspectModbusData(tx, modbus->address->min, modbus->data); + if (ret && (modbus->data != NULL)) { + ret = DetectEngineInspectModbusData(tx, modbus->address->min, modbus->data); + } + } else { + SCLogDebug("looking for Modbus access type %d and function type %d", access, function); + ret = 1; } } else { - SCLogDebug("looking for Modbus access type %d and function type %d", access, function); - ret = 1; + ret = 0; } } } @@ -274,7 +290,7 @@ int DetectEngineInspectModbus(ThreadVars *tv, static uint8_t readCoilsReq[] = {/* Transaction ID */ 0x00, 0x00, /* Protocol ID */ 0x00, 0x00, /* Length */ 0x00, 0x06, - /* Unit ID */ 0x00, + /* Unit ID */ 0x0a, /* Function code */ 0x01, /* Starting Address */ 0x78, 0x90, /* Quantity of coils */ 0x00, 0x13 }; @@ -295,7 +311,7 @@ static uint8_t readInputsRegistersReq[] = {/* Transaction ID */ 0x00, 0 static uint8_t readWriteMultipleRegistersReq[] = {/* Transaction ID */ 0x12, 0x34, /* Protocol ID */ 0x00, 0x00, /* Length */ 0x00, 0x11, - /* Unit ID */ 0x00, + /* Unit ID */ 0x0a, /* Function code */ 0x17, /* Read Starting Address */ 0x00, 0x03, /* Quantity to Read */ 0x00, 0x06, @@ -345,7 +361,7 @@ static int DetectEngineInspectModbusTest01(void) TcpSession ssn; ThreadVars tv; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&tv, 0, sizeof(ThreadVars)); memset(&f, 0, sizeof(Flow)); @@ -366,16 +382,13 @@ static int DetectEngineInspectModbusTest01(void) StreamTcpInitConfig(TRUE); de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " "(msg:\"Testing modbus code function\"; " "modbus: function 23; sid:1;)"); - - if (s == NULL) - goto end; + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); @@ -385,43 +398,26 @@ static int DetectEngineInspectModbusTest01(void) STREAM_TOSERVER, readWriteMultipleRegistersReq, sizeof(readWriteMultipleRegistersReq)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); - if (!(PacketAlertCheck(p, 1))) { - printf("sid 1 didn't match but should have: "); - goto end; - } + FAIL_IF_NOT(PacketAlertCheck(p, 1)); - result = 1; - -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - if (det_ctx != NULL) - DetectEngineThreadCtxDeinit(&tv, det_ctx); - if (de_ctx != NULL) - SigGroupCleanup(de_ctx); - if (de_ctx != NULL) - DetectEngineCtxFree(de_ctx); + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineThreadCtxDeinit(&tv, det_ctx); + SigGroupCleanup(de_ctx); + DetectEngineCtxFree(de_ctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); UTHFreePacket(p); - return result; + PASS; } /** \test code function and code subfunction. */ @@ -436,7 +432,7 @@ static int DetectEngineInspectModbusTest02(void) TcpSession ssn; ThreadVars tv; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&tv, 0, sizeof(ThreadVars)); memset(&f, 0, sizeof(Flow)); @@ -457,17 +453,14 @@ static int DetectEngineInspectModbusTest02(void) StreamTcpInitConfig(TRUE); de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " "(msg:\"Testing modbus function and subfunction\"; " "modbus: function 8, subfunction 4; sid:1;)"); - - if (s == NULL) - goto end; + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); @@ -476,43 +469,26 @@ static int DetectEngineInspectModbusTest02(void) int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, forceListenOnlyMode, sizeof(forceListenOnlyMode)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); - if (!(PacketAlertCheck(p, 1))) { - printf("sid 1 didn't match but should have: "); - goto end; - } - - result = 1; + FAIL_IF_NOT(PacketAlertCheck(p, 1)); -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - if (det_ctx != NULL) - DetectEngineThreadCtxDeinit(&tv, det_ctx); - if (de_ctx != NULL) - SigGroupCleanup(de_ctx); - if (de_ctx != NULL) - DetectEngineCtxFree(de_ctx); + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineThreadCtxDeinit(&tv, det_ctx); + SigGroupCleanup(de_ctx); + DetectEngineCtxFree(de_ctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); UTHFreePacket(p); - return result; + PASS; } /** \test function category. */ @@ -527,7 +503,7 @@ static int DetectEngineInspectModbusTest03(void) TcpSession ssn; ThreadVars tv; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&tv, 0, sizeof(ThreadVars)); memset(&f, 0, sizeof(Flow)); @@ -548,17 +524,14 @@ static int DetectEngineInspectModbusTest03(void) StreamTcpInitConfig(TRUE); de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " "(msg:\"Testing modbus category function\"; " "modbus: function reserved; sid:1;)"); - - if (s == NULL) - goto end; + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); @@ -568,43 +541,26 @@ static int DetectEngineInspectModbusTest03(void) STREAM_TOSERVER, encapsulatedInterfaceTransport, sizeof(encapsulatedInterfaceTransport)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); - if (!(PacketAlertCheck(p, 1))) { - printf("sid 1 didn't match but should have: "); - goto end; - } - - result = 1; + FAIL_IF_NOT(PacketAlertCheck(p, 1)); -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - if (det_ctx != NULL) - DetectEngineThreadCtxDeinit(&tv, det_ctx); - if (de_ctx != NULL) - SigGroupCleanup(de_ctx); - if (de_ctx != NULL) - DetectEngineCtxFree(de_ctx); + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineThreadCtxDeinit(&tv, det_ctx); + SigGroupCleanup(de_ctx); + DetectEngineCtxFree(de_ctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); UTHFreePacket(p); - return result; + PASS; } /** \test negative function category. */ @@ -619,7 +575,7 @@ static int DetectEngineInspectModbusTest04(void) TcpSession ssn; ThreadVars tv; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&tv, 0, sizeof(ThreadVars)); memset(&f, 0, sizeof(Flow)); @@ -640,17 +596,14 @@ static int DetectEngineInspectModbusTest04(void) StreamTcpInitConfig(TRUE); de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " "(msg:\"Testing modbus category function\"; " "modbus: function !assigned; sid:1;)"); - - if (s == NULL) - goto end; + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); @@ -659,43 +612,26 @@ static int DetectEngineInspectModbusTest04(void) int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, unassigned, sizeof(unassigned)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); - if (!(PacketAlertCheck(p, 1))) { - printf("sid 1 didn't match but should have: "); - goto end; - } - - result = 1; + FAIL_IF_NOT(PacketAlertCheck(p, 1)); -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - if (det_ctx != NULL) - DetectEngineThreadCtxDeinit(&tv, det_ctx); - if (de_ctx != NULL) - SigGroupCleanup(de_ctx); - if (de_ctx != NULL) - DetectEngineCtxFree(de_ctx); + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineThreadCtxDeinit(&tv, det_ctx); + SigGroupCleanup(de_ctx); + DetectEngineCtxFree(de_ctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); UTHFreePacket(p); - return result; + PASS; } /** \test access type. */ @@ -710,7 +646,7 @@ static int DetectEngineInspectModbusTest05(void) TcpSession ssn; ThreadVars tv; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&tv, 0, sizeof(ThreadVars)); memset(&f, 0, sizeof(Flow)); @@ -731,17 +667,14 @@ static int DetectEngineInspectModbusTest05(void) StreamTcpInitConfig(TRUE); de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " "(msg:\"Testing modbus access type\"; " "modbus: access read; sid:1;)"); - - if (s == NULL) - goto end; + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); @@ -750,43 +683,26 @@ static int DetectEngineInspectModbusTest05(void) int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, readCoilsReq, sizeof(readCoilsReq)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); - if (!(PacketAlertCheck(p, 1))) { - printf("sid 1 didn't match but should have: "); - goto end; - } + FAIL_IF_NOT(PacketAlertCheck(p, 1)); - result = 1; - -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - if (det_ctx != NULL) - DetectEngineThreadCtxDeinit(&tv, det_ctx); - if (de_ctx != NULL) - SigGroupCleanup(de_ctx); - if (de_ctx != NULL) - DetectEngineCtxFree(de_ctx); + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineThreadCtxDeinit(&tv, det_ctx); + SigGroupCleanup(de_ctx); + DetectEngineCtxFree(de_ctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); UTHFreePacket(p); - return result; + PASS; } /** \test access function. */ @@ -801,7 +717,7 @@ static int DetectEngineInspectModbusTest06(void) TcpSession ssn; ThreadVars tv; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&tv, 0, sizeof(ThreadVars)); memset(&f, 0, sizeof(Flow)); @@ -822,17 +738,14 @@ static int DetectEngineInspectModbusTest06(void) StreamTcpInitConfig(TRUE); de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " "(msg:\"Testing modbus access type\"; " "modbus: access read input; sid:1;)"); - - if (s == NULL) - goto end; + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); @@ -841,43 +754,26 @@ static int DetectEngineInspectModbusTest06(void) int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, readInputsRegistersReq, sizeof(readInputsRegistersReq)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); - if (!(PacketAlertCheck(p, 1))) { - printf("sid 1 didn't match but should have: "); - goto end; - } - - result = 1; + FAIL_IF_NOT(PacketAlertCheck(p, 1)); -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - if (det_ctx != NULL) - DetectEngineThreadCtxDeinit(&tv, det_ctx); - if (de_ctx != NULL) - SigGroupCleanup(de_ctx); - if (de_ctx != NULL) - DetectEngineCtxFree(de_ctx); + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineThreadCtxDeinit(&tv, det_ctx); + SigGroupCleanup(de_ctx); + DetectEngineCtxFree(de_ctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); UTHFreePacket(p); - return result; + PASS; } /** \test read access at an address. */ @@ -892,7 +788,7 @@ static int DetectEngineInspectModbusTest07(void) TcpSession ssn; ThreadVars tv; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&tv, 0, sizeof(ThreadVars)); memset(&f, 0, sizeof(Flow)); @@ -913,17 +809,14 @@ static int DetectEngineInspectModbusTest07(void) StreamTcpInitConfig(TRUE); de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; s = de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " "(msg:\"Testing modbus address access\"; " "modbus: access read, address 30870; sid:1;)"); - - if (s == NULL) - goto end; + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); @@ -932,43 +825,26 @@ static int DetectEngineInspectModbusTest07(void) int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, readCoilsReq, sizeof(readCoilsReq)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); - if (!(PacketAlertCheck(p, 1))) { - printf("sid 1 didn't match but should have: "); - goto end; - } - - result = 1; + FAIL_IF_NOT(PacketAlertCheck(p, 1)); -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - if (det_ctx != NULL) - DetectEngineThreadCtxDeinit(&tv, det_ctx); - if (de_ctx != NULL) - SigGroupCleanup(de_ctx); - if (de_ctx != NULL) - DetectEngineCtxFree(de_ctx); + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineThreadCtxDeinit(&tv, det_ctx); + SigGroupCleanup(de_ctx); + DetectEngineCtxFree(de_ctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); UTHFreePacket(p); - return result; + PASS; } /** \test read access at a range of address. */ @@ -983,7 +859,7 @@ static int DetectEngineInspectModbusTest08(void) TcpSession ssn; ThreadVars tv; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&tv, 0, sizeof(ThreadVars)); memset(&f, 0, sizeof(Flow)); @@ -1004,8 +880,7 @@ static int DetectEngineInspectModbusTest08(void) StreamTcpInitConfig(TRUE); de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; @@ -1060,9 +935,7 @@ static int DetectEngineInspectModbusTest08(void) "(msg:\"Testing modbus access\"; " "modbus: access read input, " "address 104<>110; sid:10;)"); - - if (s == NULL) - goto end; + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); @@ -1071,88 +944,36 @@ static int DetectEngineInspectModbusTest08(void) int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, readInputsRegistersReq, sizeof(readInputsRegistersReq)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); - if (PacketAlertCheck(p, 1)) { - printf("sid 1 did match but should not have: "); - goto end; - } + FAIL_IF(PacketAlertCheck(p, 1)); + FAIL_IF(PacketAlertCheck(p, 3)); + FAIL_IF(PacketAlertCheck(p, 9)); + FAIL_IF(PacketAlertCheck(p, 10)); - if (!(PacketAlertCheck(p, 2))) { - printf("sid 2 didn't match but should have: "); - goto end; - } + FAIL_IF_NOT(PacketAlertCheck(p, 2)); + FAIL_IF_NOT(PacketAlertCheck(p, 4)); + FAIL_IF_NOT(PacketAlertCheck(p, 5)); + FAIL_IF_NOT(PacketAlertCheck(p, 6)); + FAIL_IF_NOT(PacketAlertCheck(p, 7)); + FAIL_IF_NOT(PacketAlertCheck(p, 8)); - if (PacketAlertCheck(p, 3)) { - printf("sid 3 did match but should not have: "); - goto end; - } - - if (!(PacketAlertCheck(p, 4))) { - printf("sid 4 didn't match but should have: "); - goto end; - } - - if (!(PacketAlertCheck(p, 5))) { - printf("sid 5 didn't match but should have: "); - goto end; - } - - if (!(PacketAlertCheck(p, 6))) { - printf("sid 6 didn't match but should have: "); - goto end; - } - - if (!(PacketAlertCheck(p, 7))) { - printf("sid 7 didn't match but should have: "); - goto end; - } - - if (!(PacketAlertCheck(p, 8))) { - printf("sid 8 didn't match but should have: "); - goto end; - } - - if (PacketAlertCheck(p, 9)) { - printf("sid 9 did match but should not have: "); - goto end; - } - - if (PacketAlertCheck(p, 10)) { - printf("sid 10 did match but should not have: "); - goto end; - } - - result = 1; - -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - if (det_ctx != NULL) - DetectEngineThreadCtxDeinit(&tv, det_ctx); - if (de_ctx != NULL) - SigGroupCleanup(de_ctx); - if (de_ctx != NULL) - DetectEngineCtxFree(de_ctx); + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineThreadCtxDeinit(&tv, det_ctx); + SigGroupCleanup(de_ctx); + DetectEngineCtxFree(de_ctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); UTHFreePacket(p); - return result; + PASS; } /** \test write access at a address in a range of value. */ @@ -1167,7 +988,7 @@ static int DetectEngineInspectModbusTest09(void) TcpSession ssn; ThreadVars tv; - int result = 0; + FAIL_IF_NULL(alp_tctx); memset(&tv, 0, sizeof(ThreadVars)); memset(&f, 0, sizeof(Flow)); @@ -1188,8 +1009,7 @@ static int DetectEngineInspectModbusTest09(void) StreamTcpInitConfig(TRUE); de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; @@ -1246,9 +1066,129 @@ static int DetectEngineInspectModbusTest09(void) "(msg:\"Testing modbus write access\"; " "modbus: access write holding, " "address 15, value >4660; sid:10;)"); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); + + FLOWLOCK_WRLOCK(&f); + int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, + STREAM_TOSERVER, + readWriteMultipleRegistersReq, + sizeof(readWriteMultipleRegistersReq)); + FAIL_IF_NOT(r == 0); + FLOWLOCK_UNLOCK(&f); + + ModbusState *modbus_state = f.alstate; + FAIL_IF_NULL(modbus_state); + + /* do detect */ + SigMatchSignatures(&tv, de_ctx, det_ctx, p); + + FAIL_IF(PacketAlertCheck(p, 1)); + FAIL_IF(PacketAlertCheck(p, 4)); + FAIL_IF(PacketAlertCheck(p, 5)); + FAIL_IF(PacketAlertCheck(p, 8)); + FAIL_IF(PacketAlertCheck(p, 10)); + + FAIL_IF_NOT(PacketAlertCheck(p, 2)); + FAIL_IF_NOT(PacketAlertCheck(p, 3)); + FAIL_IF_NOT(PacketAlertCheck(p, 6)); + FAIL_IF_NOT(PacketAlertCheck(p, 7)); + FAIL_IF_NOT(PacketAlertCheck(p, 9)); + + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineThreadCtxDeinit(&tv, det_ctx); + SigGroupCleanup(de_ctx); + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + PASS; +} + +/** \test Test code unit_id. */ +static int DetectEngineInspectModbusTest10(void) +{ + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + DetectEngineThreadCtx *det_ctx = NULL; + DetectEngineCtx *de_ctx = NULL; + Flow f; + Packet *p = NULL; + Signature *s = NULL; + TcpSession ssn; + ThreadVars tv; - if (s == NULL) - goto end; + FAIL_IF_NULL(alp_tctx); + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&f, 0, sizeof(Flow)); + memset(&ssn, 0, sizeof(TcpSession)); + + p = UTHBuildPacket(readWriteMultipleRegistersReq, + sizeof(readWriteMultipleRegistersReq), + IPPROTO_TCP); + + FLOW_INITIALIZE(&f); + f.alproto = ALPROTO_MODBUS; + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.flags |= FLOW_IPV4; + + p->flow = &f; + p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; + + StreamTcpInitConfig(TRUE); + + de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + + de_ctx->flags |= DE_QUIET; + + /* readWriteMultipleRegistersReq, Write Starting Address = 0x0E, Quantity to Write = 0x03 */ + /* Unit ID = 0x0a (10) */ + /* Function code = 0x17 (23) */ + /* Write access register address 15 = 0x1234 (4660) */ + /* Write access register address 16 = 0x5678 (22136) */ + /* Write access register address 17 = 0x9ABC (39612) */ + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus code unit_id\"; " + "modbus: unit 10; sid:1;)"); + + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus code unit_id\"; " + "modbus: unit 12; sid:2;)"); + + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus code unit_id\"; " + "modbus: unit 5<>15; sid:3;)"); + + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus code unit_id\"; " + "modbus: unit 5<>9; sid:4;)"); + + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus code unit_id\"; " + "modbus: unit 11<>15; sid:5;)"); + + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus code unit_id\"; " + "modbus: unit >9; sid:6;)"); + + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus code unit_id\"; " + "modbus: unit >11; sid:7;)"); + + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus code unit_id\"; " + "modbus: unit <11; sid:8;)"); + + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus code unit_id\"; " + "modbus: unit <9; sid:9;)"); + FAIL_IF_NULL(s); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); @@ -1258,88 +1198,246 @@ static int DetectEngineInspectModbusTest09(void) STREAM_TOSERVER, readWriteMultipleRegistersReq, sizeof(readWriteMultipleRegistersReq)); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - FLOWLOCK_UNLOCK(&f); - goto end; - } + FAIL_IF_NOT(r == 0); FLOWLOCK_UNLOCK(&f); ModbusState *modbus_state = f.alstate; - if (modbus_state == NULL) { - printf("no modbus state: "); - goto end; - } + FAIL_IF_NULL(modbus_state); /* do detect */ SigMatchSignatures(&tv, de_ctx, det_ctx, p); - if (PacketAlertCheck(p, 1)) { - printf("sid 1 did match but should not have: "); - goto end; - } + FAIL_IF(PacketAlertCheck(p, 2)); + FAIL_IF(PacketAlertCheck(p, 4)); + FAIL_IF(PacketAlertCheck(p, 5)); + FAIL_IF(PacketAlertCheck(p, 7)); + FAIL_IF(PacketAlertCheck(p, 9)); - if (!(PacketAlertCheck(p, 2))) { - printf("sid 2 didn't match but should have: "); - goto end; - } + FAIL_IF_NOT(PacketAlertCheck(p, 1)); + FAIL_IF_NOT(PacketAlertCheck(p, 3)); + FAIL_IF_NOT(PacketAlertCheck(p, 6)); + FAIL_IF_NOT(PacketAlertCheck(p, 8)); - if (!(PacketAlertCheck(p, 3))) { - printf("sid 3 didn't match but should have: "); - goto end; - } + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineThreadCtxDeinit(&tv, det_ctx); + SigGroupCleanup(de_ctx); + DetectEngineCtxFree(de_ctx); - if (PacketAlertCheck(p, 4)) { - printf("sid 4 did match but should not have: "); - goto end; - } + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + PASS; +} - if (PacketAlertCheck(p, 5)) { - printf("sid 5 did match but should not have: "); - goto end; - } +/** \test Test code unit_id and code function. */ +static int DetectEngineInspectModbusTest11(void) +{ + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + DetectEngineThreadCtx *det_ctx = NULL; + DetectEngineCtx *de_ctx = NULL; + Flow f; + Packet *p = NULL; + Signature *s = NULL; + TcpSession ssn; + ThreadVars tv; - if (!(PacketAlertCheck(p, 6))) { - printf("sid 6 didn't match but should have: "); - goto end; - } + FAIL_IF_NULL(alp_tctx); - if (!(PacketAlertCheck(p, 7))) { - printf("sid 7 didn't match but should have: "); - goto end; - } + memset(&tv, 0, sizeof(ThreadVars)); + memset(&f, 0, sizeof(Flow)); + memset(&ssn, 0, sizeof(TcpSession)); - if (PacketAlertCheck(p, 8)) { - printf("sid 8 did match but should not have: "); - goto end; - } + p = UTHBuildPacket(readWriteMultipleRegistersReq, + sizeof(readWriteMultipleRegistersReq), + IPPROTO_TCP); - if (!(PacketAlertCheck(p, 9))) { - printf("sid 9 didn't match but should have: "); - goto end; - } + FLOW_INITIALIZE(&f); + f.alproto = ALPROTO_MODBUS; + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.flags |= FLOW_IPV4; - if (PacketAlertCheck(p, 10)) { - printf("sid 10 did match but should not have: "); - goto end; - } + p->flow = &f; + p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; + + StreamTcpInitConfig(TRUE); + + de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + + de_ctx->flags |= DE_QUIET; + + /* readWriteMultipleRegistersReq, Write Starting Address = 0x0E, Quantity to Write = 0x03 */ + /* Unit ID = 0x0a (10) */ + /* Function code = 0x17 (23) */ + /* Write access register address 15 = 0x1234 (4660) */ + /* Write access register address 16 = 0x5678 (22136) */ + /* Write access register address 17 = 0x9ABC (39612) */ + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus code unit_id\"; " + "modbus: unit 10, function 20; sid:1;)"); + + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus code unit_id\"; " + "modbus: unit 10, function 23; sid:2;)"); + + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus code unit_id\"; " + "modbus: unit 11, function 20; sid:3;)"); + + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus code unit_id\"; " + "modbus: unit 11, function 23; sid:4;)"); + + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus code unit_id\"; " + "modbus: unit 10, function public; sid:5;)"); + + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus code unit_id\"; " + "modbus: unit 11, function public; sid:6;)"); + + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus code unit_id\"; " + "modbus: unit 10, function user; sid:7;)"); + + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus code unit_id\"; " + "modbus: unit 10, function !user; sid:8;)"); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); + + FLOWLOCK_WRLOCK(&f); + int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, + STREAM_TOSERVER, + readWriteMultipleRegistersReq, + sizeof(readWriteMultipleRegistersReq)); + FAIL_IF_NOT(r == 0); + FLOWLOCK_UNLOCK(&f); + + ModbusState *modbus_state = f.alstate; + FAIL_IF_NULL(modbus_state); + + /* do detect */ + SigMatchSignatures(&tv, de_ctx, det_ctx, p); + + FAIL_IF(PacketAlertCheck(p, 1)); + FAIL_IF(PacketAlertCheck(p, 3)); + FAIL_IF(PacketAlertCheck(p, 4)); + FAIL_IF(PacketAlertCheck(p, 6)); + FAIL_IF(PacketAlertCheck(p, 7)); + + FAIL_IF_NOT(PacketAlertCheck(p, 2)); + FAIL_IF_NOT(PacketAlertCheck(p, 5)); + FAIL_IF_NOT(PacketAlertCheck(p, 8)); + + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineThreadCtxDeinit(&tv, det_ctx); + SigGroupCleanup(de_ctx); + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + PASS; +} + +/** \test unit_id and read access at an address. */ +static int DetectEngineInspectModbusTest12(void) +{ + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + DetectEngineThreadCtx *det_ctx = NULL; + DetectEngineCtx *de_ctx = NULL; + Flow f; + Packet *p = NULL; + Signature *s = NULL; + TcpSession ssn; + ThreadVars tv; + + FAIL_IF_NULL(alp_tctx); + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&f, 0, sizeof(Flow)); + memset(&ssn, 0, sizeof(TcpSession)); + + p = UTHBuildPacket(readCoilsReq, sizeof(readCoilsReq), IPPROTO_TCP); + + FLOW_INITIALIZE(&f); + f.alproto = ALPROTO_MODBUS; + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.flags |= FLOW_IPV4; + + p->flow = &f; + p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; + + StreamTcpInitConfig(TRUE); + + de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + + de_ctx->flags |= DE_QUIET; + + /* readCoilsReq, Read coils Starting Address = 0x7890 (30864), Quantity of coils = 0x13 (19) */ + /* Unit ID = 0x0a (10) */ + /* Function code = 0x01 (01) */ + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus address access\"; " + "modbus: unit 10, access read, address 30870; sid:1;)"); + + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus address access\"; " + "modbus: unit 10, access read, address 30863; sid:2;)"); + + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus address access\"; " + "modbus: unit 11, access read, address 30870; sid:3;)"); + + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus address access\"; " + "modbus: unit 11, access read, address 30863; sid:4;)"); + + s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus address access\"; " + "modbus: unit 10, access write; sid:5;)"); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); + + FLOWLOCK_WRLOCK(&f); + int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, + STREAM_TOSERVER, readCoilsReq, + sizeof(readCoilsReq)); + FAIL_IF_NOT(r == 0); + FLOWLOCK_UNLOCK(&f); + + ModbusState *modbus_state = f.alstate; + FAIL_IF_NULL(modbus_state); + + /* do detect */ + SigMatchSignatures(&tv, de_ctx, det_ctx, p); + + FAIL_IF(PacketAlertCheck(p, 2)); + FAIL_IF(PacketAlertCheck(p, 3)); + FAIL_IF(PacketAlertCheck(p, 4)); + FAIL_IF(PacketAlertCheck(p, 5)); - result = 1; + FAIL_IF_NOT(PacketAlertCheck(p, 1)); -end: - if (alp_tctx != NULL) - AppLayerParserThreadCtxFree(alp_tctx); - if (det_ctx != NULL) - DetectEngineThreadCtxDeinit(&tv, det_ctx); - if (de_ctx != NULL) - SigGroupCleanup(de_ctx); - if (de_ctx != NULL) - DetectEngineCtxFree(de_ctx); + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineThreadCtxDeinit(&tv, det_ctx); + SigGroupCleanup(de_ctx); + DetectEngineCtxFree(de_ctx); StreamTcpFreeConfig(TRUE); FLOW_DESTROY(&f); UTHFreePacket(p); - return result; + PASS; } #endif /* UNITTESTS */ @@ -1364,6 +1462,12 @@ void DetectEngineInspectModbusRegisterTests(void) DetectEngineInspectModbusTest08); UtRegisterTest("DetectEngineInspectModbusTest09 - Write access at an address a range of value", DetectEngineInspectModbusTest09); + UtRegisterTest("DetectEngineInspectModbusTest10 - Code unit_id", + DetectEngineInspectModbusTest10); + UtRegisterTest("DetectEngineInspectModbusTest11 - Code unit_id and code function", + DetectEngineInspectModbusTest11); + UtRegisterTest("DetectEngineInspectModbusTest12 - Code unit_id and acces function", + DetectEngineInspectModbusTest12); #endif /* UNITTESTS */ return; } diff --git a/src/detect-modbus.c b/src/detect-modbus.c index 972e09543d..159dd05dd1 100644 --- a/src/detect-modbus.c +++ b/src/detect-modbus.c @@ -56,6 +56,13 @@ #include "stream-tcp.h" +/** + * \brief Regex for parsing the Modbus unit id string + */ +#define PARSE_REGEX_UNIT_ID "^\\s*\"?\\s*unit\\s+([<>]?\\d+)(<>\\d+)?(,\\s*(.*))?\\s*\"?\\s*$" +static pcre *unit_id_parse_regex; +static pcre_extra *unit_id_parse_regex_study; + /** * \brief Regex for parsing the Modbus function string */ @@ -90,6 +97,9 @@ static void DetectModbusFree(void *ptr) { if (modbus->subfunction) SCFree(modbus->subfunction); + if (modbus->unit_id) + SCFree(modbus->unit_id); + if (modbus->address) SCFree(modbus->address); @@ -302,6 +312,14 @@ static DetectModbus *DetectModbusFunctionParse(const char *str) if (isdigit((unsigned char)ptr[0])) { modbus->function = atoi((const char*) ptr); + /* Function code 0 is managed by decoder_event INVALID_FUNCTION_CODE */ + if (modbus->function == MODBUS_FUNC_NONE) { + SCLogError(SC_ERR_INVALID_SIGNATURE, + "Invalid argument \"%d\" supplied to modbus function keyword.", + modbus->function); + goto error; + } + SCLogDebug("will look for modbus function %d", modbus->function); if (ret > 2) { @@ -354,6 +372,96 @@ error: SCReturnPtr(NULL, "DetectModbus"); } +/** \internal + * + * \brief This function is used to parse Modbus parameters in unit id mode + * + * \param str Pointer to the user provided id option + * + * \retval Pointer to DetectModbusUnit on success or NULL on failure + */ +static DetectModbus *DetectModbusUnitIdParse(const char *str) +{ + SCEnter(); + DetectModbus *modbus = NULL; + + char arg[MAX_SUBSTRINGS]; + int ov[MAX_SUBSTRINGS], ret, res; + + ret = pcre_exec(unit_id_parse_regex, unit_id_parse_regex_study, str, strlen(str), 0, 0, ov, MAX_SUBSTRINGS); + + if (ret < 1) + goto error; + + res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 1, arg, MAX_SUBSTRINGS); + if (res < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + + if (ret > 3) { + /* We have more Modbus option */ + const char *str_ptr; + + res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 4, &str_ptr); + if (res < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + + if ((modbus = DetectModbusFunctionParse(str_ptr)) == NULL) { + if ((modbus = DetectModbusAccessParse(str_ptr)) == NULL) { + SCLogError(SC_ERR_PCRE_MATCH, "invalid modbus option"); + goto error; + } + } + } else { + /* We have only unit id Modbus option */ + modbus = (DetectModbus *) SCCalloc(1, sizeof(DetectModbus)); + if (unlikely(modbus == NULL)) + goto error; + } + + /* We have a correct unit id option */ + modbus->unit_id = (DetectModbusValue *) SCCalloc(1, sizeof(DetectModbusValue)); + if (unlikely(modbus->unit_id == NULL)) + goto error; + + if (arg[0] == '>') { + modbus->unit_id->min = atoi((const char*) (arg+1)); + modbus->unit_id->mode = DETECT_MODBUS_GT; + } else if (arg[0] == '<') { + modbus->unit_id->min = atoi((const char*) (arg+1)); + modbus->unit_id->mode = DETECT_MODBUS_LT; + } else { + modbus->unit_id->min = atoi((const char*) arg); + } + SCLogDebug("and min/equal unit id %d", modbus->unit_id->min); + + if (ret > 2) { + res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 2, arg, MAX_SUBSTRINGS); + if (res < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + + if (*arg != '\0') { + modbus->unit_id->max = atoi((const char*) (arg+2)); + modbus->unit_id->mode = DETECT_MODBUS_RA; + SCLogDebug("and max unit id %d", modbus->unit_id->max); + } + } + + SCReturnPtr(modbus, "DetectModbusUnitId"); + +error: + if (modbus != NULL) + DetectModbusFree(modbus); + + SCReturnPtr(NULL, "DetectModbus"); +} + + /** \internal * * \brief this function is used to add the parsed "id" option into the current signature @@ -373,10 +481,12 @@ static int DetectModbusSetup(DetectEngineCtx *de_ctx, Signature *s, const char * if (DetectSignatureSetAppProto(s, ALPROTO_MODBUS) != 0) return -1; - if ((modbus = DetectModbusFunctionParse(str)) == NULL) { - if ((modbus = DetectModbusAccessParse(str)) == NULL) { - SCLogError(SC_ERR_PCRE_MATCH, "invalid modbus option"); - goto error; + if ((modbus = DetectModbusUnitIdParse(str)) == NULL) { + if ((modbus = DetectModbusFunctionParse(str)) == NULL) { + if ((modbus = DetectModbusAccessParse(str)) == NULL) { + SCLogError(SC_ERR_PCRE_MATCH, "invalid modbus option"); + goto error; + } } } @@ -412,6 +522,8 @@ void DetectModbusRegister(void) sigmatch_table[DETECT_AL_MODBUS].Free = DetectModbusFree; sigmatch_table[DETECT_AL_MODBUS].RegisterTests = DetectModbusRegisterTests; + DetectSetupParseRegexes(PARSE_REGEX_UNIT_ID, + &unit_id_parse_regex, &unit_id_parse_regex_study); DetectSetupParseRegexes(PARSE_REGEX_FUNCTION, &function_parse_regex, &function_parse_regex_study); DetectSetupParseRegexes(PARSE_REGEX_ACCESS, @@ -433,42 +545,26 @@ static int DetectModbusTest01(void) DetectEngineCtx *de_ctx = NULL; DetectModbus *modbus = NULL; - int result = 0; - de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " "(msg:\"Testing modbus function\"; " "modbus: function 1; sid:1;)"); - - if (de_ctx->sig_list == NULL) - goto end; - - if ((de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id] == NULL) || - (de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx == NULL)) { - printf("de_ctx->pmatch_tail == NULL && de_ctx->pmatch_tail->ctx == NULL: "); - goto end; - } + FAIL_IF_NULL(de_ctx->sig_list); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx; - if (modbus->function != 1) { - printf("expected function %d, got %" PRIu8 ": ", 1, modbus->function); - goto end; - } - - result = 1; + FAIL_IF_NOT(modbus->function == 1); - end: SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineCtxFree(de_ctx); - - return result; + PASS; } /** \test Signature containing a function and a subfunction. */ @@ -477,43 +573,27 @@ static int DetectModbusTest02(void) DetectEngineCtx *de_ctx = NULL; DetectModbus *modbus = NULL; - int result = 0; - de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " "(msg:\"Testing modbus function and subfunction\"; " "modbus: function 8, subfunction 4; sid:1;)"); - - if (de_ctx->sig_list == NULL) - goto end; - - if ((de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id] == NULL) || - (de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx == NULL)) { - printf("de_ctx->pmatch_tail == NULL && de_ctx->pmatch_tail->ctx == NULL: "); - goto end; - } + FAIL_IF_NULL(de_ctx->sig_list); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx; - if ((modbus->function != 8) || (*modbus->subfunction != 4)) { - printf("expected function %d, got %" PRIu8 ": ", 1, modbus->function); - printf("expected subfunction %d, got %" PRIu16 ": ", 4, *modbus->subfunction); - goto end; - } - - result = 1; + FAIL_IF_NOT(modbus->function == 8); + FAIL_IF_NOT(*modbus->subfunction == 4); - end: SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineCtxFree(de_ctx); - - return result; + PASS; } /** \test Signature containing a function category. */ @@ -522,42 +602,26 @@ static int DetectModbusTest03(void) DetectEngineCtx *de_ctx = NULL; DetectModbus *modbus = NULL; - int result = 0; - de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " "(msg:\"Testing modbus.function\"; " "modbus: function reserved; sid:1;)"); - - if (de_ctx->sig_list == NULL) - goto end; - - if ((de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id] == NULL) || - (de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx == NULL)) { - printf("de_ctx->pmatch_tail == NULL && de_ctx->pmatch_tail->ctx == NULL: "); - goto end; - } + FAIL_IF_NULL(de_ctx->sig_list); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx; - if (modbus->category != MODBUS_CAT_RESERVED) { - printf("expected function %d, got %" PRIu8 ": ", MODBUS_CAT_RESERVED, modbus->category); - goto end; - } - - result = 1; + FAIL_IF_NOT(modbus->category == MODBUS_CAT_RESERVED); - end: SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineCtxFree(de_ctx); - - return result; + PASS; } /** \test Signature containing a negative function category. */ @@ -568,42 +632,26 @@ static int DetectModbusTest04(void) uint8_t category = ~MODBUS_CAT_PUBLIC_ASSIGNED; - int result = 0; - de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " "(msg:\"Testing modbus function\"; " "modbus: function !assigned; sid:1;)"); - - if (de_ctx->sig_list == NULL) - goto end; - - if ((de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id] == NULL) || - (de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx == NULL)) { - printf("de_ctx->pmatch_tail == NULL && de_ctx->pmatch_tail->ctx == NULL: "); - goto end; - } + FAIL_IF_NULL(de_ctx->sig_list); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx; - if (modbus->category != category) { - printf("expected function %u, got %" PRIu8 ": ", ~MODBUS_CAT_PUBLIC_ASSIGNED, modbus->category); - goto end; - } - - result = 1; + FAIL_IF_NOT(modbus->category == category); - end: SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineCtxFree(de_ctx); - - return result; + PASS; } /** \test Signature containing a access type. */ @@ -612,42 +660,26 @@ static int DetectModbusTest05(void) DetectEngineCtx *de_ctx = NULL; DetectModbus *modbus = NULL; - int result = 0; - de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " "(msg:\"Testing modbus.access\"; " "modbus: access read; sid:1;)"); - - if (de_ctx->sig_list == NULL) - goto end; - - if ((de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id] == NULL) || - (de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx == NULL)) { - printf("de_ctx->pmatch_tail == NULL && de_ctx->pmatch_tail->ctx == NULL: "); - goto end; - } + FAIL_IF_NULL(de_ctx->sig_list); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx; - if (modbus->type != MODBUS_TYP_READ) { - printf("expected function %d, got %" PRIu8 ": ", MODBUS_TYP_READ, modbus->type); - goto end; - } + FAIL_IF_NOT(modbus->type == MODBUS_TYP_READ); - result = 1; - - end: SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineCtxFree(de_ctx); - - return result; + PASS; } /** \test Signature containing a access function. */ @@ -658,42 +690,26 @@ static int DetectModbusTest06(void) uint8_t type = (MODBUS_TYP_READ | MODBUS_TYP_DISCRETES); - int result = 0; - de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " "(msg:\"Testing modbus.access\"; " "modbus: access read discretes; sid:1;)"); - - if (de_ctx->sig_list == NULL) - goto end; - - if ((de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id] == NULL) || - (de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx == NULL)) { - printf("de_ctx->pmatch_tail == NULL && de_ctx->pmatch_tail->ctx == NULL: "); - goto end; - } + FAIL_IF_NULL(de_ctx->sig_list); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx; - if (modbus->type != type) { - printf("expected function %" PRIu8 ", got %" PRIu8 ": ", type, modbus->type); - goto end; - } - - result = 1; + FAIL_IF_NOT(modbus->type == type); - end: SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineCtxFree(de_ctx); - - return result; + PASS; } /** \test Signature containing a read access at an address. */ @@ -704,46 +720,29 @@ static int DetectModbusTest07(void) DetectModbusMode mode = DETECT_MODBUS_EQ; uint8_t type = MODBUS_TYP_READ; - int result = 0; de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " "(msg:\"Testing modbus.access\"; " "modbus: access read, address 1000; sid:1;)"); - - if (de_ctx->sig_list == NULL) - goto end; - - if ((de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id] == NULL) || - (de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx == NULL)) { - printf("de_ctx->pmatch_tail == NULL && de_ctx->pmatch_tail->ctx == NULL: "); - goto end; - } + FAIL_IF_NULL(de_ctx->sig_list); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx; - if ((modbus->type != type) || - ((*modbus->address).mode != mode) || - ((*modbus->address).min != 1000)) { - printf("expected function %" PRIu8 ", got %" PRIu8 ": ", type, modbus->type); - printf("expected mode %u, got %u: ", mode, (*modbus->address).mode); - printf("expected address %d, got %" PRIu16 ": ", 1000, (*modbus->address).min); - goto end; - } - - result = 1; + FAIL_IF_NOT(modbus->type == type); + FAIL_IF_NOT((*modbus->address).mode == mode); + FAIL_IF_NOT((*modbus->address).min == 1000); - end: SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineCtxFree(de_ctx); - - return result; + PASS; } /** \test Signature containing a write access at a range of address. */ @@ -754,46 +753,29 @@ static int DetectModbusTest08(void) DetectModbusMode mode = DETECT_MODBUS_GT; uint8_t type = (MODBUS_TYP_WRITE | MODBUS_TYP_COILS); - int result = 0; de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " "(msg:\"Testing modbus.access\"; " "modbus: access write coils, address >500; sid:1;)"); - - if (de_ctx->sig_list == NULL) - goto end; - - if ((de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id] == NULL) || - (de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx == NULL)) { - printf("de_ctx->pmatch_tail == NULL && de_ctx->pmatch_tail->ctx == NULL: "); - goto end; - } + FAIL_IF_NULL(de_ctx->sig_list); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx; - if ((modbus->type != type) || - ((*modbus->address).mode != mode) || - ((*modbus->address).min != 500)) { - printf("expected function %" PRIu8 ", got %" PRIu8 ": ", type, modbus->type); - printf("expected mode %d, got %u: ", mode, (*modbus->address).mode); - printf("expected address %u, got %" PRIu16 ": ", 500, (*modbus->address).min); - goto end; - } - - result = 1; + FAIL_IF_NOT(modbus->type == type); + FAIL_IF_NOT((*modbus->address).mode == mode); + FAIL_IF_NOT((*modbus->address).min == 500); - end: SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineCtxFree(de_ctx); - - return result; + PASS; } /** \test Signature containing a write access at a address a range of value. */ @@ -805,52 +787,160 @@ static int DetectModbusTest09(void) DetectModbusMode valueMode = DETECT_MODBUS_RA; uint8_t type = (MODBUS_TYP_WRITE | MODBUS_TYP_HOLDING); - int result = 0; de_ctx = DetectEngineCtxInit(); - if (de_ctx == NULL) - goto end; + FAIL_IF_NULL(de_ctx); de_ctx->flags |= DE_QUIET; de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " "(msg:\"Testing modbus.access\"; " "modbus: access write holding, address 100, value 500<>1000; sid:1;)"); + FAIL_IF_NULL(de_ctx->sig_list); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); - if (de_ctx->sig_list == NULL) - goto end; + modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx; - if ((de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id] == NULL) || - (de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx == NULL)) { - printf("de_ctx->pmatch_tail == NULL && de_ctx->pmatch_tail->ctx == NULL: "); - goto end; - } + FAIL_IF_NOT(modbus->type == type); + FAIL_IF_NOT((*modbus->address).mode == addressMode); + FAIL_IF_NOT((*modbus->address).min == 100); + FAIL_IF_NOT((*modbus->data).mode == valueMode); + FAIL_IF_NOT((*modbus->data).min == 500); + FAIL_IF_NOT((*modbus->data).max == 1000); + + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + PASS; +} + +/** \test Signature containing a unit_id. */ +static int DetectModbusTest10(void) +{ + DetectEngineCtx *de_ctx = NULL; + DetectModbus *modbus = NULL; + DetectModbusMode mode = DETECT_MODBUS_EQ; + + de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + + de_ctx->flags |= DE_QUIET; + + de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus unit_id\"; " + "modbus: unit 10; sid:1;)"); + FAIL_IF_NULL(de_ctx->sig_list); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx; - if ((modbus->type != type) || - ((*modbus->address).mode != addressMode) || - ((*modbus->address).min != 100) || - ((*modbus->data).mode != valueMode) || - ((*modbus->data).min != 500) || - ((*modbus->data).max != 1000)) { - printf("expected function %" PRIu8 ", got %" PRIu8 ": ", type, modbus->type); - printf("expected address mode %u, got %u: ", addressMode, (*modbus->address).mode); - printf("expected address %d, got %" PRIu16 ": ", 500, (*modbus->address).min); - printf("expected value mode %u, got %u: ", valueMode, (*modbus->data).mode); - printf("expected min value %d, got %" PRIu16 ": ", 500, (*modbus->data).min); - printf("expected max value %d, got %" PRIu16 ": ", 1000, (*modbus->data).max); - goto end; - } + FAIL_IF_NOT((*modbus->unit_id).min == 10); + FAIL_IF_NOT((*modbus->unit_id).mode == mode); + + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + PASS; +} - result = 1; +/** \test Signature containing a unit_id, a function and a subfunction. */ +static int DetectModbusTest11(void) +{ + DetectEngineCtx *de_ctx = NULL; + DetectModbus *modbus = NULL; + DetectModbusMode mode = DETECT_MODBUS_EQ; + + de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + + de_ctx->flags |= DE_QUIET; + + de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus function and subfunction\"; " + "modbus: unit 10, function 8, subfunction 4; sid:1;)"); + FAIL_IF_NULL(de_ctx->sig_list); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); + + modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx; + + FAIL_IF_NOT((*modbus->unit_id).min == 10); + FAIL_IF_NOT((*modbus->unit_id).mode == mode); + FAIL_IF_NOT(modbus->function == 8); + FAIL_IF_NOT((*modbus->subfunction) == 4); - end: SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineCtxFree(de_ctx); + PASS; +} - return result; +/** \test Signature containing an unit_id and a read access at an address. */ +static int DetectModbusTest12(void) +{ + DetectEngineCtx *de_ctx = NULL; + DetectModbus *modbus = NULL; + DetectModbusMode mode = DETECT_MODBUS_EQ; + + uint8_t type = MODBUS_TYP_READ; + + de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + + de_ctx->flags |= DE_QUIET; + + de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus.access\"; " + "modbus: unit 10, access read, address 1000; sid:1;)"); + FAIL_IF_NULL(de_ctx->sig_list); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); + + modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx; + + FAIL_IF_NOT((*modbus->unit_id).min == 10); + FAIL_IF_NOT((*modbus->unit_id).mode == mode); + FAIL_IF_NOT(modbus->type == type); + FAIL_IF_NOT((*modbus->address).mode == mode); + FAIL_IF_NOT((*modbus->address).min == 1000); + + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + PASS; +} + +/** \test Signature containing a range of unit_id. */ +static int DetectModbusTest13(void) +{ + DetectEngineCtx *de_ctx = NULL; + DetectModbus *modbus = NULL; + DetectModbusMode mode = DETECT_MODBUS_RA; + + de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + + de_ctx->flags |= DE_QUIET; + + de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any " + "(msg:\"Testing modbus.access\"; " + "modbus: unit 10<>500; sid:1;)"); + FAIL_IF_NULL(de_ctx->sig_list); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]); + FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx); + + modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx; + + FAIL_IF_NOT((*modbus->unit_id).min == 10); + FAIL_IF_NOT((*modbus->unit_id).max == 500); + FAIL_IF_NOT((*modbus->unit_id).mode == mode); + + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + PASS; } #endif /* UNITTESTS */ @@ -878,5 +968,13 @@ void DetectModbusRegisterTests(void) DetectModbusTest08); UtRegisterTest("DetectModbusTest09 - Testing write a range of value", DetectModbusTest09); + UtRegisterTest("DetectModbusTest10 - Testing unit_id", + DetectModbusTest10); + UtRegisterTest("DetectModbusTest11 - Testing unit_id, function and subfunction", + DetectModbusTest11); + UtRegisterTest("DetectModbusTest12 - Testing unit_id and access at address", + DetectModbusTest12); + UtRegisterTest("DetectModbusTest13 - Testing a range of unit_id", + DetectModbusTest13); #endif /* UNITTESTS */ } diff --git a/src/detect-modbus.h b/src/detect-modbus.h index 408fb7aff8..8ec02c99cf 100644 --- a/src/detect-modbus.h +++ b/src/detect-modbus.h @@ -54,6 +54,7 @@ typedef struct DetectModbus_ { uint8_t function; /** < Modbus function code to match */ uint16_t *subfunction; /** < Modbus subfunction to match */ uint8_t type; /** < Modbus access type to match */ + DetectModbusValue *unit_id; /** < Modbus unit id to match */ DetectModbusValue *address; /** < Modbus address to match */ DetectModbusValue *data; /** < Modbus data to match */ } DetectModbus;