From 009efbcb27f9b8271d0b1a1a5873087e639ef7a0 Mon Sep 17 00:00:00 2001 From: Seth Hall Date: Mon, 17 Sep 2012 09:19:52 -0400 Subject: [PATCH] Major revisions to Modbus analyzer support (not quite done yet). - Renamed many data structures to align with most recent standard. - Reworked modbus events to make them more canonically "Bro". - Converted the Modbus analyzer to a simpler style for easier maintenance. - Modbus coil related events still don't work (I haven't finished the function for converting the data structures). - Modbus file record events remain incomplete. --- scripts/base/init-bare.bro | 18 +- scripts/base/protocols/modbus/__load__.bro | 1 + scripts/base/protocols/modbus/consts.bro | 67 ++ scripts/base/protocols/modbus/main.bro | 18 +- src/Modbus.cc | 6 +- src/Modbus.h | 66 +- src/event.bif | 297 +++--- src/modbus-analyzer.pac | 1046 ++++++++++---------- src/modbus-protocol.pac | 695 ++++++------- src/modbus.pac | 16 + src/types.bif | 7 + 11 files changed, 1098 insertions(+), 1139 deletions(-) create mode 100644 scripts/base/protocols/modbus/consts.bro diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index 3dd28ee854..cd2e2dabc6 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -53,14 +53,6 @@ type string_vec: vector of string; ## then remove this alias. type addr_vec: vector of addr; - -## A vector of integers. -## -## .. todo:: We need this type definition only for declaring builtin functions via -## ``bifcl``. We should extend ``bifcl`` to understand composite types directly and -## then remove this alias. -type int_vec:vector of int; - ## A table of strings indexed by strings. ## ## .. todo:: We need this type definition only for declaring builtin functions via @@ -2465,6 +2457,16 @@ type bittorrent_benc_dir: table[string] of bittorrent_benc_value; ## bt_tracker_response_not_ok type bt_tracker_headers: table[string] of string; +type ModbusCoils: vector of bool; +type ModbusRegisters: vector of count; + +type ModbusHeaders: record { + tid: count; + pid: count; + uid: count; + function_code: count; +}; + module SOCKS; export { ## This record is for a SOCKS client or server to provide either a diff --git a/scripts/base/protocols/modbus/__load__.bro b/scripts/base/protocols/modbus/__load__.bro index d551be57d3..0098b81a7a 100644 --- a/scripts/base/protocols/modbus/__load__.bro +++ b/scripts/base/protocols/modbus/__load__.bro @@ -1 +1,2 @@ +@load ./consts @load ./main \ No newline at end of file diff --git a/scripts/base/protocols/modbus/consts.bro b/scripts/base/protocols/modbus/consts.bro new file mode 100644 index 0000000000..ddad5a4787 --- /dev/null +++ b/scripts/base/protocols/modbus/consts.bro @@ -0,0 +1,67 @@ + +module Modbus; + +export { + ## Standard defined Modbus function codes. + const function_codes = { + [0x01] = "READ_COILS", + [0x02] = "READ_DISCRETE_INPUTS", + [0x03] = "READ_HOLDING_REGISTERS", + [0x04] = "READ_INPUT_REGISTERS", + [0x05] = "WRITE_SINGLE_COIL", + [0x06] = "WRITE_SINGLE_REGISTER", + [0x07] = "READ_EXCEPTION_STATUS", + [0x08] = "DIAGNOSTICS", + [0x0B] = "GET_COMM_EVENT_COUNTER", + [0x0C] = "GET_COMM_EVENT_LOG", + [0x0F] = "WRITE_MULTIPLE_COILS", + [0x10] = "WRITE_MULTIPLE_REGISTERS", + [0x11] = "REPORT_SLAVE_ID", + [0x14] = "READ_FILE_RECORD", + [0x15] = "WRITE_FILE_RECORD", + [0x16] = "MASK_WRITE_REGISTER", + [0x17] = "READ_WRITE_MULTIPLE_REGISTERS", + [0x18] = "READ_FIFO_QUEUE", + [0x2B] = "ENCAP_INTERFACE_TRANSPORT", + + # Machine/vendor/network specific functions + [0x09] = "PROGRAM_484", + [0x0A] = "POLL_484", + [0x0D] = "PROGRAM_584_984", + [0x0E] = "POLL_584_984", + [0x12] = "PROGRAM_884_U84", + [0x13] = "RESET_COMM_LINK_884_U84", + [0x28] = "PROGRAM_CONCEPT", + [0x7D] = "FIRMWARE_REPLACEMENT", + [0x7E] = "PROGRAM_584_984_2", + [0x7F] = "REPORT_LOCAL_ADDRESS", + + # Exceptions + [0x81] = "READ_COILS_EXCEPTION", + [0x82] = "READ_DISCRETE_INPUTS_EXCEPTION", + [0x83] = "READ_HOLDING_REGISTERS_EXCEPTION", + [0x84] = "READ_INPUT_REGISTERS_EXCEPTION", + [0x85] = "WRITE_SINGLE_COIL_EXCEPTION", + [0x86] = "WRITE_SINGLE_REGISTER_EXCEPTION", + [0x87] = "READ_EXCEPTION_STATUS_EXCEPTION", + [0x8F] = "WRITE_MULTIPLE_COILS_EXCEPTION", + [0x90] = "WRITE_MULTIPLE_REGISTERS_EXCEPTION", + [0x94] = "READ_FILE_RECORD_EXCEPTION", + [0x95] = "WRITE_FILE_RECORD_EXCEPTION", + [0x96] = "MASK_WRITE_REGISTER_EXCEPTION", + [0x97] = "READ_WRITE_MULTIPLE_REGISTERS_EXCEPTION", + [0x98] = "READ_FIFO_QUEUE_EXCEPTION", + } &default=function(i: count):string { return fmt("unknown-%d", i); } &redef; + + const exception_codes = { + [0x01] = "ILLEGAL_FUNCTION", + [0x02] = "ILLEGAL_DATA_ADDRESS", + [0x03] = "ILLEGAL_DATA_VALUE", + [0x04] = "SLAVE_DEVICE_FAILURE", + [0x05] = "ACKNOWLEDGE", + [0x06] = "SLAVE_DEVICE_BUSY", + [0x08] = "MEMORY_PARITY_ERROR", + [0x0A] = "GATEWAY_PATH_UNAVAILABLE", + [0x0B] = "GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND", + } &default=function(i: count):string { return fmt("unknown-%d", i); } &redef; +} \ No newline at end of file diff --git a/scripts/base/protocols/modbus/main.bro b/scripts/base/protocols/modbus/main.bro index 831ffd64cf..3c3d1db97e 100644 --- a/scripts/base/protocols/modbus/main.bro +++ b/scripts/base/protocols/modbus/main.bro @@ -1,12 +1,26 @@ -##! Base Modbus analysis script. For now it does not do anything else than -##! activating the analyzer for connections on Modbus port 502/tcp. +##! Base Modbus analysis script. module Modbus; export { + } # Configure DPD and the packet filter. redef capture_filters += { ["modbus"] = "tcp port 502" }; redef dpd_config += { [ANALYZER_MODBUS] = [$ports = set(502/tcp)] }; redef likely_server_ports += { 502/tcp }; + + +event modbus_exception(c: connection, header: ModbusHeaders, code: count) + { + print fmt("%.6f %s There was an exception: %s", network_time(), c$id, exception_codes[code]); + } + +event modbus_message(c: connection, header: ModbusHeaders, is_orig: bool) + { + #if ( function_codes[header$function_code] in set("READ_MULTIPLE_REGISTERS", "READ_WRITE_REGISTERS", "WRITE_MULTIPLE_REGISTERS") ) + # return; + + print fmt("%.6f %s %s: %s", network_time(), c$id, is_orig ? "request":"response", function_codes[header$function_code]); + } diff --git a/src/Modbus.cc b/src/Modbus.cc index e251e38d3e..278eb91e57 100644 --- a/src/Modbus.cc +++ b/src/Modbus.cc @@ -33,9 +33,9 @@ void ModbusTCP_Analyzer::Undelivered(int seq, int len, bool orig) interp->NewGap(orig, len); } -void ModbusTCP_Analyzer::EndpointEOF(TCP_Reassembler* endp) +void ModbusTCP_Analyzer::EndpointEOF(bool is_orig) { - TCP_ApplicationAnalyzer::EndpointEOF(endp); - interp->FlowEOF(endp->IsOrig()); + TCP_ApplicationAnalyzer::EndpointEOF(is_orig); + interp->FlowEOF(is_orig); } diff --git a/src/Modbus.h b/src/Modbus.h index a60b0006b4..5e3294d91c 100644 --- a/src/Modbus.h +++ b/src/Modbus.h @@ -1,9 +1,7 @@ - #ifndef MODBUS_H #define MODBUS_H #include "TCP.h" - #include "modbus_pac.h" class ModbusTCP_Analyzer : public TCP_ApplicationAnalyzer { @@ -15,7 +13,7 @@ public: virtual void DeliverStream(int len, const u_char* data, bool orig); virtual void Undelivered(int seq, int len, bool orig); - virtual void EndpointEOF(TCP_Reassembler* endp); + virtual void EndpointEOF(bool is_orig); static Analyzer* InstantiateAnalyzer(Connection* conn) { return new ModbusTCP_Analyzer(conn); } @@ -23,40 +21,34 @@ public: // Put event names in this function static bool Available() { - return modbus_read_coils_request - || modbus_read_coils_response - || modbus_read_input_discretes_request - || modbus_read_input_discretes_response - || modbus_read_multi_request - || modbus_read_multi_response - || modbus_read_input_request - || modbus_read_input_response - || modbus_write_single_request - || modbus_write_single_response - || modbus_write_coil_request - || modbus_write_coil_response - || modbus_force_coils_request - || modbus_force_coils_response - || modbus_read_reference_request - || modbus_read_reference_response - || modbus_read_single_reference_request - || modbus_read_single_reference_response - || modbus_write_reference_request - || modbus_write_reference_response - || modbus_write_single_reference - || modbus_write_multi_request - || modbus_write_multi_response - || modbus_mask_write_request - || modbus_mask_write_response - || modbus_read_write_request - || modbus_read_write_response - || modbus_read_FIFO_request - || modbus_read_FIFO_response - || modbus_read_except_request - || modbus_read_except_response - || modbus_exception - || modbus_request - || modbus_response; + return modbus_message + | modbus_exception + | modbus_read_coils_request + | modbus_read_coils_response + | modbus_read_discrete_inputs_request + | modbus_read_discrete_inputs_response + | modbus_read_holding_registers_request + | modbus_read_holding_registers_response + | modbus_read_input_registers_request + | modbus_read_input_registers_response + | modbus_write_single_coil_request + | modbus_write_single_coil_response + | modbus_write_single_register_request + | modbus_write_single_register_response + | modbus_write_multiple_coils_request + | modbus_write_multiple_coils_response + | modbus_write_multiple_registers_request + | modbus_write_multiple_registers_response + | modbus_read_file_record_request + | modbus_read_file_record_response + | modbus_write_file_record_request + | modbus_write_file_record_response + | modbus_mask_write_register_request + | modbus_mask_write_register_response + | modbus_read_write_multiple_registers_request + | modbus_read_write_multiple_registers_response + | modbus_read_fifo_queue_request + | modbus_read_fifo_queue_response; } protected: diff --git a/src/event.bif b/src/event.bif index 1e1271ec65..480ffd21c5 100644 --- a/src/event.bif +++ b/src/event.bif @@ -6547,175 +6547,146 @@ event netflow_v5_header%(h: nf_v5_header%); ## .. bro:see:: netflow_v5_record event netflow_v5_record%(r: nf_v5_record%); -## Event that passes request header only +## Event for any function code whether or not it's supported. ## ## TODO-Dina: Document event. -event modbus_request%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count%); - -## Event that passes response header only -## -## TODO-Dina: Document event. -event modbus_response%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count%); - -## Event that passes modbus request function code =1 -## -## TODO-Dina: Document event. -event modbus_read_coils_request%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, ref: count, bcount: count%); - -## Event that passes modbus request function code =2 -## -## TODO-Dina: Document event. -event modbus_read_input_discretes_request%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, ref: count, bcount: count%); - -## Event that passes modbus request function code =3 -## -## TODO-Dina: Document event. -event modbus_read_multi_request%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, ref: count, wcount: count, len: count%); - -## Event that passes modbus request function code =4 -## -## TODO-Dina: Document event. -event modbus_read_input_request%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, ref: count, wcount: count, len: count%); - -## Event that passes modbus request function code =5 -## -## TODO-Dina: Document event. -event modbus_write_coil_request%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, ref: count, onOff: count, other: count%); - -## Event that passes modbus request function code =6 -## -## TODO-Dina: Document event. -event modbus_write_single_request%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, len: count, ref: count, value: count%); - -## Event that passes modbus request function code =15 -## -## TODO-Dina: Document event. -event modbus_force_coils_request%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, ref: count, bitCount: count, byteCount: count, coils: string%); - -## Event that passes modbus request function code=16 -## -## TODO-Dina: Document event. -event modbus_write_multi_request%(c: connection, is_orig: bool, t: int_vec, tid: count, pid: count, uid: count, fc: count, ref: count, wCount: count, bCount: count, len: count%); - -## Event that passes modbus request function code=20 -## -## TODO-Dina: Document event. -event modbus_read_reference_request%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, refCount: count, t: int_vec%); - -## Event that passes modbus request function code=20 for single reference -## -## TODO-Dina: Document event. -event modbus_read_single_reference_request%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, refType: count, refNumber: count, wordCount: count%); - -## Event that passes modbus request function code=21 -## -## TODO-Dina: Document event. -event modbus_write_reference_request%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, byteCount: count, t: int_vec%); - -## Event that passes modbus request/response function code=21 for single reference -## -## TODO-Dina: Document event. -event modbus_write_single_reference%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, refType: count, refNumber: count, wordCount: count, t: int_vec%); - -## Event that passes modbus request function code=22 -## -## TODO-Dina: Document event. -event modbus_mask_write_request%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, ref: count, andMask: count, orMask: count%); - -## Event that passes modbus request function code=23 -## -## TODO-Dina: Document event. -event modbus_read_write_request%(c: connection, is_orig: bool, t: int_vec, tid: count, pid: count, uid: count, fc: count, refRead: count, wcRead: count, refWrite: count, wcWrite: count, bCount: count, len: count%); - -## Event that passes modbus request function code=24 -## -## TODO-Dina: Document event. -event modbus_read_FIFO_request%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, ref: count%); - -## Event that passes modbus request function code=7 -## -## TODO-Dina: Document event. -event modbus_read_except_request%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, len: count%); - -## Event that passes modbus response function code=1 -## -## TODO-Dina: Document event. -event modbus_read_coils_response%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, bcount: count, bits: string%); - -## Event that passes modbus response function code=2 -## -## TODO-Dina: Document event. -event modbus_read_input_discretes_response%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, bcount: count, bits: string%); - -## Event that passes modbus response function code=3 -## -## TODO-Dina: Document event. -event modbus_read_multi_response%(c: connection, is_orig: bool, t: int_vec, tid: count, pid: count, uid: count, fc: count, bCount: count, len: count%); - -## Event that passes modbus response function code=4 -## -## TODO-Dina: Document event. -event modbus_read_input_response%(c: connection, is_orig: bool, t: int_vec, tid: count, pid: count, uid: count, fc: count, bCount: count, len: count%); - -## Event that passes modbus request function code =5 -## -## TODO-Dina: Document event. -event modbus_write_coil_response%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, ref: count, onOff: count, other: count%); - -## Event that passes modbus response function code=6 -## -## TODO-Dina: Document event. -event modbus_write_single_response%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, len: count, ref: count, value: count%); - -## Event that passes modbus response function code=15 -## -## TODO-Dina: Document event. -event modbus_force_coils_response%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, ref: count, bitCount: count%); - -## Event that passes modbus response function code=16 -## -## TODO-Dina: Document event. -event modbus_write_multi_response%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, ref: count, wcount: count, len: count%); - -## Event that passes modbus response function code=20 -## -## TODO-Dina: Document event. -event modbus_read_reference_response%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, byteCount: count, t: int_vec%); - -## Event that passes modbus response function code=20 for single reference -## -## TODO-Dina: Document event. -event modbus_read_single_reference_response%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, byteCount: count, refType: count, t: int_vec%); - -## Event that passes modbus response function code=21 -## -## TODO-Dina: Document event. -event modbus_write_reference_response%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, byteCount: count, t: int_vec%); - -## Event that passes modbus response function code=22 -## -## TODO-Dina: Document event. -event modbus_mask_write_response%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, ref: count, andMask: count, orMask: count%); - -## Event that passes modbus response function code=23 -## -## TODO-Dina: Document event. -event modbus_read_write_response%(c: connection, is_orig: bool, t: int_vec, tid: count, pid: count, uid: count, fc: count, bCount: count, len: count%); - -## Event that passes modbus response function code=24 -## -## TODO-Dina: Document event. -event modbus_read_FIFO_response%(c: connection, is_orig: bool, t: int_vec, tid: count, pid: count, uid: count, fc: count, bcount: count%); - -## Event that passes modbus response function code=7 -## -## TODO-Dina: Document event. -event modbus_read_except_response%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, status: count, len: count%); +event modbus_message%(c: connection, header: ModbusHeaders, is_orig: bool%); ## Event that parses modbus exception ## ## TODO-Dina: Document event. -event modbus_exception%(c: connection, is_orig: bool, tid: count, pid: count, uid: count, fc: count, code: count%); +event modbus_exception%(c: connection, header: ModbusHeaders, code: count%); + +## Event that passes modbus request function code=1 +## +## TODO-Dina: Document event. +event modbus_read_coils_request%(c: connection, header: ModbusHeaders, start_address: count, quantity: count%); + +## Event that passes modbus response function code=1 +## +## TODO-Dina: Document event. +event modbus_read_coils_response%(c: connection, header: ModbusHeaders, coils: ModbusCoils%); + +## Event that passes modbus request function code=2 +## +## TODO-Dina: Document event. +event modbus_read_discrete_inputs_request%(c: connection, header: ModbusHeaders, start_address: count, quantity: count%); + +## Event that passes modbus response function code=2 +## +## TODO-Dina: Document event. +event modbus_read_discrete_inputs_response%(c: connection, header: ModbusHeaders, coils: ModbusCoils%); + +## Event that passes modbus request function code=3 +## +## TODO-Dina: Document event. +event modbus_read_holding_registers_request%(c: connection, header: ModbusHeaders, start_address: count, quantity: count%); + +## Event that passes modbus response function code=3 +## +## TODO-Dina: Document event. +event modbus_read_holding_registers_response%(c: connection, header: ModbusHeaders, registers: ModbusRegisters%); + +## Event that passes modbus request function code =4 +## +## TODO-Dina: Document event. +event modbus_read_input_registers_request%(c: connection, header: ModbusHeaders, start_address: count, quantity: count%); + +## Event that passes modbus response function code=4 +## +## TODO-Dina: Document event. +event modbus_read_input_registers_response%(c: connection, header: ModbusHeaders, registers: ModbusRegisters%); + +## Event that passes modbus request function code=5 +## +## TODO-Dina: Document event. +event modbus_write_single_coil_request%(c: connection, header: ModbusHeaders, start_address: count, on_off: count, other: count%); + +## Event that passes modbus request function code=5 +## +## TODO-Dina: Document event. +event modbus_write_single_coil_response%(c: connection, header: ModbusHeaders, start_address: count, on_off: count, other: count%); + +## Event that passes modbus request function code=6 +## +## TODO-Dina: Document event. +event modbus_write_single_register_request%(c: connection, header: ModbusHeaders, start_address: count, value: count%); + +## Event that passes modbus response function code=6 +## +## TODO-Dina: Document event. +event modbus_write_single_register_response%(c: connection, header: ModbusHeaders, start_address: count, value: count%); + + +## Event that passes modbus request function code=15 +## +## TODO-Dina: Document event. +event modbus_write_multiple_coils_request%(c: connection, header: ModbusHeaders, start_address: count, coils: ModbusCoils%); + +## Event that passes modbus response function code=15 +## +## TODO-Dina: Document event. +event modbus_write_multiple_coils_response%(c: connection, header: ModbusHeaders, start_address: count, quantity: count%); + +## Event that passes modbus request function code=16 +## +## TODO-Dina: Document event. +event modbus_write_multiple_registers_request%(c: connection, header: ModbusHeaders, start_address: count, registers: ModbusRegisters%); + +## Event that passes modbus response function code=16 +## +## TODO-Dina: Document event. +event modbus_write_multiple_registers_response%(c: connection, header: ModbusHeaders, start_address: count, quantity: count%); + +## Event that passes modbus request function code=20 +## +## TODO-Dina: Document event. +event modbus_read_file_record_request%(c: connection, header: ModbusHeaders%); + +## Event that passes modbus response function code=20 +## +## TODO-Dina: Document event. +event modbus_read_file_record_response%(c: connection, header: ModbusHeaders%); + +## Event that passes modbus request function code=21 +## +## TODO-Dina: Document event. +event modbus_write_file_record_request%(c: connection, header: ModbusHeaders%); + +## Event that passes modbus response function code=21 +## +## TODO-Dina: Document event. +event modbus_write_file_record_response%(c: connection, header: ModbusHeaders%); + +## Event that passes modbus request function code=22 +## +## TODO-Dina: Document event. +event modbus_mask_write_register_request%(c: connection, header: ModbusHeaders, start_address: count, and_mask: count, or_mask: count%); + +## Event that passes modbus response function code=22 +## +## TODO-Dina: Document event. +event modbus_mask_write_register_response%(c: connection, header: ModbusHeaders, start_address: count, and_mask: count, or_mask: count%); + +## Event that passes modbus request function code=23 +## +## TODO-Dina: Document event. +event modbus_read_write_multiple_registers_request%(c: connection, header: ModbusHeaders, read_start_address: count, read_quantity: count, write_start_address: count, write_registers: ModbusRegisters%); + +## Event that passes modbus response function code=23 +## +## TODO-Dina: Document event. +event modbus_read_write_multiple_registers_response%(c: connection, header: ModbusHeaders, written_registers: ModbusRegisters%); + +## Event that passes modbus request function code=24 +## +## TODO-Dina: Document event. +event modbus_read_fifo_queue_request%(c: connection, header: ModbusHeaders, start_address: count%); + +## Event that passes modbus response function code=24 +## +## TODO-Dina: Document event. +event modbus_read_fifo_queue_response%(c: connection, header: ModbusHeaders, fifos: ModbusRegisters%); ## Raised for informational messages reported via Bro's reporter framework. Such ## messages may be generated internally by the event engine and also by other diff --git a/src/modbus-analyzer.pac b/src/modbus-analyzer.pac index 782285b811..fd3e45486c 100644 --- a/src/modbus-analyzer.pac +++ b/src/modbus-analyzer.pac @@ -7,581 +7,529 @@ # http://www.simplymodbus.ca/faq.htm # -flow ModbusTCP_Flow(is_orig: bool) -{ - flowunit = ModbusTCP_PDU(is_orig) withcontext (connection, this); +%header{ + VectorVal* bytestring_to_coils(bytestring coils, uint quantity); + RecordVal* HeaderToBro(ModbusTCP_TransportHeader *header); + %} - # Parse only headers for request and response. - function deliver_message(tid: uint16, pid: uint16, uid: uint8, fc: uint8, flag: int): bool +%code{ + VectorVal* bytestring_to_coils(bytestring coils, uint quantity) + { + VectorVal* modbus_coils = new VectorVal(BifType::Vector::ModbusCoils); + + return modbus_coils; + } + + RecordVal* HeaderToBro(ModbusTCP_TransportHeader *header) + { + RecordVal* modbus_header = new RecordVal(BifType::Record::ModbusHeaders); + modbus_header->Assign(0, new Val(header->tid(), TYPE_COUNT)); + modbus_header->Assign(1, new Val(header->pid(), TYPE_COUNT)); + modbus_header->Assign(2, new Val(header->uid(), TYPE_COUNT)); + modbus_header->Assign(3, new Val(header->fc(), TYPE_COUNT)); + return modbus_header; + } + + %} + +refine flow ModbusTCP_Flow += { + + function deliver_message(header: ModbusTCP_TransportHeader): bool %{ - if ( flag == 1 ) + if ( ::modbus_message ) { - if ( ::modbus_request ) - { - BifEvent::generate_modbus_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc); - } - } - - else if ( flag == 2 ) - { - if ( ::modbus_response ) - { - BifEvent::generate_modbus_response(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc); - } - } - - return true; - %} - - # REQUEST FC=1 - function deliver_ReadCoilsReq(tid: uint16, pid: uint16, uid: uint8, fc: uint8, ref: uint16, bitCount: uint16): bool - %{ - if ( ::modbus_read_coils_request ) - { - BifEvent::generate_modbus_read_coils_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, ref, bitCount); - } - - return true; - %} - - # REQUEST FC=2 - function deliver_ReadInputDiscReq(tid: uint16, pid: uint16, uid: uint8, fc: uint8, ref: uint16, bitCount: uint16): bool - %{ - if ( ::modbus_read_input_discretes_request ) - { - BifEvent::generate_modbus_read_input_discretes_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, ref, bitCount); - } - - return true; - %} - - # REQUEST FC=3 - function deliver_ReadMultiRegReq(tid: uint16, pid: uint16, uid: uint8, fc: uint8, ref: uint16, wcount: uint16, flag: uint16, len: uint16): bool - %{ - if ( ::modbus_read_multi_request ) - { - BifEvent::generate_modbus_read_multi_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, ref, wcount, len); - } - - return true; - %} - - # REQUEST FC=4 - function deliver_ReadInputRegReq(tid: uint16, pid: uint16, uid: uint8, fc: uint8, ref: uint16, wcount: uint16, flag: uint16, len: uint16): bool - %{ - if ( ::modbus_read_input_request ) - { - BifEvent::generate_modbus_read_input_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, ref, wcount, len); - } - - return true; - %} - - # REQUEST FC=5 - function deliver_WriteCoilReq(tid: uint16, pid: uint16, uid: uint8, fc: uint8, ref: uint16, onOff: uint8, other: uint8): bool - %{ - if ( ::modbus_write_coil_request ) - { - BifEvent::generate_modbus_write_coil_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, ref, onOff, other); - } - - return true; - %} - - # REQUEST FC=6 - function deliver_WriteSingleRegReq(tid: uint16, pid: uint16, uid: uint8, fc: uint8, len: uint16, ref: uint16, value: uint16): bool - %{ - if ( ::modbus_write_single_request ) - { - BifEvent::generate_modbus_write_single_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, len, ref, value); - } - - return true; - %} - - # REQUEST FC=15 - function deliver_ForceMultiCoilsReq(tid: uint16, pid: uint16, uid: uint8, fc: uint8, ref: uint16, bitCount: uint16, byteCount: uint16, coils: bytestring): bool - %{ - if ( ::modbus_force_coils_request ) - { - BifEvent::generate_modbus_force_coils_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, ref, bitCount, byteCount, new StringVal(coils.length(), (const char*) coils.data())); - } - - return true; - %} - - # REQUEST FC=16 - function deliver_WriteMultiRegReq( writeMulti: WriteMultipleRegistersRequest, tid: uint16, pid: uint16, uid: uint8, fc: uint8, len: uint16): bool - %{ - VectorVal * t = new VectorVal( new VectorType(base_type(TYPE_INT))); - - for ( unsigned int i = 0; i < (${writeMulti.registers}->size()); ++i ) - { - Val* r = new Val(((*writeMulti->registers())[i]), TYPE_INT); - t->Assign(i, r, 0, OP_ASSIGN); - } - - if ( ::modbus_write_multi_request ) - { - BifEvent::generate_modbus_write_multi_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), t, tid, pid, uid, fc, ${writeMulti.referenceNumber}, ${writeMulti.wordCount}, ${writeMulti.byteCount}, len); - } - - return true; - %} - - # REQUEST FC=20 - function deliver_ReadReferenceReq(tid: uint16, pid: uint16, uid: uint8, fc: uint8, refCount: uint8, reference:Reference[]): bool - %{ - VectorVal *t = new VectorVal(new VectorType(base_type(TYPE_INT))); - - for ( unsigned int i = 0; i < (${reference}->size()); ++i ) - { - Val* r = new Val((${reference[i].refType}), TYPE_INT); - t->Assign(i, r, 0, OP_ASSIGN); - - Val* k = new Val((${reference[i].refNumber}), TYPE_INT); - t->Assign(i, k, 0, OP_ASSIGN); - - Val* l = new Val((${reference[i].wordCount}), TYPE_INT); - t->Assign(i, l, 0, OP_ASSIGN); - } - - if ( ::modbus_read_reference_request ) - { - BifEvent::generate_modbus_read_reference_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, refCount, t); - } - - return true; - %} - - # REQUEST FC=20 (to read single reference) - function deliver_ReadSingleReferenceReq(tid: uint16, pid: uint16, uid: uint8, fc: uint8, refType: uint8, refNumber: uint32, wordCount: uint16): bool - %{ - if ( ::modbus_read_single_reference_request) - { - BifEvent::generate_modbus_read_single_reference_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, refType, refNumber, wordCount); - } - - return true; - %} - - # RESPONSE FC=20 (to read single reference) - function deliver_ReadSingleReferenceRes(tid: uint16, pid: uint16, uid: uint8, fc: uint8, byteCount: uint8, refType: uint8, ref:ReferenceResponse): bool - %{ - VectorVal *t = new VectorVal(new VectorType(base_type(TYPE_INT))); - - for ( unsigned int i = 0; i < (${ref.registerValue}->size()); ++i ) - { - Val* r = new Val(((*ref->registerValue())[i]), TYPE_INT); - t->Assign(i, r, 0, OP_ASSIGN); - } - - if ( ::modbus_read_single_reference_response ) - { - BifEvent::generate_modbus_read_single_reference_response(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, byteCount, refType, t); - } - - return true; - %} - - # REQUEST FC=21 - function deliver_WriteReferenceReq(tid: uint16, pid: uint16, uid: uint8, fc: uint8, byteCount: uint8, reference:ReferenceWithData[]): bool - %{ - VectorVal* t = new VectorVal(new VectorType(base_type(TYPE_INT))); - - for ( unsigned int i = 0; i < (${reference}->size()); ++i ) - { - Val* r = new Val((${reference[i].refType}), TYPE_INT); - t->Assign(i, r, 0, OP_ASSIGN); - - Val* k = new Val((${reference[i].refNumber}), TYPE_INT); - t->Assign(i, k, 0, OP_ASSIGN); - - Val* n = new Val((${reference[i].wordCount}), TYPE_INT); - t->Assign(i, n, 0, OP_ASSIGN); - - for ( unsigned int j = 0; j < (${reference[i].registerValue}->size()); ++j ) - { - k = new Val((${reference[i].registerValue[j]}), TYPE_INT); - t->Assign(i, k, 0, OP_ASSIGN); - } - } - - if ( ::modbus_write_reference_request ) - { - BifEvent::generate_modbus_write_reference_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, byteCount, t); - } - - return true; - %} - - # RESPONSE FC=21 (to write single reference) - function deliver_WriteSingleReference(tid: uint16, pid: uint16, uid: uint8, fc: uint8, refType: uint8, refNumber: uint32, wordCount: uint16, ref:ReferenceWithData): bool - %{ - VectorVal* t = new VectorVal(new VectorType(base_type(TYPE_INT))); - - for ( unsigned int i = 0; i < (${ref.registerValue}->size()); ++i ) - { - Val* r = new Val(((*ref->registerValue())[i]), TYPE_INT); - t->Assign(i, r, 0, OP_ASSIGN); - } - - if ( ::modbus_write_single_reference) - { - BifEvent::generate_modbus_write_single_reference(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, refType, refNumber, wordCount, t); - } - - return true; - %} - - # REQUEST FC=22 - function deliver_MaskWriteRegReq(tid: uint16, pid: uint16, uid: uint8, fc: uint8, ref: uint16, andMask: uint16, orMask: uint16): bool - %{ - if ( ::modbus_mask_write_request ) - { - BifEvent::generate_modbus_mask_write_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, ref, andMask, orMask); - } - - return true; - %} - - # REQUEST FC=23 - function deliver_ReadWriteRegReq(doMulti: ReadWriteRegistersRequest, tid: uint16, pid: uint16, uid: uint8, fc: uint16, len: uint16): bool - %{ - VectorVal* t = new VectorVal(new VectorType(base_type(TYPE_INT))); - - for ( unsigned int i = 0; i < (${doMulti.registerValues})->size(); ++i ) - { - Val* r = new Val(((*doMulti->registerValues())[i]), TYPE_INT); - t->Assign(i, r, 0, OP_ASSIGN); - } - - if ( ::modbus_read_write_request ) - { - BifEvent::generate_modbus_read_write_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), t, tid, pid, uid, fc, ${doMulti.referenceNumberRead}, ${doMulti.wordCountRead}, ${doMulti.referenceNumberWrite}, ${doMulti.wordCountWrite}, ${doMulti.byteCount}, len); - } - - return true; - %} - - # REQUEST FC=24 - function deliver_ReadFIFOReq(tid: uint16, pid: uint16, uid: uint8, fc: uint8, ref: uint16): bool - %{ - if ( ::modbus_read_FIFO_request ) - { - BifEvent::generate_modbus_read_FIFO_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, ref); - } - - return true; - %} - - # RESPONSE FC=1 - function deliver_ReadCoilsRes(tid: uint16, pid: uint16, uid: uint8, fc: uint8, bcount: uint8, bits: bytestring): bool - %{ - if ( ::modbus_read_coils_response ) - { - BifEvent::generate_modbus_read_coils_response(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, bcount, new StringVal(bits.length(), (const char*) bits.data())); - } - - return true; - %} - - # RESPONSE FC=2 - function deliver_ReadInputDiscRes(tid: uint16, pid: uint16, uid: uint8, fc: uint8, bcount: uint8, bits: bytestring): bool - %{ - if ( ::modbus_read_input_discretes_response ) - { - BifEvent::generate_modbus_read_input_discretes_response(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, bcount, new StringVal(bits.length(), (const char*) bits.data())); - } - - return true; - %} - - # RESPONSE FC=3 - function deliver_ReadMultiRegRes( doMulti: ReadMultipleRegistersResponse, tid: uint16, pid: uint16, uid: uint8, fc: uint16, len: uint16): bool - %{ - VectorVal* t = new VectorVal(new VectorType(base_type(TYPE_INT))); - - for ( unsigned int i = 0; i < (${doMulti.registers})->size(); ++i ) - { - Val* r = new Val(((*doMulti->registers())[i]), TYPE_INT); - t->Assign(i, r, 0, OP_ASSIGN); - } - - if ( ::modbus_read_multi_response ) - { - BifEvent::generate_modbus_read_multi_response(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), t, tid, pid, uid, fc, ${doMulti.byteCount}, len); - } - - return true; - %} - - # RESPONSE FC=4 - function deliver_ReadInputRegRes( doMulti: ReadInputRegistersResponse, tid: uint16, pid: uint16, uid: uint8, fc: uint16, len: uint16): bool - %{ - VectorVal* t = new VectorVal(new VectorType(base_type(TYPE_INT))); - - for ( unsigned int i = 0; i < (${doMulti.registers})->size(); ++i ) - { - Val* r = new Val(((*doMulti->registers())[i]), TYPE_INT); - t->Assign(i, r, 0, OP_ASSIGN); - } - - if ( ::modbus_read_input_response ) - { - BifEvent::generate_modbus_read_input_response(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), t, tid, pid, uid, fc, ${doMulti.byteCount}, len); - } - - return true; - %} - - # RESPONSE FC=5 - function deliver_WriteCoilRes(tid: uint16, pid: uint16, uid: uint8, fc: uint8, ref: uint16, onOff: uint8, other: uint8): bool - %{ - if ( ::modbus_write_coil_response ) - { - BifEvent::generate_modbus_write_coil_response(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, ref, onOff, other); - } - - return true; - %} - - # RESPONSE FC=6 - function deliver_WriteSingleRegRes(tid: uint16, pid: uint16, uid: uint8, fc: uint8, len: uint16, ref: uint16, value: uint16): bool - %{ - if ( ::modbus_write_single_response) - { - BifEvent::generate_modbus_write_single_response(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, len, ref, value); - } - - return true; - %} - - - # RESPONSE FC=15 - function deliver_ForceMultiCoilsRes(tid: uint16, pid: uint16, uid: uint8, fc: uint8, ref: uint16, bitCount: uint16): bool - %{ - if ( ::modbus_force_coils_response) - { - BifEvent::generate_modbus_force_coils_response(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, ref, bitCount); - } - - return true; - %} - - # RESPONSE FC=16 - function deliver_WriteMultiRegRes(tid: uint16, pid: uint16, uid: uint8, fc: uint8, ref: uint16, wcount: uint16, len: uint16): bool - %{ - if ( ::modbus_write_multi_response) - { - BifEvent::generate_modbus_write_multi_response(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, ref, wcount, len); - } - - return true; - %} - - # RESPONSE FC=20 - function deliver_ReadReferenceRes(tid: uint16, pid: uint16, uid: uint8, fc: uint8, byteCount: uint8, reference:ReferenceResponse[]): bool - %{ - VectorVal* t = new VectorVal(new VectorType(base_type(TYPE_INT))); - - for ( unsigned int i = 0; i < (${reference}->size()); ++i ) - { - Val* r = new Val((${reference[i].byteCount}), TYPE_INT); - t->Assign(i, r, 0, OP_ASSIGN); - - Val* k = new Val((${reference[i].refType}), TYPE_INT); - t->Assign(i, k, 0, OP_ASSIGN); - - for ( unsigned int j = 0; j<(${reference[i].registerValue}->size());++j) - { - k = new Val((${reference[i].registerValue[j]}), TYPE_INT); - t->Assign(i, k, 0, OP_ASSIGN); - } - } - - if ( ::modbus_read_reference_response ) - { - BifEvent::generate_modbus_read_reference_response(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, byteCount, t); - } - return true; - %} - - # RESPONSE FC=21 - function deliver_WriteReferenceRes(tid: uint16, pid: uint16, uid: uint8, fc: uint8, byteCount: uint8, reference:ReferenceWithData[]): bool - %{ - VectorVal* t = new VectorVal(new VectorType(base_type(TYPE_INT))); - - for ( unsigned int i = 0; i < (${reference}->size()); ++i ) - { - Val* r = new Val((${reference[i].refType}), TYPE_INT); - t->Assign(i, r, 0, OP_ASSIGN); - - Val* k = new Val((${reference[i].refNumber}), TYPE_INT); - t->Assign(i, k, 0, OP_ASSIGN); - - Val* n = new Val((${reference[i].wordCount}), TYPE_INT); - t->Assign(i, n, 0, OP_ASSIGN); - - for ( unsigned int j = 0; j<(${reference[i].registerValue}->size());++j) - { - k = new Val((${reference[i].registerValue[j]}), TYPE_INT); - t->Assign(i, k, 0, OP_ASSIGN); - } - } - - if ( ::modbus_write_reference_response ) - { - BifEvent::generate_modbus_write_reference_response(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, byteCount, t); - } - - return true; - %} - - # RESPONSE FC=22 - function deliver_MaskWriteRegRes(tid: uint16, pid: uint16, uid: uint8, fc: uint8, ref: uint16, andMask: uint16, orMask: uint16): bool - %{ - if ( ::modbus_mask_write_response ) - { - BifEvent::generate_modbus_mask_write_response(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, ref, andMask, orMask); - } - return true; - %} - - # RESPONSE FC=23 - function deliver_ReadWriteRegRes(doMulti: ReadWriteRegistersResponse, tid: uint16, pid: uint16, uid: uint8, fc: uint16, len: uint16): bool - %{ - VectorVal* t = new VectorVal(new VectorType(base_type(TYPE_INT))); - - for ( unsigned int i = 0; i < (${doMulti.registerValues})->size(); ++i ) - { - Val* r = new Val(((*doMulti->registerValues())[i]), TYPE_INT); - t->Assign(i, r, 0, OP_ASSIGN); - } - - if ( ::modbus_read_write_response ) - { - BifEvent::generate_modbus_read_write_response(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), t, tid, pid, uid, fc, ${doMulti.byteCount}, len); - } - - return true; - %} - - # RESPONSE FC=24 - function deliver_ReadFIFORes( doMulti: ReadFIFOQueueResponse, tid: uint16, pid: uint16, uid: uint8, fc: uint16): bool - %{ - VectorVal* t = new VectorVal(new VectorType(base_type(TYPE_INT))); - - for ( unsigned int i = 0; i < (${doMulti.registerData})->size(); ++i ) - { - Val* r = new Val(((*doMulti->registerData())[i]), TYPE_INT); - t->Assign(i, r, 0, OP_ASSIGN); - } - - if ( ::modbus_read_FIFO_response ) - { - BifEvent::generate_modbus_read_FIFO_response(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), t, tid, pid, uid, fc, ${doMulti.byteCount}); + BifEvent::generate_modbus_message(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + is_orig()); } return true; %} # EXCEPTION - function deliver_Exception(tid: uint16, pid: uint16, uid: uint8, fc: uint8, code: uint8): bool + function deliver_Exception(header: ModbusTCP_TransportHeader, message: Exception): bool %{ - if ( ::modbus_exception) + if ( ::modbus_exception ) { BifEvent::generate_modbus_exception(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, code); + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + ${message.code}); } return true; %} - # REQUEST FC=7 - function deliver_ReadExceptStatReq(tid: uint16, pid: uint16, uid: uint8, fc: uint8, len: uint16): bool + # REQUEST FC=1 + function deliver_ReadCoilsRequest(header: ModbusTCP_TransportHeader, message: ReadCoilsRequest): bool %{ - if ( ::modbus_read_except_request) + if ( ::modbus_read_coils_request ) { - BifEvent::generate_modbus_read_except_request(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, len); + BifEvent::generate_modbus_read_coils_request(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + ${message.start_address}, + ${message.quantity}); } return true; %} - # RESPONSE FC=7 - function deliver_ReadExceptStatRes(tid: uint16, pid: uint16, uid: uint8, fc: uint8, status: uint8, len: uint16): bool + # RESPONSE FC=1 + function deliver_ReadCoilsResponse(header: ModbusTCP_TransportHeader, message: ReadCoilsResponse): bool %{ - if ( ::modbus_read_except_response) + if ( ::modbus_read_coils_response ) { - BifEvent::generate_modbus_read_except_response(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - is_orig(), tid, pid, uid, fc, status, len); + BifEvent::generate_modbus_read_coils_response(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + bytestring_to_coils(${message.bits}, ${message.bits}.length()*8)); + } + return true; + %} + + # REQUEST FC=2 + function deliver_ReadDiscreteInputsRequest(header: ModbusTCP_TransportHeader, message: ReadDiscreteInputsRequest): bool + %{ + if ( ::modbus_read_discrete_inputs_request ) + { + BifEvent::generate_modbus_read_discrete_inputs_request(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + ${message.start_address}, ${message.quantity}); + } + + return true; + %} + + # RESPONSE FC=2 + function deliver_ReadDiscreteInputsResponse(header: ModbusTCP_TransportHeader, message: ReadDiscreteInputsResponse): bool + %{ + if ( ::modbus_read_discrete_inputs_response ) + { + BifEvent::generate_modbus_read_discrete_inputs_response(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + bytestring_to_coils(${message.bits}, ${message.bits}.length()*8)); + } + + return true; + %} + + + # REQUEST FC=3 + function deliver_ReadHoldingRegistersRequest(header: ModbusTCP_TransportHeader, message: ReadHoldingRegistersRequest): bool + %{ + if ( ::modbus_read_holding_registers_request ) + { + BifEvent::generate_modbus_read_holding_registers_request(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + ${message.start_address}, ${message.quantity}); + } + + return true; + %} + + # RESPONSE FC=3 + function deliver_ReadHoldingRegistersResponse(header: ModbusTCP_TransportHeader, message: ReadHoldingRegistersResponse): bool + %{ + if ( ::modbus_read_holding_registers_response ) + { + VectorVal* t = new VectorVal(BifType::Vector::ModbusRegisters); + for ( unsigned int i=0; i < ${message.registers}->size(); ++i ) + { + Val* r = new Val(${message.registers[i]}, TYPE_COUNT); + t->Assign(i, r, 0, OP_ASSIGN); + } + + BifEvent::generate_modbus_read_holding_registers_response(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + t); + } + + return true; + %} + + + # REQUEST FC=4 + function deliver_ReadInputRegistersRequest(header: ModbusTCP_TransportHeader, message: ReadInputRegistersRequest): bool + %{ + if ( ::modbus_read_input_registers_request ) + { + BifEvent::generate_modbus_read_input_registers_request(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + ${message.start_address}, ${message.quantity}); + } + + return true; + %} + + # RESPONSE FC=4 + function deliver_ReadInputRegistersResponse(header: ModbusTCP_TransportHeader, message: ReadInputRegistersResponse): bool + %{ + if ( ::modbus_read_input_registers_response ) + { + VectorVal* t = new VectorVal(BifType::Vector::ModbusRegisters); + for ( unsigned int i=0; i < (${message.registers})->size(); ++i ) + { + Val* r = new Val(${message.registers[i]}, TYPE_COUNT); + t->Assign(i, r, 0, OP_ASSIGN); + } + + BifEvent::generate_modbus_read_input_registers_response(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), t); + } + + return true; + %} + + + # REQUEST FC=5 + function deliver_WriteSingleCoilRequest(header: ModbusTCP_TransportHeader, message: WriteSingleCoilRequest): bool + %{ + if ( ::modbus_write_single_coil_request ) + { + BifEvent::generate_modbus_write_single_coil_request(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + ${message.start_address}, + ${message.on_off}, ${message.other}); + } + + return true; + %} + + # RESPONSE FC=5 + function deliver_WriteSingleCoilResponse(header: ModbusTCP_TransportHeader, message: WriteSingleCoilResponse): bool + %{ + if ( ::modbus_write_single_coil_response ) + { + BifEvent::generate_modbus_write_single_coil_response(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + ${message.start_address}, + ${message.on_off}, ${message.other}); + } + + return true; + %} + + + # REQUEST FC=6 + function deliver_WriteSingleRegisterRequest(header: ModbusTCP_TransportHeader, message: WriteSingleRegisterRequest): bool + %{ + if ( ::modbus_write_single_register_request ) + { + BifEvent::generate_modbus_write_single_register_request(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + ${message.start_address}, ${message.value}); + } + + return true; + %} + + # RESPONSE FC=6 + function deliver_WriteSingleRegisterResponse(header: ModbusTCP_TransportHeader, message: WriteSingleRegisterResponse): bool + %{ + if ( ::modbus_write_single_register_response ) + { + BifEvent::generate_modbus_write_single_register_response(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + ${message.start_address}, ${message.value}); + } + + return true; + %} + + + # REQUEST FC=15 + function deliver_WriteMultipleCoilsRequest(header: ModbusTCP_TransportHeader, message: WriteMultipleCoilsRequest): bool + %{ + if ( ::modbus_write_multiple_coils_request ) + { + BifEvent::generate_modbus_write_multiple_coils_request(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + ${message.start_address}, + bytestring_to_coils(${message.coils}, ${message.quantity})); + } + + return true; + %} + + # RESPONSE FC=15 + function deliver_WriteMultipleCoilsResponse(header: ModbusTCP_TransportHeader, message: WriteMultipleCoilsResponse): bool + %{ + if ( ::modbus_write_multiple_coils_response ) + { + BifEvent::generate_modbus_write_multiple_coils_response(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + ${message.start_address}, ${message.quantity}); + } + + return true; + %} + + + # REQUEST FC=16 + function deliver_WriteMultipleRegistersRequest(header: ModbusTCP_TransportHeader, message: WriteMultipleRegistersRequest): bool + %{ + if ( ::modbus_write_multiple_registers_request ) + { + VectorVal * t = new VectorVal(BifType::Vector::ModbusRegisters); + for ( unsigned int i = 0; i < (${message.registers}->size()); ++i ) + { + Val* r = new Val(${message.registers[i]}, TYPE_COUNT); + t->Assign(i, r, 0, OP_ASSIGN); + } + + BifEvent::generate_modbus_write_multiple_registers_request(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + ${message.start_address}, t); + } + + return true; + %} + + # RESPONSE FC=16 + function deliver_WriteMultipleRegistersResponse(header: ModbusTCP_TransportHeader, message: WriteMultipleRegistersResponse): bool + %{ + if ( ::modbus_write_multiple_registers_response ) + { + BifEvent::generate_modbus_write_multiple_registers_response(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + ${message.start_address}, ${message.quantity}); + } + + return true; + %} + + # REQUEST FC=20 + function deliver_ReadFileRecordRequest(header: ModbusTCP_TransportHeader, message: ReadFileRecordRequest): bool + %{ + if ( ::modbus_read_file_record_request ) + { + //TODO: this need to be a vector of some Reference Request record type + //VectorVal *t = new VectorVal(new VectorType(base_type(TYPE_COUNT))); + //for ( unsigned int i = 0; i < (${message.references}->size()); ++i ) + // { + // Val* r = new Val((${message.references[i].ref_type}), TYPE_COUNT); + // t->Assign(i, r, 0, OP_ASSIGN); + // + // Val* k = new Val((${message.references[i].file_num}), TYPE_COUNT); + // t->Assign(i, k, 0, OP_ASSIGN); + // + // Val* l = new Val((${message.references[i].record_num}), TYPE_COUNT); + // t->Assign(i, l, 0, OP_ASSIGN); + // } + + BifEvent::generate_modbus_read_file_record_request(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header)); + } + + return true; + %} + + # RESPONSE FC=20 + function deliver_ReadFileRecordResponse(header: ModbusTCP_TransportHeader, message: ReadFileRecordResponse): bool + %{ + if ( ::modbus_read_file_record_response ) + { + //VectorVal *t = new VectorVal(new VectorType(base_type(TYPE_COUNT))); + //for ( unsigned int i = 0; i < ${message.references}->size(); ++i ) + // { + // //TODO: work the reference type in here somewhere + // Val* r = new Val(${message.references[i].record_data}), TYPE_COUNT); + // t->Assign(i, r, 0, OP_ASSIGN); + // } + + BifEvent::generate_modbus_read_file_record_response(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header)); + } + + return true; + %} + + # REQUEST FC=21 + function deliver_WriteFileRecordRequest(header: ModbusTCP_TransportHeader, message: WriteFileRecordRequest): bool + %{ + if ( ::modbus_write_file_record_request ) + { + //VectorVal* t = new VectorVal(new VectorType(base_type(TYPE_COUNT))); + //for ( unsigned int i = 0; i < (${message.references}->size()); ++i ) + // { + // Val* r = new Val((${message.references[i].ref_type}), TYPE_COUNT); + // t->Assign(i, r, 0, OP_ASSIGN); +// + // Val* k = new Val((${message.references[i].file_num}), TYPE_COUNT); + // t->Assign(i, k, 0, OP_ASSIGN); +// + // Val* n = new Val((${message.references[i].record_num}), TYPE_COUNT); + // t->Assign(i, n, 0, OP_ASSIGN); +// + // for ( unsigned int j = 0; j < (${message.references[i].register_value}->size()); ++j ) + // { + // k = new Val((${message.references[i].register_value[j]}), TYPE_COUNT); + // t->Assign(i, k, 0, OP_ASSIGN); + // } + // } + + BifEvent::generate_modbus_write_file_record_request(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header)); + } + + return true; + %} + + + # RESPONSE FC=21 + function deliver_WriteFileRecordResponse(header: ModbusTCP_TransportHeader, message: WriteFileRecordResponse): bool + %{ + if ( ::modbus_write_file_record_response ) + { + //VectorVal* t = new VectorVal(new VectorType(base_type(TYPE_COUNT))); + //for ( unsigned int i = 0; i < (${messages.references}->size()); ++i ) + // { + // Val* r = new Val((${message.references[i].ref_type}), TYPE_COUNT); + // t->Assign(i, r, 0, OP_ASSIGN); +// + // Val* f = new Val((${message.references[i].file_num}), TYPE_COUNT); + // t->Assign(i, f, 0, OP_ASSIGN); +// + // Val* rn = new Val((${message.references[i].record_num}), TYPE_COUNT); + // t->Assign(i, rn, 0, OP_ASSIGN); +// + // for ( unsigned int j = 0; j<(${message.references[i].register_value}->size()); ++j ) + // { + // Val* k = new Val((${message.references[i].register_value[j]}), TYPE_COUNT); + // t->Assign(i, k, 0, OP_ASSIGN); + // } + + BifEvent::generate_modbus_write_file_record_response(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header)); + } + + return true; + %} + + # REQUEST FC=22 + function deliver_MaskWriteRegisterRequest(header: ModbusTCP_TransportHeader, message: MaskWriteRegisterRequest): bool + %{ + if ( ::modbus_mask_write_register_request ) + { + BifEvent::generate_modbus_mask_write_register_request(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + ${message.start_address}, + ${message.and_mask}, ${message.or_mask}); + } + + return true; + %} + + # RESPONSE FC=22 + function deliver_MaskWriteRegisterResponse(header: ModbusTCP_TransportHeader, message: MaskWriteRegisterResponse): bool + %{ + if ( ::modbus_mask_write_register_response ) + { + BifEvent::generate_modbus_mask_write_register_response(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + ${message.start_address}, + ${message.and_mask}, ${message.or_mask}); + } + + return true; + %} + + # REQUEST FC=23 + function deliver_ReadWriteMultipleRegistersRequest(header: ModbusTCP_TransportHeader, message: ReadWriteMultipleRegistersRequest): bool + %{ + if ( ::modbus_read_write_multiple_registers_request ) + { + VectorVal* t = new VectorVal(BifType::Vector::ModbusRegisters); + for ( unsigned int i = 0; i < ${message.write_register_values}->size(); ++i ) + { + Val* r = new Val(${message.write_register_values[i]}, TYPE_COUNT); + t->Assign(i, r, 0, OP_ASSIGN); + } + + BifEvent::generate_modbus_read_write_multiple_registers_request(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + ${message.read_start_address}, + ${message.read_quantity}, + ${message.write_start_address}, + t); + } + + return true; + %} + + # RESPONSE FC=23 + function deliver_ReadWriteMultipleRegistersResponse(header: ModbusTCP_TransportHeader, message: ReadWriteMultipleRegistersResponse): bool + %{ + if ( ::modbus_read_write_multiple_registers_response ) + { + VectorVal* t = new VectorVal(BifType::Vector::ModbusRegisters); + for ( unsigned int i = 0; i < ${message.registers}->size(); ++i ) + { + Val* r = new Val(${message.registers[i]}, TYPE_COUNT); + t->Assign(i, r, 0, OP_ASSIGN); + } + + BifEvent::generate_modbus_read_write_multiple_registers_response(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), t); + } + + return true; + %} + + # REQUEST FC=24 + function deliver_ReadFIFOQueueRequest(header: ModbusTCP_TransportHeader, message: ReadFIFOQueueRequest): bool + %{ + if ( ::modbus_read_fifo_queue_request ) + { + BifEvent::generate_modbus_read_fifo_queue_request(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), + ${message.start_address}); + } + + return true; + %} + + + # RESPONSE FC=24 + function deliver_ReadFIFOQueueResponse(header: ModbusTCP_TransportHeader, message: ReadFIFOQueueResponse): bool + %{ + if ( ::modbus_read_fifo_queue_response ) + { + VectorVal* t = new VectorVal(new VectorType(base_type(TYPE_COUNT))); + for ( unsigned int i = 0; i < (${message.register_data})->size(); ++i ) + { + Val* r = new Val(${message.register_data[i]}, TYPE_COUNT); + t->Assign(i, r, 0, OP_ASSIGN); + } + + BifEvent::generate_modbus_read_fifo_queue_response(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + HeaderToBro(header), t); } return true; diff --git a/src/modbus-protocol.pac b/src/modbus-protocol.pac index 3879b9cb66..e227357a8b 100644 --- a/src/modbus-protocol.pac +++ b/src/modbus-protocol.pac @@ -6,453 +6,394 @@ # Useful references: http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf # http://www.simplymodbus.ca/faq.htm - -analyzer ModbusTCP withcontext { - connection: ModbusTCP_Conn; - flow: ModbusTCP_Flow; -}; - -connection ModbusTCP_Conn( bro_analyzer: BroAnalyzer) { - upflow = ModbusTCP_Flow(true); - downflow = ModbusTCP_Flow(false); -}; - enum function_codes { - # Class 0 - READ_MULTIPLE_REGISTERS = 3, - WRITE_MULTIPLE_REGISTERS = 16, - - # Class 1 - READ_COILS = 1, - READ_INPUT_DISCRETES = 2, - READ_INPUT_REGISTERS = 4, - WRITE_COIL = 5, - WRITE_SINGLE_REGISTER = 6, - READ_EXCEPTION_STATUS = 7, - - # Class 2 - FORCE_MULTIPLE_COILS = 15, - READ_GENERAL_REFERENCE = 20, - WRITE_GENERAL_REFERENCE = 21, - MASK_WRITE_REGISTER = 22, - READ_WRITE_REGISTERS = 23, - READ_FIFO_QUEUE = 24, + # Standard functions + READ_COILS = 0x01, + READ_DISCRETE_INPUTS = 0x02, + READ_HOLDING_REGISTERS = 0x03, + READ_INPUT_REGISTERS = 0x04, + WRITE_SINGLE_COIL = 0x05, + WRITE_SINGLE_REGISTER = 0x06, + READ_EXCEPTION_STATUS = 0x07, + DIAGNOSTICS = 0x08, + GET_COMM_EVENT_COUNTER = 0x0B, + GET_COMM_EVENT_LOG = 0x0C, + WRITE_MULTIPLE_COILS = 0x0F, + WRITE_MULTIPLE_REGISTERS = 0x10, + REPORT_SLAVE_ID = 0x11, + READ_FILE_RECORD = 0x14, + WRITE_FILE_RECORD = 0x15, + MASK_WRITE_REGISTER = 0x16, + READ_WRITE_MULTIPLE_REGISTERS = 0x17, + READ_FIFO_QUEUE = 0x18, + ENCAP_INTERFACE_TRANSPORT = 0x2B, # Machine/vendor/network specific functions - DIAGNOSTICS = 8, - PROGRAM_484 = 9, - POLL_484 = 10, - GET_COMM_EVENT_COUNTERS = 11, - GET_COMM_EVENT_LOG = 12, - PROGRAM_584_984 = 13, - POLL_584_984 = 14, - REPORT_SLAVE = 17, - PROGRAM_884_U84 = 18, - RESET_COMM_LINK_884_U84 = 19, - PROGRAM_CONCEPT = 40, - FIRMWARE_REPLACEMENT = 125, - PROGRAM_584_984_2 = 126, - REPORT_LOCAL_ADDRESS = 127, + PROGRAM_484 = 0x09, + POLL_484 = 0x0A, + PROGRAM_584_984 = 0x0D, + POLL_584_984 = 0x0E, + PROGRAM_884_U84 = 0x12, + RESET_COMM_LINK_884_U84 = 0x13, + PROGRAM_CONCEPT = 0x28, + FIRMWARE_REPLACEMENT = 0x7D, + PROGRAM_584_984_2 = 0x7E, + REPORT_LOCAL_ADDRESS = 0x7F, - # Exceptions - READ_MULTIPLE_REGISTERS_EXCEPTION = 0x83, - WRITE_MULTIPLE_REGISTERS_EXCEPTION = 0x90, - READ_COILS_EXCEPTION = 0x81, - READ_INPUT_DISCRETES_EXCEPTION = 0x82, - READ_INPUT_REGISTERS_EXCEPTION = 0x84, - WRITE_COIL_EXCEPTION = 0x85, - WRITE_SINGLE_REGISTER_EXCEPTION = 0x86, - READ_EXCEPTION_STATUS_EXCEPTION = 0x87, - FORCE_MULTIPLE_COILS_EXCEPTION = 0x8F, - READ_GENERAL_REFERENCE_EXCEPTION = 0x94, - WRITE_GENERAL_REFERENCE_EXCEPTION = 0x95, - MASK_WRITE_REGISTER_EXCEPTION = 0x96, - READ_WRITE_REGISTERS_EXCEPTION = 0x97, - READ_FIFO_QUEUE_EXCEPTION = 0x98, + # Exceptions (not really function codes but they are used similarly) + READ_COILS_EXCEPTION = 0x81, + READ_DISCRETE_INPUTS_EXCEPTION = 0x82, + READ_HOLDING_REGISTERS_EXCEPTION = 0x83, + READ_INPUT_REGISTERS_EXCEPTION = 0x84, + WRITE_SINGLE_COIL_EXCEPTION = 0x85, + WRITE_SINGLE_REGISTER_EXCEPTION = 0x86, + READ_EXCEPTION_STATUS_EXCEPTION = 0x87, + WRITE_MULTIPLE_COILS_EXCEPTION = 0x8F, + WRITE_MULTIPLE_REGISTERS_EXCEPTION = 0x90, + READ_FILE_RECORD_EXCEPTION = 0x94, + WRITE_FILE_RECORD_EXCEPTION = 0x95, + MASK_WRITE_REGISTER_EXCEPTION = 0x96, + READ_WRITE_MULTIPLE_REGISTERS_EXCEPTION = 0x97, + READ_FIFO_QUEUE_EXCEPTION = 0x98, }; # Main Modbus/TCP PDU -type ModbusTCP_PDU(is_orig: bool) = case is_orig of { - true -> request: ModbusTCP_RequestPDU; - false -> response: ModbusTCP_ResponsePDU; -} &byteorder = bigendian; +type ModbusTCP_PDU(is_orig: bool) = record { + header: ModbusTCP_TransportHeader; + body: case is_orig of { + true -> request: ModbusTCP_Request(header); + false -> response: ModbusTCP_Response(header); + }; +} &length=header.len+6, &byteorder=bigendian, &let { + deliver: bool = $context.flow.deliver_message(header); +}; type ModbusTCP_TransportHeader = record { tid: uint16; # Transaction identifier pid: uint16; # Protocol identifier len: uint16; # Length of everyting after this field uid: uint8; # Unit identifier (previously 'slave address') - fc: uint8; # MODBUS function code (see function_codes enum) + fc: uint8; # MODBUS function code (see function_codes enum) }; -type Reference(header: ModbusTCP_TransportHeader) = record { - refType: uint8; - refNumber: uint32; - wordCount: uint16; -} &let { - deliver: bool =$context.flow.deliver_ReadSingleReferenceReq(header.tid, header.pid, header.uid, header.fc, refType, refNumber, wordCount); -}; - -type ReferenceWithData (header: ModbusTCP_TransportHeader) = record { - refType: uint8; - refNumber: uint32; - wordCount: uint16; - registerValue: uint16[wordCount] &length = 2*wordCount; # TODO: check that the array length is calculated correctly -} &let { - deliver: bool =$context.flow.deliver_WriteSingleReference(header.tid, header.pid, header.uid, header.fc, refType, refNumber, wordCount, this); -}; - -type ReferenceResponse(header: ModbusTCP_TransportHeader)= record{ - byteCount: uint8; - refType: uint8; - registerValue: uint16[wordCount]; -} &let { - wordCount : uint8 = byteCount/2; - deliver: bool =$context.flow.deliver_ReadSingleReferenceRes(header.tid, header.pid, header.uid, header.fc, byteCount, refType, this); -}; - - -type Exception(len: uint16, header: ModbusTCP_TransportHeader) = record { - code: uint8; -} &let { - deliver: bool =$context.flow.deliver_Exception(header.tid, header.pid, header.uid, header.fc, code); -}; - - -type ModbusTCP_RequestPDU = record { - header: ModbusTCP_TransportHeader; - data: case header.fc of { - - # Class 0 - READ_MULTIPLE_REGISTERS -> readMultipleRegisters: ReadMultipleRegistersRequest(header.len-2, header); - WRITE_MULTIPLE_REGISTERS -> writeMultipleRegisters: WriteMultipleRegistersRequest(header.len-2, header); - - # Class 1 - READ_COILS -> readCoils: ReadCoilsRequest(header.len-2, header); - READ_INPUT_DISCRETES -> readInputDiscretes: ReadInputDiscretesRequest(header.len-2, header); - READ_INPUT_REGISTERS -> readInputRegisters: ReadInputRegistersRequest(header.len-2, header); - WRITE_COIL -> writeCoil: WriteCoilRequest(header.len-2, header); - WRITE_SINGLE_REGISTER -> writeSingleRegister: WriteSingleRegisterRequest(header.len-2, header); - READ_EXCEPTION_STATUS -> readExceptionStatus: ReadExceptionStatusRequest(header.len-2, header); - - # Class 2 - FORCE_MULTIPLE_COILS -> forceMultipleCoils: ForceMultipleCoilsRequest(header.len-2, header); - READ_GENERAL_REFERENCE -> readGeneralreference: ReadGeneralReferenceRequest(header.len-2, header); - WRITE_GENERAL_REFERENCE -> writeGeneralReference: WriteGeneralReferenceRequest(header.len-2, header); - MASK_WRITE_REGISTER -> maskWriteRegister: MaskWriteRegisterRequest(header.len-2, header); - READ_WRITE_REGISTERS -> readWriteRegisters: ReadWriteRegistersRequest(header.len-2, header); - READ_FIFO_QUEUE -> readFIFOQueue: ReadFIFOQueueRequest(header.len-2, header); +type ModbusTCP_Request(header: ModbusTCP_TransportHeader) = case header.fc of { + READ_COILS -> readCoils: ReadCoilsRequest(header); + READ_DISCRETE_INPUTS -> readDiscreteInputs: ReadDiscreteInputsRequest(header); + READ_HOLDING_REGISTERS -> readHoldingRegisters: ReadHoldingRegistersRequest(header); + READ_INPUT_REGISTERS -> readInputRegisters: ReadInputRegistersRequest(header); + WRITE_SINGLE_COIL -> writeSingleCoil: WriteSingleCoilRequest(header); + WRITE_SINGLE_REGISTER -> writeSingleRegister: WriteSingleRegisterRequest(header); + #READ_EXCEPTION_STATUS -> readExceptionStatus: ReadExceptionStatusRequest(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); + WRITE_MULTIPLE_REGISTERS -> writeMultRegisters: WriteMultipleRegistersRequest(header); + #REPORT_SLAVE_ID + READ_FILE_RECORD -> readFileRecord: ReadFileRecordRequest(header); + WRITE_FILE_RECORD -> writeFileRecord: WriteFileRecordRequest(header); + MASK_WRITE_REGISTER -> maskWriteRegister: MaskWriteRegisterRequest(header); + READ_WRITE_MULTIPLE_REGISTERS -> readWriteMultipleRegisters: ReadWriteMultipleRegistersRequest(header); + READ_FIFO_QUEUE -> readFIFOQueue: ReadFIFOQueueRequest(header); + #ENCAP_INTERFACE_TRANSPORT # All the rest - default -> unknown: bytestring &restofdata; - }; - -} &length = (header.len + 6), - &let { - deliver: bool =$context.flow.deliver_message(header.tid, header.pid, header.uid, header.fc, 1); #1 is flag for request -}; - -# Class 0 requests - -# REQUEST FC=3 -type ReadMultipleRegistersRequest(len: uint16, header: ModbusTCP_TransportHeader) = record { - referenceNumber: uint16; - wordCount: uint16 &check(wordCount <= 125); -} &let { - deliver: bool =$context.flow.deliver_ReadMultiRegReq(header.tid, header.pid, header.uid, header.fc, referenceNumber, wordCount, 1, len); -}; - -# REQUEST FC=16 -type WriteMultipleRegistersRequest(len: uint16, header: ModbusTCP_TransportHeader) = record { - referenceNumber: uint16; - wordCount: uint16 &check(wordCount <= 100); - byteCount: uint8; - registers: uint16[wordCount] &length = byteCount; -} &let { - deliver: bool =$context.flow.deliver_WriteMultiRegReq(this, header.tid, header.pid, header.uid, header.fc, len); -}; - -# Class 1 requests - -# REQUEST FC=1 -type ReadCoilsRequest(len: uint16, header: ModbusTCP_TransportHeader) = record { - referenceNumber: uint16; - bitCount: uint16 &check(bitCount <= 2000); -} &let { - deliver: bool =$context.flow.deliver_ReadCoilsReq(header.tid, header.pid, header.uid, header.fc, referenceNumber, bitCount); -}; - -# REQUEST FC=2 -type ReadInputDiscretesRequest(len: uint16, header: ModbusTCP_TransportHeader) = record { - referenceNumber: uint16; - bitCount: uint16 &check(bitCount <= 2000); -} &let { - deliver: bool =$context.flow.deliver_ReadInputDiscReq(header.tid, header.pid, header.uid, header.fc, referenceNumber, bitCount); -}; - -# REQUEST FC=4 -type ReadInputRegistersRequest(len: uint16, header: ModbusTCP_TransportHeader) = record { - referenceNumber: uint16; - wordCount: uint16 &check(wordCount <= 125); -} &let { - deliver: bool =$context.flow.deliver_ReadInputRegReq(header.tid, header.pid, header.uid, header.fc, referenceNumber, wordCount, 1, len); -}; - -# REQUEST FC=5 -type WriteCoilRequest(len: uint16, header: ModbusTCP_TransportHeader) = record { - referenceNumber: uint16; - onOff: uint8 &check(onOff == 0x00 || onOff == 0xFF); - other: uint8 &check(other == 0x00); -} &let { - deliver: bool =$context.flow.deliver_WriteCoilReq(header.tid, header.pid, header.uid, header.fc, referenceNumber, onOff, other); -}; - -# REQUEST FC=6 -type WriteSingleRegisterRequest(len: uint16, header: ModbusTCP_TransportHeader) = record { - referenceNumber: uint16; - registerValue: uint16; -} &let { - deliver: bool =$context.flow.deliver_WriteSingleRegReq(header.tid, header.pid, header.uid, header.fc, len, referenceNumber, registerValue); -}; - -type ReadExceptionStatusRequest(len: uint16, header: ModbusTCP_TransportHeader) = record { -} &let { - deliver: bool =$context.flow.deliver_ReadExceptStatReq(header.tid, header.pid, header.uid, header.fc, len); -}; - -# Class 2 requests - -# REQUEST FC=15 -type ForceMultipleCoilsRequest(len: uint16, header: ModbusTCP_TransportHeader) = record { - referenceNumber: uint16; - bitCount: uint16 &check(bitCount <= 800); - byteCount: uint8 &check(byteCount == (bitCount + 7)/8); - coils: bytestring &length = byteCount; -} &let { - deliver: bool =$context.flow.deliver_ForceMultiCoilsReq(header.tid, header.pid, header.uid, header.fc, referenceNumber, bitCount, byteCount, coils); -}; - -# REQUEST FC=20 -type ReadGeneralReferenceRequest(len: uint16, header: ModbusTCP_TransportHeader) = record { - byteCount: uint8; - references: Reference(header)[referenceCount] &length = byteCount; -} &let { - referenceCount: uint8 = byteCount/7; - deliver: bool =$context.flow.deliver_ReadReferenceReq(header.tid, header.pid, header.uid, header.fc, referenceCount, references); -}; - -# REQUEST FC=21 -type WriteGeneralReferenceRequest(len: uint16, header: ModbusTCP_TransportHeader) = record { - byteCount: uint8; - references: ReferenceWithData(header)[] &until($input.length() == 0) &length = byteCount; -} &length = len, - &let { - deliver: bool =$context.flow.deliver_WriteReferenceReq(header.tid, header.pid, header.uid, header.fc, byteCount, references); -}; - -# REQUESTeFC=22 -type MaskWriteRegisterRequest(len: uint16, header: ModbusTCP_TransportHeader) = record { - referenceNumber: uint16; - andMask: uint16; - orMask: uint16; -} &let{ - deliver: bool =$context.flow.deliver_MaskWriteRegReq(header.tid, header.pid, header.uid, header.fc, referenceNumber, andMask, orMask); -}; - -# REQUEST FC=23 -type ReadWriteRegistersRequest(len: uint16, header: ModbusTCP_TransportHeader) = record { - referenceNumberRead: uint16; - wordCountRead: uint16 &check(wordCountRead <= 125); - referenceNumberWrite: uint16; - wordCountWrite: uint16 &check(wordCountWrite <= 100); - byteCount: uint8 &check(byteCount == 2*wordCountWrite); - registerValues: uint16[registerCount] &length = byteCount; -} &length = len, - &let{ - registerCount : uint8 = byteCount / 2; - deliver: bool =$context.flow.deliver_ReadWriteRegReq(this, header.tid, header.pid, header.uid, header.fc, len); -}; - -# REQUEST FC=24 -type ReadFIFOQueueRequest(len: uint16, header: ModbusTCP_TransportHeader) = record { - referenceNumber: uint16; -} &let{ - deliver: bool =$context.flow.deliver_ReadFIFOReq(header.tid, header.pid, header.uid, header.fc, referenceNumber); + default -> unknown: empty &restofdata; }; # Responses # -type ModbusTCP_ResponsePDU = record { - header: ModbusTCP_TransportHeader; - data: case header.fc of { - - # Class 0 - READ_MULTIPLE_REGISTERS -> readMultipleRegisters: ReadMultipleRegistersResponse(header.len-2, header); - WRITE_MULTIPLE_REGISTERS -> writeMultipleRegisters: WriteMultipleRegistersResponse(header.len-2, header); - - # Class 1 - READ_COILS -> readCoils: ReadCoilsResponse(header.len-2, header); - READ_INPUT_DISCRETES -> readInputDiscretes: ReadInputDiscretesResponse(header.len-2, header); - READ_INPUT_REGISTERS -> readInputRegisters: ReadInputRegistersResponse(header.len-2, header); - WRITE_COIL -> writeCoil: WriteCoilResponse(header.len-2, header); - WRITE_SINGLE_REGISTER -> writeSingleRegister: WriteSingleRegisterResponse(header.len-2, header); - READ_EXCEPTION_STATUS -> readExceptionStatus: ReadExceptionStatusResponse(header.len-2, header); - FORCE_MULTIPLE_COILS -> forceMultipleCoils: ForceMultipleCoilsResponse(header.len-2, header); - READ_GENERAL_REFERENCE -> readGeneralReference: ReadGeneralReferenceResponse(header.len-2, header); - WRITE_GENERAL_REFERENCE -> writeGeneralReference: WriteGeneralReferenceResponse(header.len-2, header); - MASK_WRITE_REGISTER -> maskWriteRegister: MaskWriteRegisterResponse(header.len-2, header); - READ_WRITE_REGISTERS -> readWriteRegisters: ReadWriteRegistersResponse(header.len-2, header); - READ_FIFO_QUEUE -> readFIFOQueue: ReadFIFOQueueResponse(header.len-2, header); +type ModbusTCP_Response(header: ModbusTCP_TransportHeader) = case header.fc of { + READ_COILS -> readCoils: ReadCoilsResponse(header); + READ_DISCRETE_INPUTS -> readDiscreteInputs: ReadDiscreteInputsResponse(header); + READ_HOLDING_REGISTERS -> readHoldingRegisters: ReadHoldingRegistersResponse(header); + READ_INPUT_REGISTERS -> readInputRegisters: ReadInputRegistersResponse(header); + WRITE_SINGLE_COIL -> writeSingleCoil: WriteSingleCoilResponse(header); + WRITE_SINGLE_REGISTER -> writeSingleRegister: WriteSingleRegisterResponse(header); + #READ_EXCEPTION_STATUS -> readExceptionStatus: ReadExceptionStatusResponse(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); + WRITE_MULTIPLE_REGISTERS -> writeMultRegisters: WriteMultipleRegistersResponse(header); + #REPORT_SLAVE_ID + READ_FILE_RECORD -> readFileRecord: ReadFileRecordResponse(header); + WRITE_FILE_RECORD -> writeFileRecord: WriteFileRecordResponse(header); + MASK_WRITE_REGISTER -> maskWriteRegister: MaskWriteRegisterResponse(header); + READ_WRITE_MULTIPLE_REGISTERS -> readWriteMultipleRegisters: ReadWriteMultipleRegistersResponse(header); + READ_FIFO_QUEUE -> readFIFOQueue: ReadFIFOQueueResponse(header); # Exceptions - READ_MULTIPLE_REGISTERS_EXCEPTION -> readMultipleRegistersException : Exception(header.len-2, header); - WRITE_MULTIPLE_REGISTERS_EXCEPTION -> writeMultipleRegistersException: Exception(header.len-2, header); - READ_COILS_EXCEPTION -> readCoilsException: Exception(header.len-2, header); - READ_INPUT_DISCRETES_EXCEPTION -> readInputDiscretesException: Exception(header.len-2, header); - READ_INPUT_REGISTERS_EXCEPTION -> readInputRegistersException: Exception(header.len-2, header); - WRITE_COIL_EXCEPTION -> writeCoilException: Exception(header.len-2, header); - WRITE_SINGLE_REGISTER_EXCEPTION -> writeSingleRegisterException: Exception(header.len-2, header); - READ_EXCEPTION_STATUS_EXCEPTION -> readExceptionStatusException: Exception(header.len-2, header); - FORCE_MULTIPLE_COILS_EXCEPTION -> forceMultipleCoilsException: Exception(header.len-2, header); - READ_GENERAL_REFERENCE_EXCEPTION -> readGeneralReferenceException: Exception(header.len-2, header); - WRITE_GENERAL_REFERENCE_EXCEPTION -> writeGeneralReferenceException: Exception(header.len-2, header); - MASK_WRITE_REGISTER_EXCEPTION -> maskWriteRegisterException: Exception(header.len-2, header); - READ_WRITE_REGISTERS_EXCEPTION -> readWriteRegistersException: Exception(header.len-2, header); - READ_FIFO_QUEUE_EXCEPTION -> readFIFOQueueException: Exception(header.len-2, header); + READ_HOLDING_REGISTERS_EXCEPTION -> readHoldingRegistersException: Exception(header); + WRITE_MULTIPLE_REGISTERS_EXCEPTION -> writeMultRegistersException: Exception(header); + READ_COILS_EXCEPTION -> readCoilsException: Exception(header); + READ_DISCRETE_INPUTS_EXCEPTION -> readDiscreteInputsException: Exception(header); + READ_INPUT_REGISTERS_EXCEPTION -> readInputRegistersException: Exception(header); + WRITE_SINGLE_COIL_EXCEPTION -> writeCoilException: Exception(header); + WRITE_SINGLE_REGISTER_EXCEPTION -> writeSingleRegisterException: Exception(header); + READ_EXCEPTION_STATUS_EXCEPTION -> readExceptionStatusException: Exception(header); + WRITE_MULTIPLE_COILS_EXCEPTION -> forceMultipleCoilsException: Exception(header); + READ_FILE_RECORD_EXCEPTION -> readGeneralReferenceException: Exception(header); + WRITE_FILE_RECORD_EXCEPTION -> writeGeneralReferenceException: Exception(header); + MASK_WRITE_REGISTER_EXCEPTION -> maskWriteRegisterException: Exception(header); + READ_WRITE_MULTIPLE_REGISTERS_EXCEPTION -> readWriteRegistersException: Exception(header); + READ_FIFO_QUEUE_EXCEPTION -> readFIFOQueueException: Exception(header); # All the rest - default -> unknown: bytestring &restofdata; - }; - -} &length = (header.len+6), - &let { - deliver: bool =$context.flow.deliver_message(header.tid, header.pid, header.uid, header.fc,2); #2 is flag for response + default -> unknown: empty &restofdata; }; -# Class 0 responses - -# RESPONSE FC=3 -type ReadMultipleRegistersResponse(len: uint16, header: ModbusTCP_TransportHeader) = record { - byteCount: uint8; - registers: uint16[registerCount] &length = byteCount; -} &let{ - registerCount : uint8 = byteCount/2; - deliver: bool =$context.flow.deliver_ReadMultiRegRes(this, header.tid, header.pid, header.uid, header.fc, len); -}; - - -# RESPONSE FC=16 -type WriteMultipleRegistersResponse(len: uint16, header: ModbusTCP_TransportHeader) = record { - referenceNumber: uint16; - wordCount: uint16; +type Exception(header: ModbusTCP_TransportHeader) = record { + code: uint8; } &let { - deliver: bool =$context.flow.deliver_WriteMultiRegRes(header.tid, header.pid, header.uid, header.fc, referenceNumber, wordCount, len); + deliver: bool = $context.flow.deliver_Exception(header, this); }; -# Class 1 responses +# REQUEST FC=1 +type ReadCoilsRequest(header: ModbusTCP_TransportHeader) = record { + start_address: uint16; + quantity: uint16 &check(quantity <= 2000); +} &let { + deliver: bool = $context.flow.deliver_ReadCoilsRequest(header, this); +}; # RESPONSE FC=1 -type ReadCoilsResponse(len: uint16, header: ModbusTCP_TransportHeader) = record { - byteCount: uint8; - bits: bytestring &length = byteCount; +type ReadCoilsResponse(header: ModbusTCP_TransportHeader) = record { + byte_count: uint8; + bits: bytestring &length=byte_count; } &let { - deliver: bool =$context.flow.deliver_ReadCoilsRes(header.tid, header.pid, header.uid, header.fc, byteCount, bits); + deliver: bool = $context.flow.deliver_ReadCoilsResponse(header, this); }; +# REQUEST FC=2 +type ReadDiscreteInputsRequest(header: ModbusTCP_TransportHeader) = record { + start_address: uint16; + quantity: uint16 &check(quantity <= 2000); +} &let { + deliver: bool = $context.flow.deliver_ReadDiscreteInputsRequest(header, this); +}; # RESPONSE FC=2 -type ReadInputDiscretesResponse(len: uint16, header: ModbusTCP_TransportHeader) = record { - byteCount: uint8; - bits: bytestring &length = byteCount; +type ReadDiscreteInputsResponse(header: ModbusTCP_TransportHeader) = record { + byte_count: uint8; + bits: bytestring &length=byte_count; } &let { - deliver: bool =$context.flow.deliver_ReadInputDiscRes(header.tid, header.pid, header.uid, header.fc, byteCount, bits); + deliver: bool = $context.flow.deliver_ReadDiscreteInputsResponse(header, this); +}; + +# REQUEST FC=3 +type ReadHoldingRegistersRequest(header: ModbusTCP_TransportHeader) = record { + start_address: uint16; + quantity: uint16 &check(1 <= quantity && quantity <= 125); +} &let { + deliver: bool = $context.flow.deliver_ReadHoldingRegistersRequest(header, this); +}; + +# RESPONSE FC=3 +type ReadHoldingRegistersResponse(header: ModbusTCP_TransportHeader) = record { + byte_count: uint8; + registers: uint16[] &length=byte_count; +} &let { + deliver: bool = $context.flow.deliver_ReadHoldingRegistersResponse(header, this); +}; + +# REQUEST FC=4 +type ReadInputRegistersRequest(header: ModbusTCP_TransportHeader) = record { + start_address: uint16; + quantity: uint16 &check(1 <= quantity && quantity <= 125); +} &let { + deliver: bool = $context.flow.deliver_ReadInputRegistersRequest(header, this); }; # RESPONSE FC=4 -type ReadInputRegistersResponse(len: uint16, header: ModbusTCP_TransportHeader) = record { - byteCount: uint8; - registers: uint16[registerCount] &length = byteCount; +type ReadInputRegistersResponse(header: ModbusTCP_TransportHeader) = record { + byte_count: uint8; + registers: uint16[] &length=byte_count; } &let { - registerCount = byteCount/2; - deliver: bool =$context.flow.deliver_ReadInputRegRes(this, header.tid, header.pid, header.uid, header.fc, len); + deliver: bool = $context.flow.deliver_ReadInputRegistersResponse(header, this); +}; + +# REQUEST FC=5 +type WriteSingleCoilRequest(header: ModbusTCP_TransportHeader) = record { + start_address: uint16; + on_off: uint8 &check(on_off == 0x00 || on_off == 0xFF); + other: uint8 &check(other == 0x00); +} &let { + deliver: bool = $context.flow.deliver_WriteSingleCoilRequest(header, this); }; # RESPONSE FC=5 -type WriteCoilResponse(len: uint16, header: ModbusTCP_TransportHeader) = record { - referenceNumber: uint16; - onOff: uint8 &check(onOff == 0x00 || onOff == 0xFF); - other: uint8 &check(other == 0x00); +type WriteSingleCoilResponse(header: ModbusTCP_TransportHeader) = record { + start_address: uint16; + on_off: uint8 &check(on_off == 0x00 || on_off == 0xFF); + other: uint8 &check(other == 0x00); } &let { - deliver: bool =$context.flow.deliver_WriteCoilRes(header.tid, header.pid, header.uid, header.fc, referenceNumber, onOff, other); + deliver: bool = $context.flow.deliver_WriteSingleCoilResponse(header, this); +}; + +# REQUEST FC=6 +type WriteSingleRegisterRequest(header: ModbusTCP_TransportHeader) = record { + start_address: uint16; + value: uint16; +} &let { + deliver: bool = $context.flow.deliver_WriteSingleRegisterRequest(header, this); }; # RESPONSE FC=6 -type WriteSingleRegisterResponse(len: uint16, header: ModbusTCP_TransportHeader) = record { - referenceNumber: uint16; - registerValue: uint16; +type WriteSingleRegisterResponse(header: ModbusTCP_TransportHeader) = record { + start_address: uint16; + value: uint16; } &let { - deliver: bool =$context.flow.deliver_WriteSingleRegRes(header.tid, header.pid, header.uid, header.fc, len, referenceNumber, registerValue); + deliver: bool = $context.flow.deliver_WriteSingleRegisterResponse(header, this); }; -type ReadExceptionStatusResponse(len: uint16, header: ModbusTCP_TransportHeader) = record { - status: uint8; +# REQUEST FC=15 +type WriteMultipleCoilsRequest(header: ModbusTCP_TransportHeader) = record { + start_address: uint16; + quantity: uint16 &check(quantity <= 0x07B0); + byte_count: uint8 &check(byte_count == (quantity + 7)/8); + coils: bytestring &length=byte_count; } &let { - deliver: bool =$context.flow.deliver_ReadExceptStatRes(header.tid, header.pid, header.uid, header.fc, status, len); + deliver: bool = $context.flow.deliver_WriteMultipleCoilsRequest(header, this); }; -# Class 2 responses - # RESPONSE FC=15 -type ForceMultipleCoilsResponse(len: uint16, header: ModbusTCP_TransportHeader) = record { - referenceNumber: uint16; - bitCount: uint16; +type WriteMultipleCoilsResponse(header: ModbusTCP_TransportHeader) = record { + start_address: uint16; + quantity: uint16 &check(quantity <= 0x07B0); } &let { - deliver: bool =$context.flow.deliver_ForceMultiCoilsRes(header.tid, header.pid, header.uid, header.fc, referenceNumber, bitCount); + deliver: bool = $context.flow.deliver_WriteMultipleCoilsResponse(header, this); +}; + +# REQUEST FC=16 +type WriteMultipleRegistersRequest(header: ModbusTCP_TransportHeader) = record { + start_address: uint16; + quantity: uint16; + byte_count: uint8; + # We specify registers buffer with quantity and byte_count so that the analyzer + # will choke if something doesn't match right (correct devices should make it right). + registers: uint16[quantity] &length=byte_count; +} &let { + deliver: bool = $context.flow.deliver_WriteMultipleRegistersRequest(header, this); +}; + +# RESPONSE FC=16 +type WriteMultipleRegistersResponse(header: ModbusTCP_TransportHeader) = record { + start_address: uint16; + quantity: uint16; +} &let { + deliver: bool = $context.flow.deliver_WriteMultipleRegistersResponse(header, this); +}; + + +# Support data structure for following message type. +type FileRecordRequest = record { + ref_type: uint8; + file_num: uint16; + record_num: uint16; + record_len: uint16; +}; + +# REQUEST FC=20 +type ReadFileRecordRequest(header: ModbusTCP_TransportHeader) = record { + byte_count: uint8; + references: FileRecordRequest[] &length=byte_count; +} &let { + deliver: bool = $context.flow.deliver_ReadFileRecordRequest(header, this); +}; + +# Support data structure for the following message type. +type FileRecordResponse = record { + file_len: uint8; + ref_type: uint8; + record_data: uint16[] &length=file_len; }; # RESPONSE FC=20 -type ReadGeneralReferenceResponse(len: uint16, header: ModbusTCP_TransportHeader) = record { - byteCount: uint8; - references: ReferenceResponse (header) [] &until($input.length()==0) &length = byteCount; -} &length = len, - &let{ - deliver: bool =$context.flow.deliver_ReadReferenceRes(header.tid, header.pid, header.uid, header.fc, byteCount, references); +type ReadFileRecordResponse(header: ModbusTCP_TransportHeader) = record { + byte_count: uint8; + references: FileRecordResponse[] &length=byte_count; +} &let { + deliver: bool = $context.flow.deliver_ReadFileRecordResponse(header, this); +}; + +# Support data structure for the two following message types. +type ReferenceWithData = record { + ref_type: uint8; + file_num: uint16; + record_num: uint16; + word_count: uint16; + register_value: uint16[word_count]; +}; + +# REQUEST FC=21 +type WriteFileRecordRequest(header: ModbusTCP_TransportHeader) = record { + byte_count: uint8; + references: ReferenceWithData[] &length=byte_count; +} &let { + deliver: bool = $context.flow.deliver_WriteFileRecordRequest(header, this); }; # RESPONSE FC=21 -type WriteGeneralReferenceResponse(len: uint16, header: ModbusTCP_TransportHeader) = record { - byteCount: uint8; - references: ReferenceWithData(header)[] &until($input.length() == 0) &length = byteCount; -} &length = len, - &let { - deliver: bool =$context.flow.deliver_WriteReferenceRes(header.tid, header.pid, header.uid, header.fc, byteCount, references); +type WriteFileRecordResponse(header: ModbusTCP_TransportHeader) = record { + byte_count: uint8; + references: ReferenceWithData[] &length=byte_count; +} &let { + deliver: bool = $context.flow.deliver_WriteFileRecordResponse(header, this); +}; + + +# REQUEST FC=22 +type MaskWriteRegisterRequest(header: ModbusTCP_TransportHeader) = record { + start_address: uint16; + and_mask: uint16; + or_mask: uint16; +} &let { + deliver: bool = $context.flow.deliver_MaskWriteRegisterRequest(header, this); }; # RESPONSE FC=22 -type MaskWriteRegisterResponse(len: uint16, header: ModbusTCP_TransportHeader) = record { - referenceNumber: uint16; - andMask: uint16; - orMask: uint16; -} &let{ - deliver: bool =$context.flow.deliver_MaskWriteRegRes(header.tid, header.pid, header.uid, header.fc, referenceNumber, andMask, orMask); +type MaskWriteRegisterResponse(header: ModbusTCP_TransportHeader) = record { + start_address: uint16; + and_mask: uint16; + or_mask: uint16; +} &let { + deliver: bool = $context.flow.deliver_MaskWriteRegisterResponse(header, this); +}; + + +# REQUEST FC=23 +type ReadWriteMultipleRegistersRequest(header: ModbusTCP_TransportHeader) = record { + read_start_address: uint16; + read_quantity: uint16 &check(read_quantity <= 125); + write_start_address: uint16; + write_quantity: uint16 &check(write_quantity <= 100); + write_byte_count: uint8; + write_register_values: uint16[write_quantity] &length=write_byte_count; +} &let { + deliver: bool = $context.flow.deliver_ReadWriteMultipleRegistersRequest(header, this); }; # RESPONSE FC=23 -type ReadWriteRegistersResponse(len: uint16, header: ModbusTCP_TransportHeader) = record { - byteCount: uint8; - registerValues: uint16[registerCount] &length = byteCount; -} &length = len, - &let { - registerCount = byteCount / 2; - deliver: bool =$context.flow.deliver_ReadWriteRegRes(this, header.tid, header.pid, header.uid, header.fc, len); +type ReadWriteMultipleRegistersResponse(header: ModbusTCP_TransportHeader) = record { + byte_count: uint8; + registers: uint16[] &length=byte_count; +} &let { + deliver: bool = $context.flow.deliver_ReadWriteMultipleRegistersResponse(header, this); +}; + +# REQUEST FC=24 +type ReadFIFOQueueRequest(header: ModbusTCP_TransportHeader) = record { + start_address: uint16; +} &let{ + deliver: bool = $context.flow.deliver_ReadFIFOQueueRequest(header, this); }; # RESPONSE FC=24 -type ReadFIFOQueueResponse(len: uint16, header: ModbusTCP_TransportHeader) = record { - byteCount: uint16 &check(byteCount <= 64); - wordCount: uint16 &check(wordCount <= 31); - registerData: uint16[wordCount] &length = byteCount; -} &length = len, - &let{ - deliver: bool =$context.flow.deliver_ReadFIFORes(this, header.tid, header.pid, header.uid, header.fc); +type ReadFIFOQueueResponse(header: ModbusTCP_TransportHeader) = record { + byte_count: uint16 &check(byte_count <= 62); + fifo_count: uint16 &check(word_count <= 31); + register_data: uint16[fifo_count]; +} &let { + deliver: bool = $context.flow.deliver_ReadFIFOQueueResponse(header, this); }; diff --git a/src/modbus.pac b/src/modbus.pac index bc0d774bf3..9148997295 100644 --- a/src/modbus.pac +++ b/src/modbus.pac @@ -6,7 +6,23 @@ # Useful references: http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf # http://www.simplymodbus.ca/faq.htm +%include binpac.pac %include bro.pac +analyzer ModbusTCP withcontext { + connection: ModbusTCP_Conn; + flow: ModbusTCP_Flow; +}; + +connection ModbusTCP_Conn(bro_analyzer: BroAnalyzer) { + upflow = ModbusTCP_Flow(true); + downflow = ModbusTCP_Flow(false); +}; + %include modbus-protocol.pac + +flow ModbusTCP_Flow(is_orig: bool) { + flowunit = ModbusTCP_PDU(is_orig) withcontext (connection, this); +} + %include modbus-analyzer.pac diff --git a/src/types.bif b/src/types.bif index 92cc8db551..34e17ce28e 100644 --- a/src/types.bif +++ b/src/types.bif @@ -156,6 +156,13 @@ type readdir_reply_t: record; type fsstat_t: record; + +module GLOBAL; + +type ModbusHeaders: record; +type ModbusCoils: vector; +type ModbusRegisters: vector; + module Log; enum Writer %{