mirror of https://github.com/OISF/suricata
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1563 lines
56 KiB
C
1563 lines
56 KiB
C
/*
|
|
* Copyright (C) 2014 ANSSI
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/**
|
|
* \file
|
|
*
|
|
* \author David DIALLO <diallo@et.esiea.fr>
|
|
*
|
|
* App-layer parser for Modbus protocol
|
|
*
|
|
*/
|
|
|
|
#include "suricata-common.h"
|
|
|
|
#include "util-debug.h"
|
|
|
|
#include "app-layer-parser.h"
|
|
#include "app-layer-modbus.h"
|
|
|
|
void ModbusParserRegisterTests(void);
|
|
|
|
/**
|
|
* \brief Function to register the Modbus protocol parser
|
|
*/
|
|
void RegisterModbusParsers(void)
|
|
{
|
|
rs_modbus_register_parser();
|
|
#ifdef UNITTESTS
|
|
AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_MODBUS, ModbusParserRegisterTests);
|
|
#endif
|
|
|
|
SCReturn;
|
|
}
|
|
|
|
/* UNITTESTS */
|
|
#ifdef UNITTESTS
|
|
#include "detect.h"
|
|
#include "detect-engine.h"
|
|
#include "detect-parse.h"
|
|
#include "detect-engine-build.h"
|
|
#include "detect-engine-alert.h"
|
|
|
|
#include "flow-util.h"
|
|
|
|
#include "util-unittest.h"
|
|
#include "util-unittest-helper.h"
|
|
|
|
#include "stream-tcp.h"
|
|
#include "stream-tcp-private.h"
|
|
|
|
#include "rust.h"
|
|
|
|
/* Modbus default stream reassembly depth */
|
|
#define MODBUS_CONFIG_DEFAULT_STREAM_DEPTH 0
|
|
|
|
/* Modbus Application Protocol Specification V1.1b3 6.1: Read Coils */
|
|
static uint8_t invalidFunctionCode[] = {
|
|
/* Transaction ID */ 0x00, 0x00,
|
|
/* Protocol ID */ 0x00, 0x00,
|
|
/* 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,
|
|
/* Protocol ID */ 0x00, 0x00,
|
|
/* Length */ 0x00, 0x06,
|
|
/* Unit ID */ 0x00,
|
|
/* Function code */ 0x01,
|
|
/* Starting Address */ 0x78, 0x90,
|
|
/* Quantity of coils */ 0x00, 0x13 };
|
|
|
|
static uint8_t readCoilsRsp[] = {/* Transaction ID */ 0x00, 0x00,
|
|
/* Protocol ID */ 0x00, 0x00,
|
|
/* Length */ 0x00, 0x06,
|
|
/* Unit ID */ 0x00,
|
|
/* Function code */ 0x01,
|
|
/* Byte count */ 0x03,
|
|
/* Coil Status */ 0xCD, 0x6B, 0x05 };
|
|
|
|
static uint8_t readCoilsErrorRsp[] = {
|
|
/* Transaction ID */ 0x00, 0x00,
|
|
/* Protocol ID */ 0x00, 0x00,
|
|
/* Length */ 0x00, 0x03,
|
|
/* Unit ID */ 0x00,
|
|
/* Function code */ 0x81,
|
|
/* Invalid Exception code: should trigger the InvalidExceptionCode ModbusEvent */
|
|
0xFF
|
|
};
|
|
|
|
/* Modbus Application Protocol Specification V1.1b3 6.6: Write Single register */
|
|
/* Example of a request to write register 2 to 00 03 hex */
|
|
static uint8_t writeSingleRegisterReq[] = {/* Transaction ID */ 0x00, 0x0A,
|
|
/* Protocol ID */ 0x00, 0x00,
|
|
/* Length */ 0x00, 0x06,
|
|
/* Unit ID */ 0x00,
|
|
/* Function code */ 0x06,
|
|
/* Register Address */ 0x00, 0x01,
|
|
/* Register Value */ 0x00, 0x03};
|
|
|
|
static uint8_t invalidWriteSingleRegisterReq[] = {/* Transaction ID */ 0x00, 0x0A,
|
|
/* Protocol ID */ 0x00, 0x00,
|
|
/* Length */ 0x00, 0x04,
|
|
/* Unit ID */ 0x00,
|
|
/* Function code */ 0x06,
|
|
/* Register Address */ 0x00, 0x01};
|
|
|
|
static uint8_t writeSingleRegisterRsp[] = {/* Transaction ID */ 0x00, 0x0A,
|
|
/* Protocol ID */ 0x00, 0x00,
|
|
/* Length */ 0x00, 0x06,
|
|
/* Unit ID */ 0x00,
|
|
/* Function code */ 0x06,
|
|
/* Register Address */ 0x00, 0x01,
|
|
/* Register Value */ 0x00, 0x03};
|
|
|
|
/* Modbus Application Protocol Specification V1.1b3 6.12: Write Multiple registers */
|
|
/* Example of a request to write two registers starting at 2 to 00 0A and 01 02 hex */
|
|
static uint8_t writeMultipleRegistersReq[] = {/* Transaction ID */ 0x00, 0x0A,
|
|
/* Protocol ID */ 0x00, 0x00,
|
|
/* Length */ 0x00, 0x0B,
|
|
/* Unit ID */ 0x00,
|
|
/* Function code */ 0x10,
|
|
/* Starting Address */ 0x00, 0x01,
|
|
/* Quantity of Registers */ 0x00, 0x02,
|
|
/* Byte count */ 0x04,
|
|
/* Registers Value */ 0x00, 0x0A,
|
|
0x01, 0x02};
|
|
|
|
static uint8_t writeMultipleRegistersRsp[] = {/* Transaction ID */ 0x00, 0x0A,
|
|
/* Protocol ID */ 0x00, 0x00,
|
|
/* Length */ 0x00, 0x06,
|
|
/* Unit ID */ 0x00,
|
|
/* Function code */ 0x10,
|
|
/* Starting Address */ 0x00, 0x01,
|
|
/* Quantity of Registers */ 0x00, 0x02};
|
|
|
|
/* Modbus Application Protocol Specification V1.1b3 6.16: Mask Write Register */
|
|
/* Example of a request to mask write to register 5 */
|
|
static uint8_t maskWriteRegisterReq[] = {/* Transaction ID */ 0x00, 0x0A,
|
|
/* Protocol ID */ 0x00, 0x00,
|
|
/* Length */ 0x00, 0x08,
|
|
/* Unit ID */ 0x00,
|
|
/* Function code */ 0x16,
|
|
/* Reference Address */ 0x00, 0x04,
|
|
/* And_Mask */ 0x00, 0xF2,
|
|
/* Or_Mask */ 0x00, 0x25};
|
|
|
|
static uint8_t invalidMaskWriteRegisterReq[] = {/* Transaction ID */ 0x00, 0x0A,
|
|
/* Protocol ID */ 0x00, 0x00,
|
|
/* Length */ 0x00, 0x06,
|
|
/* Unit ID */ 0x00,
|
|
/* Function code */ 0x16,
|
|
/* Reference Address */ 0x00, 0x04,
|
|
/* And_Mask */ 0x00, 0xF2};
|
|
|
|
static uint8_t maskWriteRegisterRsp[] = {/* Transaction ID */ 0x00, 0x0A,
|
|
/* Protocol ID */ 0x00, 0x00,
|
|
/* Length */ 0x00, 0x08,
|
|
/* Unit ID */ 0x00,
|
|
/* Function code */ 0x16,
|
|
/* Reference Address */ 0x00, 0x04,
|
|
/* And_Mask */ 0x00, 0xF2,
|
|
/* Or_Mask */ 0x00, 0x25};
|
|
|
|
/* Modbus Application Protocol Specification V1.1b3 6.17: Read/Write Multiple registers */
|
|
/* Example of a request to read six registers starting at register 4, */
|
|
/* and to write three registers starting at register 15 */
|
|
static uint8_t readWriteMultipleRegistersReq[] = {/* Transaction ID */ 0x12, 0x34,
|
|
/* Protocol ID */ 0x00, 0x00,
|
|
/* Length */ 0x00, 0x11,
|
|
/* Unit ID */ 0x00,
|
|
/* Function code */ 0x17,
|
|
/* Read Starting Address */ 0x00, 0x03,
|
|
/* Quantity to Read */ 0x00, 0x06,
|
|
/* Write Starting Address */ 0x00, 0x0E,
|
|
/* Quantity to Write */ 0x00, 0x03,
|
|
/* Write Byte count */ 0x06,
|
|
/* Write Registers Value */ 0x12, 0x34,
|
|
0x56, 0x78,
|
|
0x9A, 0xBC};
|
|
|
|
/* Mismatch value in Byte count 0x0B instead of 0x0C */
|
|
static uint8_t readWriteMultipleRegistersRsp[] = {/* Transaction ID */ 0x12, 0x34,
|
|
/* Protocol ID */ 0x00, 0x00,
|
|
/* Length */ 0x00, 0x0E,
|
|
/* Unit ID */ 0x00,
|
|
/* Function code */ 0x17,
|
|
/* Byte count */ 0x0B,
|
|
/* Read Registers Value */ 0x00, 0xFE,
|
|
0x0A, 0xCD,
|
|
0x00, 0x01,
|
|
0x00, 0x03,
|
|
0x00, 0x0D,
|
|
0x00};
|
|
|
|
/* Modbus Application Protocol Specification V1.1b3 6.8.1: 04 Force Listen Only Mode */
|
|
/* Example of a request to to remote device to its Listen Only Mode for Modbus Communications. */
|
|
static uint8_t forceListenOnlyMode[] = {/* Transaction ID */ 0x0A, 0x00,
|
|
/* Protocol ID */ 0x00, 0x00,
|
|
/* Length */ 0x00, 0x06,
|
|
/* Unit ID */ 0x00,
|
|
/* Function code */ 0x08,
|
|
/* Sub-function code */ 0x00, 0x04,
|
|
/* Data */ 0x00, 0x00};
|
|
|
|
static uint8_t invalidProtocolIdReq[] = {/* Transaction ID */ 0x00, 0x00,
|
|
/* Protocol ID */ 0x00, 0x01,
|
|
/* Length */ 0x00, 0x06,
|
|
/* Unit ID */ 0x00,
|
|
/* Function code */ 0x01,
|
|
/* Starting Address */ 0x78, 0x90,
|
|
/* Quantity of coils */ 0x00, 0x13 };
|
|
|
|
static uint8_t invalidLengthWriteMultipleRegistersReq[] = {
|
|
/* Transaction ID */ 0x00, 0x0A,
|
|
/* Protocol ID */ 0x00, 0x00,
|
|
/* Length */ 0x00, 0x09,
|
|
/* Unit ID */ 0x00,
|
|
/* Function code */ 0x10,
|
|
/* Starting Address */ 0x00, 0x01,
|
|
/* Quantity of Registers */ 0x00, 0x02,
|
|
/* Byte count */ 0x04,
|
|
/* Registers Value */ 0x00, 0x0A,
|
|
0x01, 0x02};
|
|
|
|
static uint8_t exceededLengthWriteMultipleRegistersReq[] = {
|
|
/* Transaction ID */ 0x00, 0x0A,
|
|
/* Protocol ID */ 0x00, 0x00,
|
|
/* Length */ 0xff, 0xfa,
|
|
/* Unit ID */ 0x00,
|
|
/* Function code */ 0x10,
|
|
/* Starting Address */ 0x00, 0x01,
|
|
/* Quantity of Registers */ 0x7f, 0xf9,
|
|
/* Byte count */ 0xff};
|
|
|
|
static uint8_t invalidLengthPDUWriteMultipleRegistersReq[] = {
|
|
/* Transaction ID */ 0x00, 0x0A,
|
|
/* Protocol ID */ 0x00, 0x00,
|
|
/* Length */ 0x00, 0x02,
|
|
/* Unit ID */ 0x00,
|
|
/* Function code */ 0x10};
|
|
|
|
/** \test Send Modbus Read Coils request/response. */
|
|
static int ModbusParserTest01(void) {
|
|
AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
|
|
Flow f;
|
|
TcpSession ssn;
|
|
|
|
FAIL_IF_NULL(alp_tctx);
|
|
|
|
memset(&f, 0, sizeof(f));
|
|
memset(&ssn, 0, sizeof(ssn));
|
|
|
|
FLOW_INITIALIZE(&f);
|
|
f.protoctx = (void *)&ssn;
|
|
f.proto = IPPROTO_TCP;
|
|
f.alproto = ALPROTO_MODBUS;
|
|
|
|
StreamTcpInitConfig(true);
|
|
|
|
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOSERVER, readCoilsReq,
|
|
sizeof(readCoilsReq));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
ModbusState *modbus_state = f.alstate;
|
|
FAIL_IF_NULL(modbus_state);
|
|
|
|
ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 0);
|
|
FAIL_IF_NULL(request._0);
|
|
FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 1);
|
|
FAIL_IF_NOT(rs_modbus_message_get_read_request_address(&request) == 0x7890);
|
|
FAIL_IF_NOT(rs_modbus_message_get_read_request_quantity(&request) == 19);
|
|
|
|
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOCLIENT, readCoilsRsp,
|
|
sizeof(readCoilsRsp));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
FAIL_IF_NOT(rs_modbus_state_get_tx_count(modbus_state) == 1);
|
|
|
|
AppLayerParserThreadCtxFree(alp_tctx);
|
|
StreamTcpFreeConfig(true);
|
|
FLOW_DESTROY(&f);
|
|
PASS;
|
|
}
|
|
|
|
/** \test Send Modbus Write Multiple registers request/response. */
|
|
static int ModbusParserTest02(void) {
|
|
AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
|
|
Flow f;
|
|
TcpSession ssn;
|
|
|
|
FAIL_IF_NULL(alp_tctx);
|
|
|
|
memset(&f, 0, sizeof(f));
|
|
memset(&ssn, 0, sizeof(ssn));
|
|
|
|
FLOW_INITIALIZE(&f);
|
|
f.protoctx = (void *)&ssn;
|
|
f.proto = IPPROTO_TCP;
|
|
f.alproto = ALPROTO_MODBUS;
|
|
|
|
StreamTcpInitConfig(true);
|
|
|
|
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOSERVER, writeMultipleRegistersReq,
|
|
sizeof(writeMultipleRegistersReq));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
ModbusState *modbus_state = f.alstate;
|
|
FAIL_IF_NULL(modbus_state);
|
|
|
|
ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 0);
|
|
FAIL_IF_NULL(request._0);
|
|
FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 16);
|
|
FAIL_IF_NOT(rs_modbus_message_get_write_multreq_address(&request) == 0x01);
|
|
FAIL_IF_NOT(rs_modbus_message_get_write_multreq_quantity(&request) == 2);
|
|
|
|
size_t data_len;
|
|
const uint8_t *data = rs_modbus_message_get_write_multreq_data(&request, &data_len);
|
|
FAIL_IF_NOT(data_len == 4);
|
|
FAIL_IF_NOT(data[0] == 0x00);
|
|
FAIL_IF_NOT(data[1] == 0x0A);
|
|
FAIL_IF_NOT(data[2] == 0x01);
|
|
FAIL_IF_NOT(data[3] == 0x02);
|
|
|
|
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOCLIENT, writeMultipleRegistersRsp,
|
|
sizeof(writeMultipleRegistersRsp));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
FAIL_IF_NOT(rs_modbus_state_get_tx_count(modbus_state) == 1);
|
|
|
|
AppLayerParserThreadCtxFree(alp_tctx);
|
|
StreamTcpFreeConfig(true);
|
|
FLOW_DESTROY(&f);
|
|
PASS;
|
|
}
|
|
|
|
/** \test Send Modbus Read/Write Multiple registers request/response with mismatch value. */
|
|
static int ModbusParserTest03(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 Data mismatch\"; "
|
|
"app-layer-event: "
|
|
"modbus.value_mismatch; "
|
|
"sid:1;)");
|
|
FAIL_IF_NULL(s);
|
|
|
|
SigGroupBuild(de_ctx);
|
|
DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOSERVER,
|
|
readWriteMultipleRegistersReq,
|
|
sizeof(readWriteMultipleRegistersReq));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
ModbusState *modbus_state = f.alstate;
|
|
FAIL_IF_NULL(modbus_state);
|
|
|
|
ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 0);
|
|
FAIL_IF_NULL(request._0);
|
|
|
|
FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 23);
|
|
FAIL_IF_NOT(rs_modbus_message_get_rw_multreq_read_address(&request) == 0x03);
|
|
FAIL_IF_NOT(rs_modbus_message_get_rw_multreq_read_quantity(&request) == 6);
|
|
FAIL_IF_NOT(rs_modbus_message_get_rw_multreq_write_address(&request) == 0x0E);
|
|
FAIL_IF_NOT(rs_modbus_message_get_rw_multreq_write_quantity(&request) == 3);
|
|
|
|
size_t data_len;
|
|
uint8_t const *data = rs_modbus_message_get_rw_multreq_write_data(&request, &data_len);
|
|
FAIL_IF_NOT(data_len == 6);
|
|
FAIL_IF_NOT(data[0] == 0x12);
|
|
FAIL_IF_NOT(data[1] == 0x34);
|
|
FAIL_IF_NOT(data[2] == 0x56);
|
|
FAIL_IF_NOT(data[3] == 0x78);
|
|
FAIL_IF_NOT(data[4] == 0x9A);
|
|
FAIL_IF_NOT(data[5] == 0xBC);
|
|
|
|
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOCLIENT, readWriteMultipleRegistersRsp,
|
|
sizeof(readWriteMultipleRegistersRsp));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
FAIL_IF_NOT(rs_modbus_state_get_tx_count(modbus_state) == 1);
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/** \test Send Modbus Force Listen Only Mode request. */
|
|
static int ModbusParserTest04(void) {
|
|
AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
|
|
Flow f;
|
|
TcpSession ssn;
|
|
|
|
FAIL_IF_NULL(alp_tctx);
|
|
|
|
memset(&f, 0, sizeof(f));
|
|
memset(&ssn, 0, sizeof(ssn));
|
|
|
|
FLOW_INITIALIZE(&f);
|
|
f.protoctx = (void *)&ssn;
|
|
f.proto = IPPROTO_TCP;
|
|
f.alproto = ALPROTO_MODBUS;
|
|
|
|
StreamTcpInitConfig(true);
|
|
|
|
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOSERVER, forceListenOnlyMode,
|
|
sizeof(forceListenOnlyMode));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
ModbusState *modbus_state = f.alstate;
|
|
FAIL_IF_NULL(modbus_state);
|
|
|
|
ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 0);
|
|
FAIL_IF_NULL(request._0);
|
|
|
|
FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 8);
|
|
FAIL_IF_NOT(rs_modbus_message_get_subfunction(&request) == 4);
|
|
|
|
AppLayerParserThreadCtxFree(alp_tctx);
|
|
StreamTcpFreeConfig(true);
|
|
FLOW_DESTROY(&f);
|
|
PASS;
|
|
}
|
|
|
|
/** \test Send Modbus invalid Protocol version in request. */
|
|
static int ModbusParserTest05(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 Protocol version\"; "
|
|
"app-layer-event: "
|
|
"modbus.invalid_protocol_id; "
|
|
"sid:1;)");
|
|
FAIL_IF_NULL(s);
|
|
|
|
SigGroupBuild(de_ctx);
|
|
DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOSERVER, invalidProtocolIdReq,
|
|
sizeof(invalidProtocolIdReq));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
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;
|
|
}
|
|
|
|
/** \test Send Modbus unsolicited response. */
|
|
static int ModbusParserTest06(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 unsolicited response\"; "
|
|
"app-layer-event: "
|
|
"modbus.unsolicited_response; "
|
|
"sid:1;)");
|
|
FAIL_IF_NULL(s);
|
|
|
|
SigGroupBuild(de_ctx);
|
|
DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOCLIENT, readCoilsRsp,
|
|
sizeof(readCoilsRsp));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
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;
|
|
}
|
|
|
|
/** \test Send Modbus invalid Length request. */
|
|
static int ModbusParserTest07(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 Length\"; "
|
|
"app-layer-event: "
|
|
"modbus.invalid_length; "
|
|
"sid:1;)");
|
|
FAIL_IF_NULL(s);
|
|
|
|
SigGroupBuild(de_ctx);
|
|
DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOSERVER,
|
|
invalidLengthWriteMultipleRegistersReq,
|
|
sizeof(invalidLengthWriteMultipleRegistersReq));
|
|
FAIL_IF_NOT(r == 1);
|
|
|
|
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;
|
|
}
|
|
|
|
/** \test Send Modbus Read Coils request and error response with Exception code invalid. */
|
|
static int ModbusParserTest08(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 Exception code invalid\"; "
|
|
"app-layer-event: "
|
|
"modbus.invalid_exception_code; "
|
|
"sid:1;)");
|
|
FAIL_IF_NULL(s);
|
|
|
|
SigGroupBuild(de_ctx);
|
|
DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOSERVER, readCoilsReq,
|
|
sizeof(readCoilsReq));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
ModbusState *modbus_state = f.alstate;
|
|
FAIL_IF_NULL(modbus_state);
|
|
|
|
ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 0);
|
|
FAIL_IF_NULL(request._0);
|
|
|
|
FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 1);
|
|
FAIL_IF_NOT(rs_modbus_message_get_read_request_address(&request) == 0x7890);
|
|
FAIL_IF_NOT(rs_modbus_message_get_read_request_quantity(&request) == 19);
|
|
|
|
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOCLIENT, readCoilsErrorRsp,
|
|
sizeof(readCoilsErrorRsp));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
FAIL_IF_NOT(rs_modbus_state_get_tx_count(modbus_state) == 1);
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/** \test Modbus fragmentation - 1 ADU over 2 TCP packets. */
|
|
static int ModbusParserTest09(void) {
|
|
AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
|
|
Flow f;
|
|
TcpSession ssn;
|
|
|
|
uint32_t input_len = sizeof(readCoilsReq), part2_len = 3;
|
|
uint8_t *input = readCoilsReq;
|
|
|
|
FAIL_IF_NULL(alp_tctx);
|
|
|
|
memset(&f, 0, sizeof(f));
|
|
memset(&ssn, 0, sizeof(ssn));
|
|
|
|
FLOW_INITIALIZE(&f);
|
|
f.protoctx = (void *)&ssn;
|
|
f.proto = IPPROTO_TCP;
|
|
f.alproto = ALPROTO_MODBUS;
|
|
|
|
StreamTcpInitConfig(true);
|
|
|
|
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOSERVER, input, input_len - part2_len);
|
|
FAIL_IF_NOT(r == 1);
|
|
|
|
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOSERVER, input, input_len);
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
ModbusState *modbus_state = f.alstate;
|
|
FAIL_IF_NULL(modbus_state);
|
|
|
|
ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 0);
|
|
FAIL_IF_NULL(request._0);
|
|
|
|
FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 1);
|
|
FAIL_IF_NOT(rs_modbus_message_get_read_request_address(&request) == 0x7890);
|
|
FAIL_IF_NOT(rs_modbus_message_get_read_request_quantity(&request) == 19);
|
|
|
|
input_len = sizeof(readCoilsRsp);
|
|
part2_len = 10;
|
|
input = readCoilsRsp;
|
|
|
|
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOCLIENT, input, input_len - part2_len);
|
|
FAIL_IF_NOT(r == 1);
|
|
|
|
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOCLIENT, input, input_len);
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
FAIL_IF_NOT(rs_modbus_state_get_tx_count(modbus_state) == 1);
|
|
|
|
AppLayerParserThreadCtxFree(alp_tctx);
|
|
StreamTcpFreeConfig(true);
|
|
FLOW_DESTROY(&f);
|
|
PASS;
|
|
}
|
|
|
|
/** \test Modbus fragmentation - 2 ADU in 1 TCP packet. */
|
|
static int ModbusParserTest10(void) {
|
|
uint32_t input_len = sizeof(readCoilsReq) + sizeof(writeMultipleRegistersReq);
|
|
uint8_t *input, *ptr;
|
|
|
|
AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
|
|
Flow f;
|
|
TcpSession ssn;
|
|
|
|
FAIL_IF_NULL(alp_tctx);
|
|
|
|
input = (uint8_t *) SCMalloc (input_len * sizeof(uint8_t));
|
|
FAIL_IF_NULL(input);
|
|
|
|
memcpy(input, readCoilsReq, sizeof(readCoilsReq));
|
|
memcpy(input + sizeof(readCoilsReq), writeMultipleRegistersReq, sizeof(writeMultipleRegistersReq));
|
|
|
|
memset(&f, 0, sizeof(f));
|
|
memset(&ssn, 0, sizeof(ssn));
|
|
|
|
FLOW_INITIALIZE(&f);
|
|
f.protoctx = (void *)&ssn;
|
|
f.proto = IPPROTO_TCP;
|
|
f.alproto = ALPROTO_MODBUS;
|
|
|
|
StreamTcpInitConfig(true);
|
|
|
|
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOSERVER, input, input_len);
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
ModbusState *modbus_state = f.alstate;
|
|
FAIL_IF_NULL(modbus_state);
|
|
|
|
FAIL_IF_NOT(rs_modbus_state_get_tx_count(modbus_state) == 2);
|
|
|
|
ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 1);
|
|
FAIL_IF_NULL(request._0);
|
|
|
|
FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 16);
|
|
FAIL_IF_NOT(rs_modbus_message_get_write_multreq_address(&request) == 0x01);
|
|
FAIL_IF_NOT(rs_modbus_message_get_write_multreq_quantity(&request) == 2);
|
|
|
|
size_t data_len;
|
|
uint8_t const *data = rs_modbus_message_get_write_multreq_data(&request, &data_len);
|
|
FAIL_IF_NOT(data_len == 4);
|
|
FAIL_IF_NOT(data[0] == 0x00);
|
|
FAIL_IF_NOT(data[1] == 0x0A);
|
|
FAIL_IF_NOT(data[2] == 0x01);
|
|
FAIL_IF_NOT(data[3] == 0x02);
|
|
|
|
input_len = sizeof(readCoilsRsp) + sizeof(writeMultipleRegistersRsp);
|
|
|
|
ptr = (uint8_t *) SCRealloc (input, input_len * sizeof(uint8_t));
|
|
FAIL_IF_NULL(ptr);
|
|
input = ptr;
|
|
|
|
memcpy(input, readCoilsRsp, sizeof(readCoilsRsp));
|
|
memcpy(input + sizeof(readCoilsRsp), writeMultipleRegistersRsp, sizeof(writeMultipleRegistersRsp));
|
|
|
|
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT, input, input_len);
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
SCFree(input);
|
|
AppLayerParserThreadCtxFree(alp_tctx);
|
|
StreamTcpFreeConfig(true);
|
|
FLOW_DESTROY(&f);
|
|
PASS;
|
|
}
|
|
|
|
/** \test Send Modbus exceed Length request. */
|
|
static int ModbusParserTest11(void) {
|
|
AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
|
|
DetectEngineThreadCtx *det_ctx = NULL;
|
|
Flow f;
|
|
Packet *p = NULL;
|
|
Signature *s = NULL;
|
|
TcpSession ssn;
|
|
ThreadVars tv;
|
|
|
|
size_t input_len = 65536;
|
|
uint8_t *input = SCCalloc(1, input_len);
|
|
|
|
FAIL_IF(input == NULL);
|
|
|
|
memcpy(input, exceededLengthWriteMultipleRegistersReq,
|
|
sizeof(exceededLengthWriteMultipleRegistersReq));
|
|
|
|
FAIL_IF(alp_tctx == NULL);
|
|
|
|
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 Length\"; "
|
|
"app-layer-event: "
|
|
"modbus.invalid_length; "
|
|
"sid:1;)");
|
|
FAIL_IF_NULL(s);
|
|
|
|
SigGroupBuild(de_ctx);
|
|
DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
int r = AppLayerParserParse(
|
|
NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, input, input_len);
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
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;
|
|
}
|
|
|
|
/** \test Send Modbus invalid PDU Length. */
|
|
static int ModbusParserTest12(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 Length\"; "
|
|
"app-layer-event: "
|
|
"modbus.invalid_length; "
|
|
"sid:1;)");
|
|
FAIL_IF_NULL(s);
|
|
|
|
SigGroupBuild(de_ctx);
|
|
DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOSERVER,
|
|
invalidLengthPDUWriteMultipleRegistersReq,
|
|
sizeof(invalidLengthPDUWriteMultipleRegistersReq));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
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;
|
|
}
|
|
|
|
/** \test Send Modbus Mask Write register request/response. */
|
|
static int ModbusParserTest13(void) {
|
|
AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
|
|
Flow f;
|
|
TcpSession ssn;
|
|
|
|
FAIL_IF_NULL(alp_tctx);
|
|
|
|
memset(&f, 0, sizeof(f));
|
|
memset(&ssn, 0, sizeof(ssn));
|
|
|
|
FLOW_INITIALIZE(&f);
|
|
f.protoctx = (void *)&ssn;
|
|
f.proto = IPPROTO_TCP;
|
|
f.alproto = ALPROTO_MODBUS;
|
|
|
|
StreamTcpInitConfig(true);
|
|
|
|
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOSERVER, maskWriteRegisterReq,
|
|
sizeof(maskWriteRegisterReq));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
ModbusState *modbus_state = f.alstate;
|
|
FAIL_IF_NULL(modbus_state);
|
|
|
|
ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 0);
|
|
FAIL_IF_NULL(request._0);
|
|
|
|
FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 22);
|
|
FAIL_IF_NOT(rs_modbus_message_get_and_mask(&request) == 0x00F2);
|
|
FAIL_IF_NOT(rs_modbus_message_get_or_mask(&request) == 0x0025);
|
|
|
|
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOCLIENT, maskWriteRegisterRsp,
|
|
sizeof(maskWriteRegisterRsp));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
FAIL_IF_NOT(rs_modbus_state_get_tx_count(modbus_state) == 1);
|
|
|
|
AppLayerParserThreadCtxFree(alp_tctx);
|
|
StreamTcpFreeConfig(true);
|
|
FLOW_DESTROY(&f);
|
|
PASS;
|
|
}
|
|
|
|
/** \test Send Modbus Write single register request/response. */
|
|
static int ModbusParserTest14(void) {
|
|
AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
|
|
Flow f;
|
|
TcpSession ssn;
|
|
|
|
FAIL_IF_NULL(alp_tctx);
|
|
|
|
memset(&f, 0, sizeof(f));
|
|
memset(&ssn, 0, sizeof(ssn));
|
|
|
|
FLOW_INITIALIZE(&f);
|
|
f.protoctx = (void *)&ssn;
|
|
f.proto = IPPROTO_TCP;
|
|
f.alproto = ALPROTO_MODBUS;
|
|
|
|
StreamTcpInitConfig(true);
|
|
|
|
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOSERVER, writeSingleRegisterReq,
|
|
sizeof(writeSingleRegisterReq));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
ModbusState *modbus_state = f.alstate;
|
|
FAIL_IF_NULL(modbus_state);
|
|
|
|
ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 0);
|
|
FAIL_IF_NULL(request._0);
|
|
|
|
FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 6);
|
|
FAIL_IF_NOT(rs_modbus_message_get_write_address(&request) == 0x0001);
|
|
FAIL_IF_NOT(rs_modbus_message_get_write_data(&request) == 0x0003);
|
|
|
|
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOCLIENT, writeSingleRegisterRsp,
|
|
sizeof(writeSingleRegisterRsp));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
FAIL_IF_NOT(rs_modbus_state_get_tx_count(modbus_state) == 1);
|
|
|
|
AppLayerParserThreadCtxFree(alp_tctx);
|
|
StreamTcpFreeConfig(true);
|
|
FLOW_DESTROY(&f);
|
|
PASS;
|
|
}
|
|
|
|
/** \test Send invalid Modbus Mask Write register request. */
|
|
static int ModbusParserTest15(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(f));
|
|
memset(&ssn, 0, sizeof(ssn));
|
|
|
|
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 Length\"; "
|
|
"app-layer-event: "
|
|
"modbus.invalid_length; "
|
|
"sid:1;)");
|
|
FAIL_IF_NULL(s);
|
|
|
|
SigGroupBuild(de_ctx);
|
|
DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOSERVER, invalidMaskWriteRegisterReq,
|
|
sizeof(invalidMaskWriteRegisterReq));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
ModbusState *modbus_state = f.alstate;
|
|
FAIL_IF_NULL(modbus_state);
|
|
|
|
ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 0);
|
|
FAIL_IF_NULL(request._0);
|
|
|
|
FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 22);
|
|
|
|
/* do detect */
|
|
SigMatchSignatures(&tv, de_ctx, det_ctx, p);
|
|
|
|
FAIL_IF_NOT(PacketAlertCheck(p, 1));
|
|
|
|
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOCLIENT, maskWriteRegisterRsp,
|
|
sizeof(maskWriteRegisterRsp));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
FAIL_IF_NOT(rs_modbus_state_get_tx_count(modbus_state) == 1);
|
|
ModbusMessage response = rs_modbus_state_get_tx_response(modbus_state, 0);
|
|
FAIL_IF_NULL(response._0);
|
|
|
|
FAIL_IF_NOT(rs_modbus_message_get_function(&response) == 22);
|
|
|
|
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;
|
|
}
|
|
|
|
/** \test Send invalid Modbus Mask Write register request. */
|
|
static int ModbusParserTest16(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(f));
|
|
memset(&ssn, 0, sizeof(ssn));
|
|
|
|
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 Length\"; "
|
|
"app-layer-event: "
|
|
"modbus.invalid_length; "
|
|
"sid:1;)");
|
|
FAIL_IF_NULL(s);
|
|
|
|
SigGroupBuild(de_ctx);
|
|
DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
|
|
|
|
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOSERVER,
|
|
invalidWriteSingleRegisterReq,
|
|
sizeof(invalidWriteSingleRegisterReq));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
ModbusState *modbus_state = f.alstate;
|
|
FAIL_IF_NULL(modbus_state);
|
|
|
|
ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 0);
|
|
FAIL_IF_NULL(request._0);
|
|
|
|
FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 6);
|
|
size_t data_len;
|
|
const uint8_t *data = rs_modbus_message_get_bytevec_data(&request, &data_len);
|
|
FAIL_IF_NOT(data_len == 2);
|
|
FAIL_IF_NOT(data[0] == 0x00);
|
|
FAIL_IF_NOT(data[1] == 0x01);
|
|
|
|
/* do detect */
|
|
SigMatchSignatures(&tv, de_ctx, det_ctx, p);
|
|
|
|
FAIL_IF_NOT(PacketAlertCheck(p, 1));
|
|
|
|
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOCLIENT, writeSingleRegisterRsp,
|
|
sizeof(writeSingleRegisterRsp));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
FAIL_IF_NOT(rs_modbus_state_get_tx_count(modbus_state) == 1);
|
|
ModbusMessage response = rs_modbus_state_get_tx_response(modbus_state, 0);
|
|
FAIL_IF_NULL(response._0);
|
|
|
|
FAIL_IF_NOT(rs_modbus_message_get_function(&response) == 6);
|
|
FAIL_IF_NOT(rs_modbus_message_get_write_address(&response) == 0x0001);
|
|
|
|
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;}
|
|
|
|
/** \test Checks if stream_depth is correct */
|
|
static int ModbusParserTest17(void) {
|
|
AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
|
|
Flow f;
|
|
TcpSession ssn;
|
|
|
|
FAIL_IF_NULL(alp_tctx);
|
|
|
|
memset(&f, 0, sizeof(f));
|
|
memset(&ssn, 0, sizeof(ssn));
|
|
|
|
FLOW_INITIALIZE(&f);
|
|
f.protoctx = (void *)&ssn;
|
|
f.proto = IPPROTO_TCP;
|
|
f.alproto = ALPROTO_MODBUS;
|
|
|
|
StreamTcpInitConfig(true);
|
|
|
|
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
|
|
readCoilsReq, sizeof(readCoilsReq));
|
|
FAIL_IF(r != 0);
|
|
|
|
FAIL_IF(f.alstate == NULL);
|
|
|
|
FAIL_IF(((TcpSession *)(f.protoctx))->reassembly_depth != MODBUS_CONFIG_DEFAULT_STREAM_DEPTH);
|
|
|
|
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT,
|
|
readCoilsRsp, sizeof(readCoilsRsp));
|
|
FAIL_IF(r != 0);
|
|
|
|
FAIL_IF(((TcpSession *)(f.protoctx))->reassembly_depth != MODBUS_CONFIG_DEFAULT_STREAM_DEPTH);
|
|
|
|
AppLayerParserThreadCtxFree(alp_tctx);
|
|
StreamTcpFreeConfig(true);
|
|
FLOW_DESTROY(&f);
|
|
PASS;
|
|
}
|
|
|
|
/*/ \test Checks if stream depth is correct over 2 TCP packets */
|
|
static int ModbusParserTest18(void) {
|
|
AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
|
|
Flow f;
|
|
TcpSession ssn;
|
|
|
|
uint32_t input_len = sizeof(readCoilsReq), part2_len = 3;
|
|
uint8_t *input = readCoilsReq;
|
|
|
|
FAIL_IF_NULL(alp_tctx);
|
|
|
|
memset(&f, 0, sizeof(f));
|
|
memset(&ssn, 0, sizeof(ssn));
|
|
|
|
FLOW_INITIALIZE(&f);
|
|
f.protoctx = (void *)&ssn;
|
|
f.proto = IPPROTO_TCP;
|
|
f.alproto = ALPROTO_MODBUS;
|
|
|
|
StreamTcpInitConfig(true);
|
|
|
|
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
|
|
input, input_len - part2_len);
|
|
FAIL_IF(r != 1);
|
|
|
|
FAIL_IF(((TcpSession *)(f.protoctx))->reassembly_depth != MODBUS_CONFIG_DEFAULT_STREAM_DEPTH);
|
|
|
|
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER,
|
|
input, input_len);
|
|
FAIL_IF(r != 0);
|
|
|
|
FAIL_IF(((TcpSession *)(f.protoctx))->reassembly_depth != MODBUS_CONFIG_DEFAULT_STREAM_DEPTH);
|
|
|
|
FAIL_IF(f.alstate == NULL);
|
|
|
|
input_len = sizeof(readCoilsRsp);
|
|
part2_len = 10;
|
|
input = readCoilsRsp;
|
|
|
|
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT,
|
|
input, input_len - part2_len);
|
|
FAIL_IF(r != 1);
|
|
|
|
FAIL_IF(((TcpSession *)(f.protoctx))->reassembly_depth != MODBUS_CONFIG_DEFAULT_STREAM_DEPTH);
|
|
|
|
r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT,
|
|
input, input_len);
|
|
FAIL_IF(r != 0);
|
|
|
|
FAIL_IF(((TcpSession *)(f.protoctx))->reassembly_depth != MODBUS_CONFIG_DEFAULT_STREAM_DEPTH);
|
|
|
|
AppLayerParserThreadCtxFree(alp_tctx);
|
|
StreamTcpFreeConfig(true);
|
|
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);
|
|
|
|
int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS,
|
|
STREAM_TOSERVER,
|
|
invalidFunctionCode,
|
|
sizeof(invalidFunctionCode));
|
|
FAIL_IF_NOT(r == 0);
|
|
|
|
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) {
|
|
#ifdef UNITTESTS
|
|
UtRegisterTest("ModbusParserTest01 - Modbus Read Coils request",
|
|
ModbusParserTest01);
|
|
UtRegisterTest("ModbusParserTest02 - Modbus Write Multiple registers request",
|
|
ModbusParserTest02);
|
|
UtRegisterTest("ModbusParserTest03 - Modbus Read/Write Multiple registers request",
|
|
ModbusParserTest03);
|
|
UtRegisterTest("ModbusParserTest04 - Modbus Force Listen Only Mode request",
|
|
ModbusParserTest04);
|
|
UtRegisterTest("ModbusParserTest05 - Modbus invalid Protocol version",
|
|
ModbusParserTest05);
|
|
UtRegisterTest("ModbusParserTest06 - Modbus unsolicited response",
|
|
ModbusParserTest06);
|
|
UtRegisterTest("ModbusParserTest07 - Modbus invalid Length request",
|
|
ModbusParserTest07);
|
|
UtRegisterTest("ModbusParserTest08 - Modbus Exception code invalid",
|
|
ModbusParserTest08);
|
|
UtRegisterTest("ModbusParserTest09 - Modbus fragmentation - 1 ADU in 2 TCP packets",
|
|
ModbusParserTest09);
|
|
UtRegisterTest("ModbusParserTest10 - Modbus fragmentation - 2 ADU in 1 TCP packet",
|
|
ModbusParserTest10);
|
|
UtRegisterTest("ModbusParserTest11 - Modbus exceeded Length request",
|
|
ModbusParserTest11);
|
|
UtRegisterTest("ModbusParserTest12 - Modbus invalid PDU Length",
|
|
ModbusParserTest12);
|
|
UtRegisterTest("ModbusParserTest13 - Modbus Mask Write register request",
|
|
ModbusParserTest13);
|
|
UtRegisterTest("ModbusParserTest14 - Modbus Write single register request",
|
|
ModbusParserTest14);
|
|
UtRegisterTest("ModbusParserTest15 - Modbus invalid Mask Write register request",
|
|
ModbusParserTest15);
|
|
UtRegisterTest("ModbusParserTest16 - Modbus invalid Write single register request",
|
|
ModbusParserTest16);
|
|
UtRegisterTest("ModbusParserTest17 - Modbus stream depth",
|
|
ModbusParserTest17);
|
|
UtRegisterTest("ModbusParserTest18 - Modbus stream depth in 2 TCP packets",
|
|
ModbusParserTest18);
|
|
UtRegisterTest("ModbusParserTest19 - Modbus invalid Function code",
|
|
ModbusParserTest19);
|
|
#endif /* UNITTESTS */
|
|
}
|