mirror of
https://github.com/zeek/zeek.git
synced 2025-10-07 17:18:20 +00:00
Final touches to modbus analyzer for now.
- There are still some broken events in the modbus analyzer because I don't have traffic to test with (coil and record related events primarily). - There are a few example scripts in policy/protocols/modbus
This commit is contained in:
parent
009efbcb27
commit
a2f336cc72
6 changed files with 549 additions and 179 deletions
|
@ -3,24 +3,71 @@
|
|||
module Modbus;
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { LOG };
|
||||
|
||||
type Info: record {
|
||||
## Time of the request.
|
||||
ts: time &log;
|
||||
## Unique identifier for the connnection.
|
||||
uid: string &log;
|
||||
## Identifier for the connection.
|
||||
id: conn_id &log;
|
||||
## The name of the function message that was sent.
|
||||
func: string &log &optional;
|
||||
## The status of the response.
|
||||
success: bool &log &default=T;
|
||||
## The exception if the response was a failure.
|
||||
exception: string &log &optional;
|
||||
};
|
||||
|
||||
## Event that can be handled to access the Modbus record as it is sent on
|
||||
## to the logging framework.
|
||||
global log_modbus: event(rec: Info);
|
||||
}
|
||||
|
||||
redef record connection += {
|
||||
modbus: Info &optional;
|
||||
};
|
||||
|
||||
# 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)
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
print fmt("%.6f %s There was an exception: %s", network_time(), c$id, exception_codes[code]);
|
||||
Log::create_stream(Modbus::LOG, [$columns=Info, $ev=log_modbus]);
|
||||
}
|
||||
|
||||
event modbus_message(c: connection, header: ModbusHeaders, is_orig: bool)
|
||||
event modbus_message(c: connection, headers: ModbusHeaders, is_orig: bool) &priority=5
|
||||
{
|
||||
#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]);
|
||||
if ( ! c?$modbus )
|
||||
{
|
||||
c$modbus = [$ts=network_time(), $uid=c$uid, $id=c$id];
|
||||
}
|
||||
|
||||
c$modbus$ts = network_time();
|
||||
c$modbus$func = function_codes[headers$function_code];
|
||||
|
||||
if ( ! is_orig &&
|
||||
( headers$function_code >= 0x81 || headers$function_code <= 0x98 ) )
|
||||
c$modbus$success = F;
|
||||
else
|
||||
c$modbus$success = T;
|
||||
}
|
||||
|
||||
event modbus_message(c: connection, headers: ModbusHeaders, is_orig: bool) &priority=-5
|
||||
{
|
||||
# Don't log now if this is an exception (log in the exception event handler)
|
||||
if ( c$modbus$success )
|
||||
Log::write(LOG, c$modbus);
|
||||
}
|
||||
|
||||
event modbus_exception(c: connection, headers: ModbusHeaders, code: count) &priority=5
|
||||
{
|
||||
c$modbus$exception = exception_codes[code];
|
||||
Log::write(LOG, c$modbus);
|
||||
|
||||
delete c$modbus$exception;
|
||||
}
|
||||
|
||||
|
|
56
scripts/policy/protocols/modbus/known-masters-slaves.bro
Normal file
56
scripts/policy/protocols/modbus/known-masters-slaves.bro
Normal file
|
@ -0,0 +1,56 @@
|
|||
##! Script for tracking known Modbus masters and slaves.
|
||||
##!
|
||||
##! .. todo: This script needs a lot of work. What might be more interesting is to track
|
||||
##! master/slave relationships based on commands sent and successful (non-exception)
|
||||
##! responses.
|
||||
|
||||
module Known;
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { MODBUS_LOG };
|
||||
|
||||
type ModbusDeviceType: enum {
|
||||
MODBUS_MASTER,
|
||||
MODBUS_SLAVE,
|
||||
};
|
||||
|
||||
type ModbusInfo: record {
|
||||
## The time the device was discovered.
|
||||
ts: time &log;
|
||||
## The IP address of the host.
|
||||
host: addr &log;
|
||||
## The type of device being tracked.
|
||||
device_type: ModbusDeviceType &log;
|
||||
};
|
||||
|
||||
## The Modbus nodes being tracked.
|
||||
global modbus_nodes: set[addr, ModbusDeviceType] &create_expire=1day &redef;
|
||||
|
||||
## Event that can be handled to access the loggable record as it is sent
|
||||
## on to the logging framework.
|
||||
global log_known_modbus: event(rec: ModbusInfo);
|
||||
}
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Log::create_stream(Known::MODBUS_LOG, [$columns=ModbusInfo, $ev=log_known_modbus]);
|
||||
}
|
||||
|
||||
event modbus_message(c: connection, headers: ModbusHeaders, is_orig: bool)
|
||||
{
|
||||
local master = c$id$orig_h;
|
||||
local slave = c$id$resp_h;
|
||||
|
||||
if ( [master, MODBUS_MASTER] !in modbus_nodes )
|
||||
{
|
||||
add modbus_nodes[master, MODBUS_MASTER];
|
||||
Log::write(MODBUS_LOG, [$ts=network_time(), $host=master, $device_type=MODBUS_MASTER]);
|
||||
}
|
||||
|
||||
if ( [slave, MODBUS_SLAVE] !in modbus_nodes )
|
||||
{
|
||||
add modbus_nodes[slave, MODBUS_SLAVE];
|
||||
Log::write(MODBUS_LOG, [$ts=network_time(), $host=slave, $device_type=MODBUS_SLAVE]);
|
||||
}
|
||||
|
||||
}
|
94
scripts/policy/protocols/modbus/track-memmap.bro
Normal file
94
scripts/policy/protocols/modbus/track-memmap.bro
Normal file
|
@ -0,0 +1,94 @@
|
|||
##! This script tracks the memory map of holding (read/write) registers and logs
|
||||
##! changes as they are discovered.
|
||||
##!
|
||||
##! .. todo: Not all register reads and write functions are being supported yet.
|
||||
module Modbus;
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { Modbus::REGISTER_CHANGE_LOG };
|
||||
|
||||
## The hosts that should have memory mapping enabled.
|
||||
const track_memmap: Host = ALL_HOSTS &redef;
|
||||
|
||||
type MemmapInfo: record {
|
||||
ts: time &log;
|
||||
uid: string &log;
|
||||
id: conn_id &log;
|
||||
register: count &log;
|
||||
old_val: count &log;
|
||||
new_val: count &log;
|
||||
delta: interval &log;
|
||||
};
|
||||
|
||||
type RegisterValue: record {
|
||||
last_set: time;
|
||||
value: count;
|
||||
};
|
||||
|
||||
## Indexed on the device register value and yielding the register value.
|
||||
type Registers: table[count] of RegisterValue;
|
||||
|
||||
## The memory map of slaves is tracked with this variable.
|
||||
global device_registers: table[addr] of Registers;
|
||||
|
||||
## This event is generated every time a register is seen to be different than
|
||||
## it was previously seen to be.
|
||||
global changed_register: event(c: connection, register: count, old_val: count, new_val: count, delta: interval);
|
||||
}
|
||||
|
||||
redef record Modbus::Info += {
|
||||
track_address: count &default=0;
|
||||
};
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Log::create_stream(Modbus::REGISTER_CHANGE_LOG, [$columns=MemmapInfo]);
|
||||
}
|
||||
|
||||
event modbus_read_holding_registers_request(c: connection, headers: ModbusHeaders, start_address: count, quantity: count)
|
||||
{
|
||||
c$modbus$track_address = start_address+1;
|
||||
}
|
||||
|
||||
event modbus_read_holding_registers_response(c: connection, headers: ModbusHeaders, registers: ModbusRegisters)
|
||||
{
|
||||
local slave = c$id$resp_h;
|
||||
|
||||
if ( ! addr_matches_host(slave, track_memmap ) )
|
||||
return;
|
||||
|
||||
if ( slave !in device_registers )
|
||||
device_registers[slave] = table();
|
||||
|
||||
local slave_regs = device_registers[slave];
|
||||
for ( i in registers )
|
||||
{
|
||||
if ( c$modbus$track_address in slave_regs )
|
||||
{
|
||||
if ( slave_regs[c$modbus$track_address]$value != registers[i] )
|
||||
{
|
||||
local delta = network_time() - slave_regs[c$modbus$track_address]$last_set;
|
||||
event Modbus::changed_register(c, c$modbus$track_address,
|
||||
slave_regs[c$modbus$track_address]$value, registers[i],
|
||||
delta);
|
||||
|
||||
slave_regs[c$modbus$track_address]$last_set = network_time();
|
||||
slave_regs[c$modbus$track_address]$value = registers[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
local tmp_reg: RegisterValue = [$last_set=network_time(), $value=registers[i]];
|
||||
slave_regs[c$modbus$track_address] = tmp_reg;
|
||||
}
|
||||
|
||||
++c$modbus$track_address;
|
||||
}
|
||||
}
|
||||
|
||||
event Modbus::changed_register(c: connection, register: count, old_val: count, new_val: count, delta: interval)
|
||||
{
|
||||
local rec: MemmapInfo = [$ts=network_time(), $uid=c$uid, $id=c$id,
|
||||
$register=register, $old_val=old_val, $new_val=new_val, $delta=delta];
|
||||
Log::write(REGISTER_CHANGE_LOG, rec);
|
||||
}
|
348
src/event.bif
348
src/event.bif
|
@ -6547,146 +6547,300 @@ event netflow_v5_header%(h: nf_v5_header%);
|
|||
## .. bro:see:: netflow_v5_record
|
||||
event netflow_v5_record%(r: nf_v5_record%);
|
||||
|
||||
## Event for any function code whether or not it's supported.
|
||||
## Generated for any modbus message regardless of if the particular function
|
||||
## is further supported or not.
|
||||
##
|
||||
## TODO-Dina: Document event.
|
||||
event modbus_message%(c: connection, header: ModbusHeaders, is_orig: bool%);
|
||||
|
||||
## Event that parses modbus exception
|
||||
## c: The connection.
|
||||
##
|
||||
## TODO-Dina: Document event.
|
||||
event modbus_exception%(c: connection, header: ModbusHeaders, code: count%);
|
||||
|
||||
## Event that passes modbus request function code=1
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## TODO-Dina: Document event.
|
||||
event modbus_read_coils_request%(c: connection, header: ModbusHeaders, start_address: count, quantity: count%);
|
||||
## is_orig: True if the event is raised for the originator side.
|
||||
event modbus_message%(c: connection, headers: ModbusHeaders, is_orig: bool%);
|
||||
|
||||
## Event that passes modbus response function code=1
|
||||
## Generated for any modbus exception message.
|
||||
##
|
||||
## TODO-Dina: Document event.
|
||||
event modbus_read_coils_response%(c: connection, header: ModbusHeaders, coils: ModbusCoils%);
|
||||
|
||||
## Event that passes modbus request function code=2
|
||||
## c: The connection.
|
||||
##
|
||||
## 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
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## TODO-Dina: Document event.
|
||||
event modbus_read_discrete_inputs_response%(c: connection, header: ModbusHeaders, coils: ModbusCoils%);
|
||||
## code: The exception code.
|
||||
event modbus_exception%(c: connection, headers: ModbusHeaders, code: count%);
|
||||
|
||||
## Event that passes modbus request function code=3
|
||||
## Generated for a Modbus read coils request.
|
||||
##
|
||||
## 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
|
||||
## c: The connection.
|
||||
##
|
||||
## TODO-Dina: Document event.
|
||||
event modbus_read_holding_registers_response%(c: connection, header: ModbusHeaders, registers: ModbusRegisters%);
|
||||
|
||||
## Event that passes modbus request function code =4
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## 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
|
||||
## start_address: The memory address where of the first coil to be read.
|
||||
##
|
||||
## TODO-Dina: Document event.
|
||||
event modbus_read_input_registers_response%(c: connection, header: ModbusHeaders, registers: ModbusRegisters%);
|
||||
## quantity: The number of coils to be read.
|
||||
event modbus_read_coils_request%(c: connection, headers: ModbusHeaders, start_address: count, quantity: count%);
|
||||
|
||||
## Event that passes modbus request function code=5
|
||||
## Generated for a Modbus read coils response.
|
||||
##
|
||||
## 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
|
||||
## c: The connection.
|
||||
##
|
||||
## 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
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## TODO-Dina: Document event.
|
||||
event modbus_write_single_register_request%(c: connection, header: ModbusHeaders, start_address: count, value: count%);
|
||||
## coils: The coil values returned from the device.
|
||||
event modbus_read_coils_response%(c: connection, headers: ModbusHeaders, coils: ModbusCoils%);
|
||||
|
||||
## Event that passes modbus response function code=6
|
||||
## Generated for a Modbus read discrete inputs request.
|
||||
##
|
||||
## 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
|
||||
## c: The connection.
|
||||
##
|
||||
## 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
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## 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
|
||||
## start_address: The memory address of the first coil to be read.
|
||||
##
|
||||
## TODO-Dina: Document event.
|
||||
event modbus_write_multiple_registers_request%(c: connection, header: ModbusHeaders, start_address: count, registers: ModbusRegisters%);
|
||||
## quantity: The number of coils to be read.
|
||||
event modbus_read_discrete_inputs_request%(c: connection, headers: ModbusHeaders, start_address: count, quantity: count%);
|
||||
|
||||
## Event that passes modbus response function code=16
|
||||
## Generated for a Modbus read discrete inputs response.
|
||||
##
|
||||
## TODO-Dina: Document event.
|
||||
event modbus_write_multiple_registers_response%(c: connection, header: ModbusHeaders, start_address: count, quantity: count%);
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## coils: The coil values returned from the device.
|
||||
event modbus_read_discrete_inputs_response%(c: connection, headers: ModbusHeaders, coils: ModbusCoils%);
|
||||
|
||||
## Event that passes modbus request function code=20
|
||||
## Generated for a Modbus read holding registers request.
|
||||
##
|
||||
## TODO-Dina: Document event.
|
||||
event modbus_read_file_record_request%(c: connection, header: ModbusHeaders%);
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address of the first register to be read.
|
||||
##
|
||||
## quantity: The number of registers to be read.
|
||||
event modbus_read_holding_registers_request%(c: connection, headers: ModbusHeaders, start_address: count, quantity: count%);
|
||||
|
||||
## Event that passes modbus response function code=20
|
||||
## Generated for a Modbus read holding registers response.
|
||||
##
|
||||
## TODO-Dina: Document event.
|
||||
event modbus_read_file_record_response%(c: connection, header: ModbusHeaders%);
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## registers: The register values returned from the device.
|
||||
event modbus_read_holding_registers_response%(c: connection, headers: ModbusHeaders, registers: ModbusRegisters%);
|
||||
|
||||
## Event that passes modbus request function code=21
|
||||
## Generated for a Modbus read input registers request.
|
||||
##
|
||||
## TODO-Dina: Document event.
|
||||
event modbus_write_file_record_request%(c: connection, header: ModbusHeaders%);
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address of the first register to be read.
|
||||
##
|
||||
## quantity: The number of registers to be read.
|
||||
event modbus_read_input_registers_request%(c: connection, headers: ModbusHeaders, start_address: count, quantity: count%);
|
||||
|
||||
## Event that passes modbus response function code=21
|
||||
## Generated for a Modbus read input registers response.
|
||||
##
|
||||
## TODO-Dina: Document event.
|
||||
event modbus_write_file_record_response%(c: connection, header: ModbusHeaders%);
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## registers: The register values returned from the device.
|
||||
event modbus_read_input_registers_response%(c: connection, headers: ModbusHeaders, registers: ModbusRegisters%);
|
||||
|
||||
## Event that passes modbus request function code=22
|
||||
## Generated for a Modbus write single coil request.
|
||||
##
|
||||
## TODO-Dina: Document event.
|
||||
event modbus_mask_write_register_request%(c: connection, header: ModbusHeaders, start_address: count, and_mask: count, or_mask: count%);
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## address: The memory address of the coil to be written.
|
||||
##
|
||||
## value: The value to be written to the coil.
|
||||
event modbus_write_single_coil_request%(c: connection, headers: ModbusHeaders, address: count, value: bool%);
|
||||
|
||||
## Event that passes modbus response function code=22
|
||||
## Generated for a Modbus write single coil response.
|
||||
##
|
||||
## TODO-Dina: Document event.
|
||||
event modbus_mask_write_register_response%(c: connection, header: ModbusHeaders, start_address: count, and_mask: count, or_mask: count%);
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## address: The memory address of the coil that was written.
|
||||
##
|
||||
## value: The value that was written to the coil.
|
||||
event modbus_write_single_coil_response%(c: connection, headers: ModbusHeaders, address: count, value: bool%);
|
||||
|
||||
## Event that passes modbus request function code=23
|
||||
## Generated for a Modbus write single register request.
|
||||
##
|
||||
## 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%);
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## address: The memory address of the register to be written.
|
||||
##
|
||||
## value: The value to be written to the register.
|
||||
event modbus_write_single_register_request%(c: connection, headers: ModbusHeaders, address: count, value: count%);
|
||||
|
||||
## Event that passes modbus response function code=23
|
||||
## Generated for a Modbus write single register response.
|
||||
##
|
||||
## TODO-Dina: Document event.
|
||||
event modbus_read_write_multiple_registers_response%(c: connection, header: ModbusHeaders, written_registers: ModbusRegisters%);
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## address: The memory address of the register that was written.
|
||||
##
|
||||
## value: The value that was written to the register.
|
||||
event modbus_write_single_register_response%(c: connection, headers: ModbusHeaders, address: count, value: count%);
|
||||
|
||||
## Event that passes modbus request function code=24
|
||||
## Generated for a Modbus write multiple coils request.
|
||||
##
|
||||
## TODO-Dina: Document event.
|
||||
event modbus_read_fifo_queue_request%(c: connection, header: ModbusHeaders, start_address: count%);
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address of the first coil to be written.
|
||||
##
|
||||
## value: The values to be written to the coils.
|
||||
event modbus_write_multiple_coils_request%(c: connection, headers: ModbusHeaders, start_address: count, coils: ModbusCoils%);
|
||||
|
||||
## Event that passes modbus response function code=24
|
||||
## Generated for a Modbus write multiple coils response.
|
||||
##
|
||||
## TODO-Dina: Document event.
|
||||
event modbus_read_fifo_queue_response%(c: connection, header: ModbusHeaders, fifos: ModbusRegisters%);
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address of the first coil that was written.
|
||||
##
|
||||
## quantity: The quantity of coils that were written.
|
||||
event modbus_write_multiple_coils_response%(c: connection, headers: ModbusHeaders, start_address: count, quantity: count%);
|
||||
|
||||
## Generated for a Modbus write multiple registers request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address of the first register to be written.
|
||||
##
|
||||
## registers: The values to be written to the registers.
|
||||
event modbus_write_multiple_registers_request%(c: connection, headers: ModbusHeaders, start_address: count, registers: ModbusRegisters%);
|
||||
|
||||
## Generated for a Modbus write multiple registers response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The memory address of the first register that was written.
|
||||
##
|
||||
## quantity: The quantity of registers that were written.
|
||||
event modbus_write_multiple_registers_response%(c: connection, headers: ModbusHeaders, start_address: count, quantity: count%);
|
||||
|
||||
## Generated for a Modbus read file record request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## .. note: This event is incomplete. The information from the data structure is not
|
||||
## yet passed through to the event.
|
||||
event modbus_read_file_record_request%(c: connection, headers: ModbusHeaders%);
|
||||
|
||||
## Generated for a Modbus read file record response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## .. note: This event is incomplete. The information from the data structure is not
|
||||
## yet passed through to the event.
|
||||
event modbus_read_file_record_response%(c: connection, headers: ModbusHeaders%);
|
||||
|
||||
## Generated for a Modbus write file record request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## .. note: This event is incomplete. The information from the data structure is not
|
||||
## yet passed through to the event.
|
||||
event modbus_write_file_record_request%(c: connection, headers: ModbusHeaders%);
|
||||
|
||||
## Generated for a Modbus write file record response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## .. note: This event is incomplete. The information from the data structure is not
|
||||
## yet passed through to the event.
|
||||
event modbus_write_file_record_response%(c: connection, headers: ModbusHeaders%);
|
||||
|
||||
## Generated for a Modbus mask write register request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## address: The memory address of the register where the masks should be applied.
|
||||
##
|
||||
## and_mask: The value of the logical AND mask to apply to the register.
|
||||
##
|
||||
## or_mask: The value of the logical OR mask to apply to the register.
|
||||
event modbus_mask_write_register_request%(c: connection, headers: ModbusHeaders, address: count, and_mask: count, or_mask: count%);
|
||||
|
||||
## Generated for a Modbus mask write register request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## address: The memory address of the register where the masks were applied.
|
||||
##
|
||||
## and_mask: The value of the logical AND mask applied register.
|
||||
##
|
||||
## or_mask: The value of the logical OR mask applied to the register.
|
||||
event modbus_mask_write_register_response%(c: connection, headers: ModbusHeaders, address: count, and_mask: count, or_mask: count%);
|
||||
|
||||
## Generated for a Modbus read/write multiple registers request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## read_start_address: The memory address of the first register to be read.
|
||||
##
|
||||
## read_quantity: The number of registers to read.
|
||||
##
|
||||
## write_start_address: The memory address of the first register to be written.
|
||||
##
|
||||
## write_registers: The values to be written to the registers.
|
||||
event modbus_read_write_multiple_registers_request%(c: connection, headers: ModbusHeaders, read_start_address: count, read_quantity: count, write_start_address: count, write_registers: ModbusRegisters%);
|
||||
|
||||
## Generated for a Modbus read/write multiple registers response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## written_registers: The register values read from the registers specified in the request.
|
||||
event modbus_read_write_multiple_registers_response%(c: connection, headers: ModbusHeaders, written_registers: ModbusRegisters%);
|
||||
|
||||
## Generated for a Modbus read FIFO queue request.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## start_address: The address of the FIFO queue to read.
|
||||
event modbus_read_fifo_queue_request%(c: connection, headers: ModbusHeaders, start_address: count%);
|
||||
|
||||
## Generated for a Modbus read FIFO queue response.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## headers: The headers for the modbus function.
|
||||
##
|
||||
## fifos: The register values read from the FIFO queue on the device.
|
||||
event modbus_read_fifo_queue_response%(c: connection, headers: 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
|
||||
|
|
|
@ -194,11 +194,23 @@ refine flow ModbusTCP_Flow += {
|
|||
%{
|
||||
if ( ::modbus_write_single_coil_request )
|
||||
{
|
||||
int val;
|
||||
if ( ${message.value} == 0x0000 )
|
||||
val = 0;
|
||||
else if ( ${message.value} == 0xFF00 )
|
||||
val = 1;
|
||||
else
|
||||
{
|
||||
connection()->bro_analyzer()->ProtocolViolation(fmt("invalid value for modbus write single coil request %d",
|
||||
${message.value}));
|
||||
return false;
|
||||
}
|
||||
|
||||
BifEvent::generate_modbus_write_single_coil_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address},
|
||||
${message.on_off}, ${message.other});
|
||||
${message.address},
|
||||
val);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -209,11 +221,23 @@ refine flow ModbusTCP_Flow += {
|
|||
%{
|
||||
if ( ::modbus_write_single_coil_response )
|
||||
{
|
||||
int val;
|
||||
if ( ${message.value} == 0x0000 )
|
||||
val = 0;
|
||||
else if ( ${message.value} == 0xFF00 )
|
||||
val = 1;
|
||||
else
|
||||
{
|
||||
connection()->bro_analyzer()->ProtocolViolation(fmt("invalid value for modbus write single coil response %d",
|
||||
${message.value}));
|
||||
return false;
|
||||
}
|
||||
|
||||
BifEvent::generate_modbus_write_single_coil_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address},
|
||||
${message.on_off}, ${message.other});
|
||||
${message.address},
|
||||
val);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -228,7 +252,7 @@ refine flow ModbusTCP_Flow += {
|
|||
BifEvent::generate_modbus_write_single_register_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address}, ${message.value});
|
||||
${message.address}, ${message.value});
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -242,7 +266,7 @@ refine flow ModbusTCP_Flow += {
|
|||
BifEvent::generate_modbus_write_single_register_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address}, ${message.value});
|
||||
${message.address}, ${message.value});
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -434,7 +458,7 @@ refine flow ModbusTCP_Flow += {
|
|||
BifEvent::generate_modbus_mask_write_register_request(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address},
|
||||
${message.address},
|
||||
${message.and_mask}, ${message.or_mask});
|
||||
}
|
||||
|
||||
|
@ -449,7 +473,7 @@ refine flow ModbusTCP_Flow += {
|
|||
BifEvent::generate_modbus_mask_write_register_response(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
HeaderToBro(header),
|
||||
${message.start_address},
|
||||
${message.address},
|
||||
${message.and_mask}, ${message.or_mask});
|
||||
}
|
||||
|
||||
|
|
|
@ -14,13 +14,13 @@ enum function_codes {
|
|||
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,
|
||||
# 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,
|
||||
# REPORT_SLAVE_ID = 0x11,
|
||||
READ_FILE_RECORD = 0x14,
|
||||
WRITE_FILE_RECORD = 0x15,
|
||||
MASK_WRITE_REGISTER = 0x16,
|
||||
|
@ -64,9 +64,7 @@ type ModbusTCP_PDU(is_orig: bool) = record {
|
|||
true -> request: ModbusTCP_Request(header);
|
||||
false -> response: ModbusTCP_Response(header);
|
||||
};
|
||||
} &length=header.len+6, &byteorder=bigendian, &let {
|
||||
deliver: bool = $context.flow.deliver_message(header);
|
||||
};
|
||||
} &length=header.len+6, &byteorder=bigendian;
|
||||
|
||||
type ModbusTCP_TransportHeader = record {
|
||||
tid: uint16; # Transaction identifier
|
||||
|
@ -74,6 +72,8 @@ type ModbusTCP_TransportHeader = record {
|
|||
len: uint16; # Length of everyting after this field
|
||||
uid: uint8; # Unit identifier (previously 'slave address')
|
||||
fc: uint8; # MODBUS function code (see function_codes enum)
|
||||
} &byteorder=bigendian, &let {
|
||||
deliver: bool = $context.flow.deliver_message(this);
|
||||
};
|
||||
|
||||
type ModbusTCP_Request(header: ModbusTCP_TransportHeader) = case header.fc of {
|
||||
|
@ -98,7 +98,7 @@ type ModbusTCP_Request(header: ModbusTCP_TransportHeader) = case header.fc of {
|
|||
#ENCAP_INTERFACE_TRANSPORT
|
||||
|
||||
# All the rest
|
||||
default -> unknown: empty &restofdata;
|
||||
default -> unknown: bytestring &restofdata;
|
||||
};
|
||||
|
||||
# Responses
|
||||
|
@ -140,7 +140,7 @@ type ModbusTCP_Response(header: ModbusTCP_TransportHeader) = case header.fc of {
|
|||
READ_FIFO_QUEUE_EXCEPTION -> readFIFOQueueException: Exception(header);
|
||||
|
||||
# All the rest
|
||||
default -> unknown: empty &restofdata;
|
||||
default -> unknown: bytestring &restofdata;
|
||||
};
|
||||
|
||||
type Exception(header: ModbusTCP_TransportHeader) = record {
|
||||
|
@ -155,7 +155,7 @@ type ReadCoilsRequest(header: ModbusTCP_TransportHeader) = record {
|
|||
quantity: uint16 &check(quantity <= 2000);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadCoilsRequest(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=1
|
||||
type ReadCoilsResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
|
@ -163,7 +163,7 @@ type ReadCoilsResponse(header: ModbusTCP_TransportHeader) = record {
|
|||
bits: bytestring &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadCoilsResponse(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=2
|
||||
type ReadDiscreteInputsRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
|
@ -171,7 +171,7 @@ type ReadDiscreteInputsRequest(header: ModbusTCP_TransportHeader) = record {
|
|||
quantity: uint16 &check(quantity <= 2000);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadDiscreteInputsRequest(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=2
|
||||
type ReadDiscreteInputsResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
|
@ -179,7 +179,7 @@ type ReadDiscreteInputsResponse(header: ModbusTCP_TransportHeader) = record {
|
|||
bits: bytestring &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadDiscreteInputsResponse(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=3
|
||||
type ReadHoldingRegistersRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
|
@ -187,7 +187,7 @@ type ReadHoldingRegistersRequest(header: ModbusTCP_TransportHeader) = record {
|
|||
quantity: uint16 &check(1 <= quantity && quantity <= 125);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadHoldingRegistersRequest(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=3
|
||||
type ReadHoldingRegistersResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
|
@ -195,7 +195,7 @@ type ReadHoldingRegistersResponse(header: ModbusTCP_TransportHeader) = record {
|
|||
registers: uint16[] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadHoldingRegistersResponse(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=4
|
||||
type ReadInputRegistersRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
|
@ -203,7 +203,7 @@ type ReadInputRegistersRequest(header: ModbusTCP_TransportHeader) = record {
|
|||
quantity: uint16 &check(1 <= quantity && quantity <= 125);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadInputRegistersRequest(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=4
|
||||
type ReadInputRegistersResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
|
@ -211,41 +211,39 @@ type ReadInputRegistersResponse(header: ModbusTCP_TransportHeader) = record {
|
|||
registers: uint16[] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadInputRegistersResponse(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# 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);
|
||||
address: uint16;
|
||||
value: uint16 &check(value == 0x0000 || value == 0xFF00);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteSingleCoilRequest(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=5
|
||||
type WriteSingleCoilResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
on_off: uint8 &check(on_off == 0x00 || on_off == 0xFF);
|
||||
other: uint8 &check(other == 0x00);
|
||||
address: uint16;
|
||||
value: uint16 &check(value == 0x0000 || value == 0xFF00);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteSingleCoilResponse(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=6
|
||||
type WriteSingleRegisterRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
address: uint16;
|
||||
value: uint16;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteSingleRegisterRequest(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=6
|
||||
type WriteSingleRegisterResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
address: uint16;
|
||||
value: uint16;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteSingleRegisterResponse(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=15
|
||||
type WriteMultipleCoilsRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
|
@ -255,7 +253,7 @@ type WriteMultipleCoilsRequest(header: ModbusTCP_TransportHeader) = record {
|
|||
coils: bytestring &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteMultipleCoilsRequest(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=15
|
||||
type WriteMultipleCoilsResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
|
@ -263,7 +261,7 @@ type WriteMultipleCoilsResponse(header: ModbusTCP_TransportHeader) = record {
|
|||
quantity: uint16 &check(quantity <= 0x07B0);
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteMultipleCoilsResponse(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=16
|
||||
type WriteMultipleRegistersRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
|
@ -275,7 +273,7 @@ type WriteMultipleRegistersRequest(header: ModbusTCP_TransportHeader) = record {
|
|||
registers: uint16[quantity] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteMultipleRegistersRequest(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=16
|
||||
type WriteMultipleRegistersResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
|
@ -283,39 +281,38 @@ type WriteMultipleRegistersResponse(header: ModbusTCP_TransportHeader) = record
|
|||
quantity: uint16;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteMultipleRegistersResponse(header, this);
|
||||
};
|
||||
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# Support data structure for following message type.
|
||||
type FileRecordRequest = record {
|
||||
ref_type: uint8;
|
||||
file_num: uint16;
|
||||
record_num: uint16;
|
||||
ref_type: uint8 &check(ref_type == 6);
|
||||
file_num: uint16 &check(file_num > 0);
|
||||
record_num: uint16 &check(record_num <= 0x270F);
|
||||
record_len: uint16;
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=20
|
||||
type ReadFileRecordRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint8;
|
||||
byte_count: uint8 &check(byte_count >= 0x07 && byte_count <= 0xF5);
|
||||
references: FileRecordRequest[] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadFileRecordRequest(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# Support data structure for the following message type.
|
||||
type FileRecordResponse = record {
|
||||
file_len: uint8;
|
||||
ref_type: uint8;
|
||||
file_len: uint8 &check(file_len >= 0x07 && file_len <= 0xF5);
|
||||
ref_type: uint8 &check(ref_type == 6);
|
||||
record_data: uint16[] &length=file_len;
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=20
|
||||
type ReadFileRecordResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
byte_count: uint8;
|
||||
byte_count: uint8 &check(byte_count >= 0x07 && byte_count <= 0xF5);
|
||||
references: FileRecordResponse[] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadFileRecordResponse(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# Support data structure for the two following message types.
|
||||
type ReferenceWithData = record {
|
||||
|
@ -324,7 +321,7 @@ type ReferenceWithData = record {
|
|||
record_num: uint16;
|
||||
word_count: uint16;
|
||||
register_value: uint16[word_count];
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=21
|
||||
type WriteFileRecordRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
|
@ -332,7 +329,7 @@ type WriteFileRecordRequest(header: ModbusTCP_TransportHeader) = record {
|
|||
references: ReferenceWithData[] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteFileRecordRequest(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=21
|
||||
type WriteFileRecordResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
|
@ -340,27 +337,25 @@ type WriteFileRecordResponse(header: ModbusTCP_TransportHeader) = record {
|
|||
references: ReferenceWithData[] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_WriteFileRecordResponse(header, this);
|
||||
};
|
||||
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=22
|
||||
type MaskWriteRegisterRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
address: uint16;
|
||||
and_mask: uint16;
|
||||
or_mask: uint16;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_MaskWriteRegisterRequest(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=22
|
||||
type MaskWriteRegisterResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
address: uint16;
|
||||
and_mask: uint16;
|
||||
or_mask: uint16;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_MaskWriteRegisterResponse(header, this);
|
||||
};
|
||||
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=23
|
||||
type ReadWriteMultipleRegistersRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
|
@ -372,7 +367,7 @@ type ReadWriteMultipleRegistersRequest(header: ModbusTCP_TransportHeader) = reco
|
|||
write_register_values: uint16[write_quantity] &length=write_byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadWriteMultipleRegistersRequest(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=23
|
||||
type ReadWriteMultipleRegistersResponse(header: ModbusTCP_TransportHeader) = record {
|
||||
|
@ -380,20 +375,20 @@ type ReadWriteMultipleRegistersResponse(header: ModbusTCP_TransportHeader) = rec
|
|||
registers: uint16[] &length=byte_count;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadWriteMultipleRegistersResponse(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# REQUEST FC=24
|
||||
type ReadFIFOQueueRequest(header: ModbusTCP_TransportHeader) = record {
|
||||
start_address: uint16;
|
||||
} &let{
|
||||
deliver: bool = $context.flow.deliver_ReadFIFOQueueRequest(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
||||
# RESPONSE FC=24
|
||||
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];
|
||||
fifo_count: uint16 &check(fifo_count <= 31);
|
||||
register_data: uint16[fifo_count] &length=byte_count/2;
|
||||
} &let {
|
||||
deliver: bool = $context.flow.deliver_ReadFIFOQueueResponse(header, this);
|
||||
};
|
||||
} &byteorder=bigendian;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue