Some small improvements to the Modbus Dissector:
- Better detection of query/response w/ serial line Modbus RTU
- Smallest Modbus RTU message can actually be 6 bytes, not 8.
- Only check 16/32-bit register decoding options with appropriate function codes

svn path=/trunk/; revision=51083
This commit is contained in:
Jeff Morriss 2013-08-01 19:32:24 +00:00
parent d0cb52d0b7
commit 490f48044c
1 changed files with 43 additions and 15 deletions

View File

@ -2,7 +2,7 @@
* Routines for Modbus/TCP and Modbus/UDP dissection
* By Riaan Swart <rswart@cs.sun.ac.za>
* Copyright 2001, Institute for Applied Computer Science
* University of Stellenbosch
* University of Stellenbosch
*
* See http://www.modbus.org/ for information on Modbus/TCP.
*
@ -16,6 +16,9 @@
* - Optionally decode holding/input registers as UINT16, UINT32, 32-bit Float IEEE/Modicon
* - Add various register address formatting options as "Raw", "Modicon 5-digit", "Modicon 6-digit"
*
* Updates Aug 2013 (Chris Bontje)
* - Improved dissection support for serial Modbus RTU with detection of query or response messages
*
*****************************************************************************************************
* A brief explanation of the distinction between Modbus/TCP and Modbus RTU over TCP:
*
@ -198,7 +201,7 @@ classify_mbtcp_packet(packet_info *pinfo)
}
static int
classify_mbrtu_packet(packet_info *pinfo)
classify_mbrtu_packet(packet_info *pinfo, tvbuff_t *tvb)
{
/* see if nature of packets can be derived from src/dst ports */
/* if so, return as found */
@ -207,10 +210,31 @@ classify_mbrtu_packet(packet_info *pinfo)
if (( pinfo->srcport != global_mbus_rtu_port ) && ( pinfo->destport == global_mbus_rtu_port ))
return QUERY_PACKET;
/* Special case for serial-captured packets that don't have an Ethernet header */
/* Default these to a response packet, so they at least attempt to decode a good chunk of data */
if (!pinfo->srcport)
return RESPONSE_PACKET;
/* Special case for serial-captured packets that don't have an Ethernet header */
/* Dig into these a little deeper to try to guess the message type */
if (!pinfo->srcport) {
/* If length is 8, this is either a query or very short response */
if (tvb_length(tvb) == 8) {
/* Only possible to get a response message of 8 bytes with Discrete or Coils */
if ((tvb_get_guint8(tvb, 1) == READ_COILS) || (tvb_get_guint8(tvb, 1) == READ_DISCRETE_INPUTS)) {
/* If this is, in fact, a response then the data byte count will be 3 */
/* This will correctly identify all messages except for those that are discrete or coil polls */
/* where the base address range happens to have 0x03 in the upper 16-bit address register */
if (tvb_get_guint8(tvb, 2) == 3) {
return RESPONSE_PACKET;
}
else {
return QUERY_PACKET;
}
}
else {
return QUERY_PACKET;
}
}
else {
return RESPONSE_PACKET;
}
}
/* else, cannot classify */
return CANNOT_CLASSIFY;
@ -523,7 +547,7 @@ dissect_mbrtu_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
}
/* "Request" or "Response" */
packet_type = classify_mbrtu_packet(pinfo);
packet_type = classify_mbrtu_packet(pinfo, tvb);
switch ( packet_type ) {
case QUERY_PACKET :
@ -604,7 +628,7 @@ dissect_mbrtu_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
request_info->register_format = (guint8)global_mbus_rtu_register_format;
p_add_proto_data(pinfo->fd, proto_modbus, 0, request_info);
/* Continue with dissection of Modbus data payload following Modbus/TCP frame */
/* Continue with dissection of Modbus data payload following Modbus RTU frame */
if( tvb_length_remaining(tvb, offset) > 0 )
call_dissector(modbus_handle, next_tvb, pinfo, tree);
@ -673,7 +697,7 @@ dissect_mbrtu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U
{
/* Make sure there's at least enough data to determine it's a Modbus packet */
if (!tvb_bytes_exist(tvb, 0, 8))
if (!tvb_bytes_exist(tvb, 0, 6))
return 0;
/* For Modbus RTU mode, confirm that the first byte is a valid address (non-zero), */
@ -714,13 +738,17 @@ dissect_modbus_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint8
return;
}
/* If data type of payload is Holding or Input registers */
/* AND */
/* if payload length is not a multiple of 4, don't attempt to decode anything in 32-bit format */
if ((payload_len % 4 != 0) && ( (register_format == MBTCP_PREF_REGISTER_FORMAT_UINT32) ||
(register_format == MBTCP_PREF_REGISTER_FORMAT_IEEE_FLOAT) ||
(register_format == MBTCP_PREF_REGISTER_FORMAT_MODICON_FLOAT) ) ) {
register_item = proto_tree_add_item(tree, hf_modbus_data, tvb, payload_start, payload_len, ENC_NA);
expert_add_info(pinfo, register_item, &ei_modbus_data_decode);
return;
if ((function_code == READ_HOLDING_REGS) || (function_code == READ_INPUT_REGS) || (function_code == WRITE_MULT_REGS)) {
if ((payload_len % 4 != 0) && ( (register_format == MBTCP_PREF_REGISTER_FORMAT_UINT32) ||
(register_format == MBTCP_PREF_REGISTER_FORMAT_IEEE_FLOAT) ||
(register_format == MBTCP_PREF_REGISTER_FORMAT_MODICON_FLOAT) ) ) {
register_item = proto_tree_add_item(tree, hf_modbus_data, tvb, payload_start, payload_len, ENC_NA);
expert_add_info(pinfo, register_item, &ei_modbus_data_decode);
return;
}
}
/* Build a new tvb containing just the data payload */