mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
Modbus: Add support for Diagnostics (FC=8) requests and responses
This commit is contained in:
parent
bacc0dcae3
commit
f14be0de29
10 changed files with 184 additions and 6 deletions
|
@ -294,3 +294,24 @@ event modbus_read_fifo_queue_request%(c: connection, headers: ModbusHeaders, sta
|
|||
## fifos: The register values read from the FIFO queue on the device.
|
||||
event modbus_read_fifo_queue_response%(c: connection, headers: ModbusHeaders, fifos: ModbusRegisters%);
|
||||
|
||||
## Generated for a Modbus Diagnostics request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## subfunction: The subfunction for the diagnostics request.
|
||||
##
|
||||
## data: The data passed in the diagnostics request.
|
||||
event modbus_diagnostics_request%(c: connection, headers: ModbusHeaders, subfunction: count, data: string%);
|
||||
|
||||
## Generated for a Modbus Diagnostics response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## subfunction: The subfunction for the diagnostics response.
|
||||
##
|
||||
## data: The data passed in the diagnostics response.
|
||||
event modbus_diagnostics_response%(c: connection, headers: ModbusHeaders, subfunction: count, data: string%);
|
||||
|
|
|
@ -355,6 +355,93 @@ refine flow ModbusTCP_Flow += {
|
|||
%}
|
||||
|
||||
|
||||
# REQUEST FC=8
|
||||
function deliver_DiagnosticsRequest(header: ModbusTCP_TransportHeader, message: DiagnosticsRequest): bool
|
||||
%{
|
||||
if ( ::modbus_diagnostics_request )
|
||||
{
|
||||
auto data = to_stringval(${message.data});
|
||||
|
||||
// Data should always be a multiple of two bytes. For everything except
|
||||
// "Return Query Data (0x00)" it should be two bytes long.
|
||||
if ( data->Len() < 2 || data->Len() % 2 != 0 ||
|
||||
(${message.subfunction} != DIAGNOSTICS_RETURN_QUERY_DATA && data->Len() != 2) )
|
||||
{
|
||||
zeek::reporter->Weird("modbus_diag_invalid_request_data",
|
||||
zeek::util::fmt("%s", data->CheckString()));
|
||||
}
|
||||
|
||||
switch (${message.subfunction})
|
||||
{
|
||||
case DIAGNOSTICS_RESTART_COMMUNICATIONS_OPTION:
|
||||
// For "Restart Communications Option" it's either 0x0000 or 0xFF00.
|
||||
if ( ( data->Bytes()[0] != 0x00 && data->Bytes()[0] != 0xFF ) ||
|
||||
data->Bytes()[1] != 0x00 )
|
||||
{
|
||||
zeek::reporter->Weird("modbus_diag_invalid_request_data",
|
||||
zeek::util::fmt("%s", data->CheckString()));
|
||||
}
|
||||
break;
|
||||
case DIAGNOSTICS_RETURN_DIAGNOSTIC_REGISTER:
|
||||
case DIAGNOSTICS_FORCE_LISTEN_ONLY_MODE:
|
||||
case DIAGNOSTICS_CLEAR_COUNTERS_AND_DIAGNOSTIC_REGISTER:
|
||||
case DIAGNOSTICS_RETURN_BUS_MESSAGE_COUNT:
|
||||
case DIAGNOSTICS_RETURN_BUS_COMMUNICATION_ERROR_COUNT:
|
||||
case DIAGNOSTICS_RETURN_BUS_EXCEPTION_ERROR_COUNT:
|
||||
case DIAGNOSTICS_RETURN_SERVER_MESSAGE_COUNT:
|
||||
case DIAGNOSTICS_RETURN_SERVER_NO_RESPONSE_COUNT:
|
||||
case DIAGNOSTICS_RETURN_SERVER_NAK_COUNT:
|
||||
case DIAGNOSTICS_RETURN_SERVER_BUSY_COUNT:
|
||||
case DIAGNOSTICS_RETURN_BUS_CHARACTER_OVERRUN_COUNT:
|
||||
case DIAGNOSTICS_CLEAR_OVERRUN_COUNTER_AND_FLAG:
|
||||
// For all of these subfunctions, the data should be 0x0000.
|
||||
if ( data->Bytes()[0] != 0x00 || data->Bytes()[1] != 0x00 )
|
||||
{
|
||||
zeek::reporter->Weird("modbus_diag_invalid_request_data",
|
||||
zeek::util::fmt("%s", data->CheckString()));
|
||||
}
|
||||
break;
|
||||
|
||||
case DIAGNOSTICS_CHANGE_ASCII_INPUT_DELIMITER:
|
||||
// For "Change ASCII Input Delimiter", it should be an ascii character
|
||||
// followed by a zero.
|
||||
if ( ! isascii(data->Bytes()[0]) || data->Bytes()[1] != 0x00 )
|
||||
{
|
||||
zeek::reporter->Weird("modbus_diag_invalid_request_data",
|
||||
zeek::util::fmt("%s", data->CheckString()));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
zeek::reporter->Weird("modbus_diag_unknown_request_subfunction",
|
||||
zeek::util::fmt("%d", ${message.subfunction}));
|
||||
break;
|
||||
}
|
||||
|
||||
zeek::BifEvent::enqueue_modbus_diagnostics_request(connection()->zeek_analyzer(),
|
||||
connection()->zeek_analyzer()->Conn(),
|
||||
HeaderToVal(header),
|
||||
${message.subfunction}, to_stringval(${message.data}));
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
# RESPONSE FC=8
|
||||
function deliver_DiagnosticsResponse(header: ModbusTCP_TransportHeader, message: DiagnosticsResponse): bool
|
||||
%{
|
||||
if ( ::modbus_diagnostics_response )
|
||||
{
|
||||
zeek::BifEvent::enqueue_modbus_diagnostics_response(connection()->zeek_analyzer(),
|
||||
connection()->zeek_analyzer()->Conn(),
|
||||
HeaderToVal(header),
|
||||
${message.subfunction}, to_stringval(${message.data}));
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
|
||||
# REQUEST FC=15
|
||||
function deliver_WriteMultipleCoilsRequest(header: ModbusTCP_TransportHeader, message: WriteMultipleCoilsRequest): bool
|
||||
%{
|
||||
|
|
|
@ -15,7 +15,7 @@ enum function_codes {
|
|||
WRITE_SINGLE_COIL = 0x05,
|
||||
WRITE_SINGLE_REGISTER = 0x06,
|
||||
# READ_EXCEPTION_STATUS = 0x07,
|
||||
# DIAGNOSTICS = 0x08,
|
||||
DIAGNOSTICS = 0x08,
|
||||
# GET_COMM_EVENT_COUNTER = 0x0B,
|
||||
# GET_COMM_EVENT_LOG = 0x0C,
|
||||
WRITE_MULTIPLE_COILS = 0x0F,
|
||||
|
@ -48,6 +48,7 @@ enum function_codes {
|
|||
WRITE_SINGLE_COIL_EXCEPTION = 0x85,
|
||||
WRITE_SINGLE_REGISTER_EXCEPTION = 0x86,
|
||||
READ_EXCEPTION_STATUS_EXCEPTION = 0x87,
|
||||
DIAGNOSTICS_EXCEPTION = 0x88,
|
||||
WRITE_MULTIPLE_COILS_EXCEPTION = 0x8F,
|
||||
WRITE_MULTIPLE_REGISTERS_EXCEPTION = 0x90,
|
||||
READ_FILE_RECORD_EXCEPTION = 0x94,
|
||||
|
@ -57,6 +58,24 @@ enum function_codes {
|
|||
READ_FIFO_QUEUE_EXCEPTION = 0x98,
|
||||
};
|
||||
|
||||
enum diagnostic_subfunctions {
|
||||
DIAGNOSTICS_RETURN_QUERY_DATA = 0x00,
|
||||
DIAGNOSTICS_RESTART_COMMUNICATIONS_OPTION = 0x01,
|
||||
DIAGNOSTICS_RETURN_DIAGNOSTIC_REGISTER = 0x02,
|
||||
DIAGNOSTICS_CHANGE_ASCII_INPUT_DELIMITER = 0x03,
|
||||
DIAGNOSTICS_FORCE_LISTEN_ONLY_MODE = 0x04,
|
||||
DIAGNOSTICS_CLEAR_COUNTERS_AND_DIAGNOSTIC_REGISTER = 0x0A,
|
||||
DIAGNOSTICS_RETURN_BUS_MESSAGE_COUNT = 0x0B,
|
||||
DIAGNOSTICS_RETURN_BUS_COMMUNICATION_ERROR_COUNT = 0x0C,
|
||||
DIAGNOSTICS_RETURN_BUS_EXCEPTION_ERROR_COUNT = 0x0D,
|
||||
DIAGNOSTICS_RETURN_SERVER_MESSAGE_COUNT = 0x0E,
|
||||
DIAGNOSTICS_RETURN_SERVER_NO_RESPONSE_COUNT = 0x0F,
|
||||
DIAGNOSTICS_RETURN_SERVER_NAK_COUNT = 0x10,
|
||||
DIAGNOSTICS_RETURN_SERVER_BUSY_COUNT = 0x11,
|
||||
DIAGNOSTICS_RETURN_BUS_CHARACTER_OVERRUN_COUNT = 0x12,
|
||||
DIAGNOSTICS_CLEAR_OVERRUN_COUNTER_AND_FLAG = 0x14,
|
||||
};
|
||||
|
||||
# Main Modbus/TCP PDU
|
||||
type ModbusTCP_PDU(is_orig: bool) = record {
|
||||
header: ModbusTCP_TransportHeader;
|
||||
|
@ -86,7 +105,7 @@ type ModbusTCP_Request(header: ModbusTCP_TransportHeader) = case header.fc of {
|
|||
WRITE_SINGLE_COIL -> writeSingleCoil: WriteSingleCoilRequest(header);
|
||||
WRITE_SINGLE_REGISTER -> writeSingleRegister: WriteSingleRegisterRequest(header);
|
||||
#READ_EXCEPTION_STATUS -> readExceptionStatus: ReadExceptionStatusRequest(header);
|
||||
#DIAGNOSTICS -> diagnostics: DiagnosticsRequest(header);
|
||||
DIAGNOSTICS -> diagnostics: DiagnosticsRequest(header);
|
||||
#GET_COMM_EVENT_COUNTER -> getCommEventCounter: GetCommEventCounterRequest(header);
|
||||
#GET_COMM_EVENT_LOG -> getCommEventLog: GetCommEventLogRequest(header);
|
||||
WRITE_MULTIPLE_COILS -> writeMultipleCoils: WriteMultipleCoilsRequest(header);
|
||||
|
@ -113,7 +132,7 @@ type ModbusTCP_Response(header: ModbusTCP_TransportHeader) = case header.fc of {
|
|||
WRITE_SINGLE_COIL -> writeSingleCoil: WriteSingleCoilResponse(header);
|
||||
WRITE_SINGLE_REGISTER -> writeSingleRegister: WriteSingleRegisterResponse(header);
|
||||
#READ_EXCEPTION_STATUS -> readExceptionStatus: ReadExceptionStatusResponse(header);
|
||||
#DIAGNOSTICS -> diagnostics: DiagnosticsResponse(header);
|
||||
DIAGNOSTICS -> diagnostics: DiagnosticsResponse(header);
|
||||
#GET_COMM_EVENT_COUNTER -> getCommEventCounter: GetCommEventCounterResponse(header);
|
||||
#GET_COMM_EVENT_LOG -> getCommEventLog: GetCommEventLogResponse(header);
|
||||
WRITE_MULTIPLE_COILS -> writeMultipleCoils: WriteMultipleCoilsResponse(header);
|
||||
|
@ -134,6 +153,7 @@ type ModbusTCP_Response(header: ModbusTCP_TransportHeader) = case header.fc of {
|
|||
WRITE_SINGLE_COIL_EXCEPTION -> writeCoilException: Exception(header);
|
||||
WRITE_SINGLE_REGISTER_EXCEPTION -> writeSingleRegisterException: Exception(header);
|
||||
READ_EXCEPTION_STATUS_EXCEPTION -> readExceptionStatusException: Exception(header);
|
||||
DIAGNOSTICS_EXCEPTION -> diagnosticsException: Exception(header);
|
||||
WRITE_MULTIPLE_COILS_EXCEPTION -> forceMultipleCoilsException: Exception(header);
|
||||
READ_FILE_RECORD_EXCEPTION -> readGeneralReferenceException: Exception(header);
|
||||
WRITE_FILE_RECORD_EXCEPTION -> writeGeneralReferenceException: Exception(header);
|
||||
|
@ -247,6 +267,22 @@ type WriteSingleRegisterResponse(header: ModbusTCP_TransportHeader) = record {
|
|||
deliver: bool = $context.flow.deliver_WriteSingleRegisterResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=8
|
||||
type DiagnosticsRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
subfunction: uint16;
|
||||
data: bytestring &restofdata;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_DiagnosticsRequest(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=8
|
||||
type DiagnosticsResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
subfunction: uint16;
|
||||
data: bytestring &restofdata;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_DiagnosticsResponse(header, this);
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=15
|
||||
type WriteMultipleCoilsRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||
5 of 28 events triggered by trace
|
||||
5 of 30 events triggered by trace
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||
5 of 28 events triggered by trace
|
||||
5 of 30 events triggered by trace
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||
18 of 28 events triggered by trace
|
||||
20 of 30 events triggered by trace
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||
3 modbus_diagnostics_request, [orig_h=10.0.0.57, orig_p=2578/tcp, resp_h=10.0.0.3, resp_p=502/tcp], [tid=0, pid=0, uid=10, function_code=8], 1, \x00\x00
|
||||
2 modbus_diagnostics_request, [orig_h=10.0.0.57, orig_p=2578/tcp, resp_h=10.0.0.3, resp_p=502/tcp], [tid=0, pid=0, uid=10, function_code=8], 10, \x00\x00
|
||||
3 modbus_diagnostics_request, [orig_h=10.0.0.57, orig_p=2578/tcp, resp_h=10.0.0.3, resp_p=502/tcp], [tid=0, pid=0, uid=10, function_code=8], 4, \x00\x00
|
||||
1 modbus_diagnostics_request, [orig_h=192.168.66.235, orig_p=2582/tcp, resp_h=166.161.16.230, resp_p=502/tcp], [tid=0, pid=0, uid=1, function_code=8], 0, \x00\x00
|
||||
2 modbus_diagnostics_response, [orig_h=10.0.0.57, orig_p=2578/tcp, resp_h=10.0.0.3, resp_p=502/tcp], [tid=0, pid=0, uid=10, function_code=8], 1, \x00\x00
|
||||
2 modbus_diagnostics_response, [orig_h=10.0.0.57, orig_p=2578/tcp, resp_h=10.0.0.3, resp_p=502/tcp], [tid=0, pid=0, uid=10, function_code=8], 10, \x00\x00
|
||||
1 modbus_diagnostics_response, [orig_h=192.168.66.235, orig_p=2582/tcp, resp_h=166.161.16.230, resp_p=502/tcp], [tid=0, pid=0, uid=1, function_code=8], 0, \x00\x00
|
||||
4 modbus_exception, [orig_h=10.0.0.57, orig_p=2578/tcp, resp_h=10.0.0.3, resp_p=502/tcp], [tid=0, pid=0, uid=10, function_code=136], 11
|
||||
8 modbus_exception, [orig_h=192.168.66.235, orig_p=2582/tcp, resp_h=166.161.16.230, resp_p=502/tcp], [tid=0, pid=0, uid=1, function_code=129], 2
|
||||
1 modbus_exception, [orig_h=192.168.66.235, orig_p=2582/tcp, resp_h=166.161.16.230, resp_p=502/tcp], [tid=0, pid=0, uid=1, function_code=129], 3
|
||||
1 modbus_exception, [orig_h=192.168.66.235, orig_p=2582/tcp, resp_h=166.161.16.230, resp_p=502/tcp], [tid=0, pid=0, uid=1, function_code=130], 3
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||
#separator \x09
|
||||
#set_separator ,
|
||||
#empty_field (empty)
|
||||
#unset_field -
|
||||
#path weird
|
||||
#open XXXX-XX-XX-XX-XX-XX
|
||||
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p name addl notice peer source
|
||||
#types time string addr port addr port string string bool string string
|
||||
XXXXXXXXXX.XXXXXX - - - - - modbus_diag_unknown_request_subfunction 0 F zeek -
|
||||
#close XXXX-XX-XX-XX-XX-XX
|
|
@ -8,9 +8,13 @@
|
|||
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p tid unit func pdu_type exception
|
||||
#types time string addr port addr port count count string string string
|
||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 10.0.0.57 2578 10.0.0.3 502 0 10 DIAGNOSTICS REQ -
|
||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 10.0.0.57 2578 10.0.0.3 502 0 10 unknown-136 RESP GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND
|
||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 10.0.0.57 2578 10.0.0.3 502 0 10 DIAGNOSTICS REQ -
|
||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 10.0.0.57 2578 10.0.0.3 502 0 10 unknown-136 RESP GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND
|
||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 10.0.0.57 2578 10.0.0.3 502 0 10 DIAGNOSTICS REQ -
|
||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 10.0.0.57 2578 10.0.0.3 502 0 10 unknown-136 RESP GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND
|
||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 10.0.0.57 2578 10.0.0.3 502 0 10 DIAGNOSTICS REQ -
|
||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 10.0.0.57 2578 10.0.0.3 502 0 10 unknown-136 RESP GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND
|
||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 10.0.0.57 2578 10.0.0.3 502 0 10 DIAGNOSTICS REQ -
|
||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 10.0.0.57 2578 10.0.0.3 502 0 10 DIAGNOSTICS RESP -
|
||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 10.0.0.57 2578 10.0.0.3 502 0 10 DIAGNOSTICS REQ -
|
||||
|
|
|
@ -6,9 +6,11 @@
|
|||
# @TEST-EXEC: echo `cat covered` of `cat total` events triggered by trace >coverage
|
||||
# @TEST-EXEC: btest-diff coverage
|
||||
# @TEST-EXEC: btest-diff conn.log
|
||||
# @TEST-EXEC: btest-diff weird.log
|
||||
|
||||
@load base/protocols/modbus
|
||||
@load base/protocols/conn
|
||||
@load base/frameworks/notice/weird
|
||||
|
||||
redef DPD::ignore_violations_after = 1;
|
||||
|
||||
|
@ -152,3 +154,12 @@ event modbus_read_fifo_queue_response(c: connection, headers: ModbusHeaders, fif
|
|||
print "modbus_read_fifo_queue_response", c$id, headers, fifos;
|
||||
}
|
||||
|
||||
event modbus_diagnostics_request(c: connection, headers: ModbusHeaders, subfunction: count, data: string)
|
||||
{
|
||||
print "modbus_diagnostics_request", c$id, headers, subfunction, data;
|
||||
}
|
||||
|
||||
event modbus_diagnostics_response(c: connection, headers: ModbusHeaders, subfunction: count, data: string)
|
||||
{
|
||||
print "modbus_diagnostics_response", c$id, headers, subfunction, data;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue