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.
pull/3288/head
David DIALLO 8 years ago committed by Victor Julien
parent 71742ed52b
commit c2236ea2b3

@ -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 <value>
modbus: unit <value>, function <value>
modbus: unit <value>, function <value>, subfunction <value>
modbus: unit <value>, function [!] <assigned | unassigned | public | user | reserved | all>
modbus: unit <value>, access <read | write>
modbus: unit <value>, access read <discretes | coils | input | holding>
modbus: unit <value>, access read <discretes | coils | input | holding>, address <value>
modbus: unit <value>, access write < coils | holding>
modbus: unit <value>, access write < coils | holding>, address <value>
modbus: unit <value>, access write < coils | holding>, address <value>, value <value>
With _<value>_ 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 :

File diff suppressed because it is too large Load Diff

@ -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;

File diff suppressed because it is too large Load Diff

@ -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 */
}

@ -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;

Loading…
Cancel
Save