Ethereal-dev: [Ethereal-dev] Modbus/TCP patch

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Stephen Shelley <steve.shelley@xxxxxxxxx>
Date: Tue, 15 Apr 2003 19:53:59 -0400
Greetings,

Attached is a diff which adds full payload dissection of compliance levels 0, 1 and 2 function codes for Modbus/TCP. Comments and criticisms would be much appreciated, as this is my first contribution.

Best regards,
Stephen Shelley

------------------------------------------------------------------------

Women and cats will do as they please, and men and dogs should relax and get used to the idea.

	- Robert A. Heinlein

Index: ethereal/packet-mbtcp.c
===================================================================
RCS file: /cvsroot/ethereal/packet-mbtcp.c,v
retrieving revision 1.11
diff -u -r1.11 packet-mbtcp.c
--- ethereal/packet-mbtcp.c	28 Aug 2002 21:00:20 -0000	1.11
+++ ethereal/packet-mbtcp.c	15 Apr 2003 23:40:39 -0000
@@ -58,62 +58,62 @@
 #define read_coils				1
 #define read_input_discretes	2
 #define read_mult_regs			3
-#define read_input_regs			4
+#define read_input_regs		4
 #define write_coil				5
 #define write_single_reg		6
 #define read_except_stat		7
-#define diagnostics				8
-#define program_484				9
-#define poll_484					10
+#define diagnostics			8
+#define program_484			9
+#define poll_484				10
 #define get_comm_event_ctrs	11
 #define get_comm_event_log		12
-#define program_584_984			13
-#define poll_584_984				14
+#define program_584_984		13
+#define poll_584_984			14
 #define force_mult_coils		15
-#define write_mult_regs			16
-#define report_slave_id			17
-#define program_884_u84			18
-#define reset_comm_link			19
+#define write_mult_regs		16
+#define report_slave_id		17
+#define program_884_u84		18
+#define reset_comm_link		19
 #define read_genl_ref			20
 #define write_genl_ref			21
 #define mask_write_reg			22
 #define read_write_reg			23
-#define read_fifo_queue			24
-#define program_ConCept			40
+#define read_fifo_queue		24
+#define program_ConCept		40
 #define firmware_replace		125
 #define program_584_984_2		126
 #define report_local_addr_mb	127
 
 /* Modbus protocol exception codes */
 #define illegal_function		0x01
-#define illegal_address			0x02
+#define illegal_address		0x02
 #define illegal_value			0x03
 #define illegal_response		0x04
-#define acknowledge				0x05
+#define acknowledge			0x05
 #define slave_busy				0x06
-#define negative_ack				0x07
+#define negative_ack			0x07
 #define memory_err				0x08
 #define gateway_unavailable	0x0a
 #define gateway_trgt_fail		0x0b
 
 /* return codes of function classifying packets as query/response */
-#define query_packet				0
-#define response_packet			1
-#define cannot_classify			2
+#define query_packet			0
+#define response_packet		1
+#define cannot_classify		2
 
 /* Modbus header */
 typedef struct _modbus_hdr {
 	gchar		unit_id;			/* unit identifier (previously slave addr) */
-	gchar		function_code; /* Modbus function code */
+	gchar		function_code; 		/* Modbus function code */
 } modbus_hdr;
 
 /* Modbus/TCP header, containing the Modbus header */
 typedef struct _mbtcp_hdr {
-	guint16		transaction_id;	/* copied by svr, usually 0 */
-	guint16 		protocol_id;		/* always 0 */
-	guint16		len;					/* len of data that follows */
+	guint16		transaction_id;		/* copied by svr, usually 0 */
+	guint16 	protocol_id;		/* always 0 */
+	guint16		len;				/* len of data that follows */
 	modbus_hdr	mdbs_hdr;			/* mdbus hdr directly after mdbs/tcp hdr *
-											 * in packet */
+									 * in packet */
 } mbtcp_hdr;
 
 /* Initialize the protocol and registered fields */
@@ -123,10 +123,25 @@
 static int hf_mbtcp_len = -1;
 static int hf_mbtcp_unitid = -1;
 static int hf_mbtcp_functioncode = -1;
+static int hf_modbus_reference = -1;
+static int hf_modbus_lreference = -1;
+static int hf_modbus_reftype = -1;
+static int hf_modbus_readref = -1;
+static int hf_modbus_writeref = -1;
+static int hf_modbus_wordcnt = -1;
+static int hf_modbus_readwordcnt = -1;
+static int hf_modbus_writewordcnt = -1;
+static int hf_modbus_bytecnt = -1;
+static int hf_modbus_lbytecnt = -1;
+static int hf_modbus_bitcnt = -1;
+static int hf_modbus_exceptioncode = -1;
+static int hf_modbus_andmask = -1;
+static int hf_modbus_ormask = -1;
 
 /* Initialize the subtree pointers */
 static gint ett_mbtcp = -1;
 static gint ett_modbus_hdr = -1;
+static gint ett_group_hdr = -1;
 
 static int
 classify_packet(packet_info *pinfo)
@@ -134,7 +149,7 @@
 	/* see if nature of packets can be derived from src/dst ports */
 	/* if so, return as found */
 	if ( ( 502 == pinfo->srcport && 502 != pinfo->destport ) ||
-		  ( 502 != pinfo->srcport && 502 == pinfo->destport ) ) {
+		 ( 502 != pinfo->srcport && 502 == pinfo->destport ) ) {
 		/* the slave is receiving queries on port 502 */
 		if ( 502 == pinfo->srcport )
 			return response_packet;
@@ -151,52 +166,52 @@
 function_string(guint16 func_code)
 {
 	switch ( func_code ) {
-		case read_coils:				return "Read coils";								break;
+		case read_coils:			return "Read coils";						break;
 		case read_input_discretes:	return "Read input discretes";				break;
-		case read_mult_regs:			return "Read multiple registers";			break;
+		case read_mult_regs:		return "Read multiple registers";			break;
 		case read_input_regs:		return "Read input registers";				break;
-		case write_coil:				return "Write coil";								break;
-		case write_single_reg:		return "Write single register";				break;
-		case read_except_stat:		return "Read exception status";				break;
-		case diagnostics:				return "Diagnostics";							break;
-		case program_484:				return "Program (484)";							break;
-		case poll_484:					return "Poll (484)";								break;
+		case write_coil:			return "Write coil";						break;
+		case write_single_reg:		return "Write single register";			break;
+		case read_except_stat:		return "Read exception status";			break;
+		case diagnostics:			return "Diagnostics";						break;
+		case program_484:			return "Program (484)";					break;
+		case poll_484:				return "Poll (484)";						break;
 		case get_comm_event_ctrs:	return "Get Comm. Event Counters";			break;
-		case get_comm_event_log:	return "Get Comm. Event Log";					break;
-		case program_584_984:		return "Program (584/984)";					break;
-		case poll_584_984:			return "Poll (584/984)";						break;
+		case get_comm_event_log:	return "Get Comm. Event Log";				break;
+		case program_584_984:		return "Program (584/984)";				break;
+		case poll_584_984:			return "Poll (584/984)";					break;
 		case force_mult_coils:		return "Force Multiple Coils";				break;
 		case write_mult_regs:		return "Write Multiple Registers";			break;
-		case report_slave_id:		return "Report Slave ID";						break;
-		case program_884_u84:		return "Program 884/u84";						break;
+		case report_slave_id:		return "Report Slave ID";					break;
+		case program_884_u84:		return "Program 884/u84";					break;
 		case reset_comm_link:		return "Reset Comm. Link (884/u84)";		break;
-		case read_genl_ref:			return "Read General Reference";				break;
-		case write_genl_ref:			return "Write General Reference";			break;
-		case mask_write_reg:			return "Mask Write Register";					break;
-		case read_write_reg:			return "Read Write Register";					break;
-		case read_fifo_queue:		return "Read FIFO Queue";						break;
-		case program_ConCept:		return "Program (ConCept)";					break;
+		case read_genl_ref:		return "Read General Reference";			break;
+		case write_genl_ref:		return "Write General Reference";			break;
+		case mask_write_reg:		return "Mask Write Register";				break;
+		case read_write_reg:		return "Read Write Register";				break;
+		case read_fifo_queue:		return "Read FIFO Queue";					break;
+		case program_ConCept:		return "Program (ConCept)";				break;
 		case firmware_replace:		return "Firmware replacement";				break;
-		case program_584_984_2:		return "Program (584/984)";					break;
+		case program_584_984_2:	return "Program (584/984)";				break;
 		case report_local_addr_mb:	return "Report local address (Modbus)";	break;
-		default:							return "Unknown function";						break;
+		default:					return "Unknown function";					break;
 	}
 }
 static char *
 exception_string(guint8 exception_code)
 {
 	switch( exception_code ) {
-		case illegal_function:		return "Illegal function";				break;
-		case illegal_address:		return "Illegal data address";		break;
-		case illegal_value:			return "Illegal data value";			break;
-		case illegal_response:		return "Illegal response length";	break;
-		case acknowledge:				return "Acknowledge";					break;
-		case slave_busy:				return "Slave device busy";			break;
-		case negative_ack:			return "Negative acknowledge";		break;
-		case memory_err:				return "Memory parity error";			break;
-		case gateway_unavailable:	return "Gateway path unavailable";	break;
-		case gateway_trgt_fail:		return "Gateway target device failed to respond";	break;
-		default:							return "Unknown exception code";		break;
+		case illegal_function:		return "Illegal function";					break;
+		case illegal_address:		return "Illegal data address";				break;
+		case illegal_value:		return "Illegal data value";				break;
+		case illegal_response:		return "Illegal response length";			break;
+		case acknowledge:			return "Acknowledge";						break;
+		case slave_busy:			return "Slave device busy";				break;
+		case negative_ack:			return "Negative acknowledge";				break;
+		case memory_err:			return "Memory parity error";				break;
+		case gateway_unavailable:	return "Gateway path unavailable";			break;
+		case gateway_trgt_fail:	return "Gateway target device failed to respond";	break;
+		default:					return "Unknown exception code";			break;
 	}
 }
 
@@ -207,15 +222,15 @@
 /* Set up structures needed to add the protocol subtree and manage it */
 	mbtcp_hdr	mh;
 	proto_item	*mi, *mf;
-	proto_tree	*mbtcp_tree, *modbus_tree;
-	int			offset = 0;
-	gint			packet_end, packet_len;
-	char			*func_string = "", pkt_type_str[9] = "";
-	char			err_str[100] = "";
-	int			packet_type;
-	guint32		packet_num = 0;	/* num to uniquely identify different mbtcp
-												 * packets in one TCP packet */
-	guint8		exception_code = 0, exception_returned = 0;
+	proto_tree	*mbtcp_tree, *modbus_tree, *group_tree;
+	int		i, offset, group_offset, packet_type;
+	gint		packet_len, payload_start, payload_len;
+	char		*func_string = "", pkt_type_str[9] = "";
+	char		err_str[100] = "";
+	guint32		byte_cnt, group_byte_cnt, word_cnt, group_word_cnt;
+	guint32		packet_num;	/* num to uniquely identify different mbtcp
+							 * packets in one TCP packet */
+	guint8		exception_code, exception_returned;
 
 /* Make entries in Protocol column on summary display */
 	if (check_col(pinfo->cinfo, COL_PROTOCOL))
@@ -224,26 +239,30 @@
 	if (check_col(pinfo->cinfo, COL_INFO))
 		col_clear(pinfo->cinfo, COL_INFO);
 
-/* Make entries in Info column on summary display (updated after building proto tree) */
+/* Make entries in Info column on summary display */
+	offset = 0;
 	tvb_memcpy(tvb, (guint8 *)&mh, offset, sizeof(mbtcp_hdr));
 	mh.transaction_id				=	g_ntohs(mh.transaction_id);
 	mh.protocol_id					=	g_ntohs(mh.protocol_id);
 	mh.len							=	g_ntohs(mh.len);
 	if ( mh.mdbs_hdr.function_code & 0x80 ) {
+		exception_code = tvb_get_guint8(tvb, offset + sizeof(mbtcp_hdr));
 		mh.mdbs_hdr.function_code ^= 0x80;
 		exception_returned = 1;
 	}
+	else
+		exception_returned = 0;
 	func_string = function_string(mh.mdbs_hdr.function_code);
 	if (check_col(pinfo->cinfo, COL_INFO))
 	{
 		packet_type = classify_packet(pinfo);
 		switch ( packet_type ) {
-			case query_packet : 			strcpy(pkt_type_str, "query");
+			case query_packet : 		strcpy(pkt_type_str, "query");
 												break;
-			case response_packet : 		strcpy(pkt_type_str, "response");
+			case response_packet : 	strcpy(pkt_type_str, "response");
 												break;
 			case cannot_classify :		strcpy(err_str, "Unable to classify as query or response.");
-												strcpy(pkt_type_str, "unknown");
+										strcpy(pkt_type_str, "unknown");
 												break;
 			default :
 												break;
@@ -256,96 +275,292 @@
 				(unsigned char) mh.mdbs_hdr.function_code, func_string, err_str);
 	}
 
-	/* build up protocol tree */
-	do {
-	/* Avoids alignment problems on many architectures. */
-		tvb_memcpy(tvb, (guint8 *)&mh, offset, sizeof(mbtcp_hdr));
-		mh.transaction_id				=	g_ntohs(mh.transaction_id);
-		mh.protocol_id					=	g_ntohs(mh.protocol_id);
-		mh.len							=	g_ntohs(mh.len);
-
-		if ( mh.mdbs_hdr.function_code & 0x80 ) {
-			tvb_memcpy(tvb, (guint8 *)&exception_code, offset + sizeof(mbtcp_hdr), 1);
-			mh.mdbs_hdr.function_code ^= 0x80;
-			exception_returned = 1;
-		} else
-			exception_code = 0;
-
+	/* build up protocol tree and iterate over multiple packets */
+	packet_num = 0;
+	while (1) {
 		packet_type = classify_packet(pinfo);
+		packet_len = sizeof(mbtcp_hdr) - sizeof(modbus_hdr) + mh.len;
 
 		/* if a tree exists, perform operations to add fields to it */
 		if (tree) {
-			packet_len = sizeof(mbtcp_hdr) - sizeof(modbus_hdr) + mh.len;
 			mi = proto_tree_add_protocol_format(tree, proto_mbtcp, tvb, offset,
 					packet_len, "Modbus/TCP");
 			mbtcp_tree = proto_item_add_subtree(mi, ett_mbtcp);
 
-			/* Add items to protocol tree */
-			/* Modbus/TCP */
+			/* Add items to protocol tree specific to Modbus/TCP Modbus/TCP */
 			proto_tree_add_uint(mbtcp_tree, hf_mbtcp_transid, tvb, offset, 2,
 					mh.transaction_id);
 			proto_tree_add_uint(mbtcp_tree, hf_mbtcp_protid, tvb, offset + 2, 2,
 					mh.protocol_id);
 			proto_tree_add_uint(mbtcp_tree, hf_mbtcp_len, tvb, offset + 4, 2,
 					mh.len);
-			/* Modbus */
-			packet_end = mh.len;
-			mf = proto_tree_add_text(mbtcp_tree, tvb, offset + 6, packet_end,
+					
+			/* Add items to protocol tree specific to Modbus generic */
+			mf = proto_tree_add_text(mbtcp_tree, tvb, offset + 6, mh.len,
 					"Modbus");
 	  		modbus_tree = proto_item_add_subtree(mf, ett_modbus_hdr);
-			proto_tree_add_item(modbus_tree, hf_mbtcp_unitid, tvb, offset + 6, 1,
+			proto_tree_add_uint(modbus_tree, hf_mbtcp_unitid, tvb, offset + 6, 1,
 					mh.mdbs_hdr.unit_id);
-			mi = proto_tree_add_item(modbus_tree, hf_mbtcp_functioncode, tvb, offset + 7, 1,
+			mi = proto_tree_add_uint(modbus_tree, hf_mbtcp_functioncode, tvb, offset + 7, 1,
 					mh.mdbs_hdr.function_code);
+					
+			/** detail payload as a function of exception/function code */
 			func_string = function_string(mh.mdbs_hdr.function_code);
-			if ( 0 == exception_code )
-				proto_item_set_text(mi, "function %u:  %s", mh.mdbs_hdr.function_code,
-						func_string);
-			else
+			payload_start = offset + 8;
+			payload_len = mh.len - sizeof(modbus_hdr);
+			if (exception_returned) {
 				proto_item_set_text(mi, "function %u:  %s.  Exception: %s",
 						mh.mdbs_hdr.function_code, func_string, exception_string(exception_code));
-
-			packet_end = mh.len - 2;
-			proto_tree_add_text(modbus_tree, tvb, offset + 8, packet_end,
-					"Modbus data");
+				proto_tree_add_uint(modbus_tree, hf_modbus_exceptioncode, tvb, payload_start, 1,
+						exception_code);
+			}
+			else {
+				proto_item_set_text(mi, "function %u:  %s", mh.mdbs_hdr.function_code,
+						func_string);
+				switch (mh.mdbs_hdr.function_code) {
+					
+					case read_coils:			
+					case read_input_discretes:	
+						if (packet_type == query_packet) {
+							proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
+							proto_tree_add_item(modbus_tree, hf_modbus_bitcnt, tvb, payload_start + 2, 2, FALSE);
+						}
+						else if (packet_type == response_packet) {
+							byte_cnt = (guint32)tvb_get_guint8(tvb, payload_start);
+							proto_tree_add_uint(modbus_tree, hf_modbus_bytecnt, tvb, payload_start, 1, byte_cnt);
+							proto_tree_add_text(modbus_tree, tvb, payload_start + 1, byte_cnt, "Data");
+						}
+						break;
+						
+					case read_mult_regs:		
+					case read_input_regs:		
+						if (packet_type == query_packet) {
+							proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
+							proto_tree_add_item(modbus_tree, hf_modbus_wordcnt, tvb, payload_start + 2, 2, FALSE);
+						}
+						else if (packet_type == response_packet) {
+							byte_cnt = (guint32)tvb_get_guint8(tvb, payload_start);
+							proto_tree_add_uint(modbus_tree, hf_modbus_bytecnt, tvb, payload_start, 1, byte_cnt);
+							proto_tree_add_text(modbus_tree, tvb, payload_start + 1, byte_cnt, "Data");
+						}
+						break;
+						
+					case write_coil:			
+						if (packet_type == query_packet) {
+							proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
+							proto_tree_add_text(modbus_tree, tvb, payload_start + 2, 1, "Data");
+							proto_tree_add_text(modbus_tree, tvb, payload_start + 3, 1, "Padding");
+						}
+						else if (packet_type == response_packet) {
+							proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
+							proto_tree_add_text(modbus_tree, tvb, payload_start + 2, 1, "Data");
+							proto_tree_add_text(modbus_tree, tvb, payload_start + 3, 1, "Padding");
+						}
+						break;
+						
+					case write_single_reg:		
+						if (packet_type == query_packet) {
+							proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
+							proto_tree_add_text(modbus_tree, tvb, payload_start + 2, 2, "Data");
+						}
+						else if (packet_type == response_packet) {
+							proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
+							proto_tree_add_text(modbus_tree, tvb, payload_start + 2, 2, "Data");
+						}
+						break;
+						
+					case read_except_stat:		
+						if (packet_type == response_packet)
+							proto_tree_add_text(modbus_tree, tvb, payload_start, 1, "Data");
+						break;
+						
+					case force_mult_coils:		
+						if (packet_type == query_packet) {
+							proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
+							proto_tree_add_item(modbus_tree, hf_modbus_bitcnt, tvb, payload_start + 2, 2, FALSE);
+							byte_cnt = (guint32)tvb_get_guint8(tvb, payload_start + 4);
+							proto_tree_add_uint(modbus_tree, hf_modbus_bytecnt, tvb, payload_start + 4, 1,
+									byte_cnt);
+							proto_tree_add_text(modbus_tree, tvb, payload_start + 5, byte_cnt, "Data");
+						}
+						else if (packet_type == response_packet) {
+							proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
+							proto_tree_add_item(modbus_tree, hf_modbus_bitcnt, tvb, payload_start + 2, 2, FALSE);
+						}
+						break;
+						
+					case write_mult_regs:		
+						if (packet_type == query_packet) {
+							proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
+							proto_tree_add_item(modbus_tree, hf_modbus_wordcnt, tvb, payload_start + 2, 2, FALSE);
+							byte_cnt = (guint32)tvb_get_guint8(tvb, payload_start + 4);
+							proto_tree_add_uint(modbus_tree, hf_modbus_bytecnt, tvb, payload_start + 4, 1,
+									byte_cnt);
+							proto_tree_add_text(modbus_tree, tvb, payload_start + 5, byte_cnt, "Data");
+						}
+						else if (packet_type == response_packet) {
+							proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
+							proto_tree_add_item(modbus_tree, hf_modbus_wordcnt, tvb, payload_start + 2, 2, FALSE);
+						}
+						break;
+						
+					case read_genl_ref:			
+						if (packet_type == query_packet) {
+							byte_cnt = (guint32)tvb_get_guint8(tvb, payload_start);
+							proto_tree_add_uint(modbus_tree, hf_modbus_bytecnt, tvb, payload_start, 1,
+									byte_cnt);
+									
+							/* add subtrees to describe each group of packet */
+							group_offset = payload_start + 1;
+							for (i = 0; i < byte_cnt / 7; i++) {
+								mi = proto_tree_add_text( modbus_tree, tvb, group_offset, 7,
+										"Group %u", i);
+						  		group_tree = proto_item_add_subtree(mi, ett_group_hdr);
+								proto_tree_add_item(group_tree, hf_modbus_reftype, tvb, group_offset, 1, FALSE);
+								proto_tree_add_item(group_tree, hf_modbus_lreference, tvb, group_offset + 1, 4, FALSE);
+								proto_tree_add_item(group_tree, hf_modbus_wordcnt, tvb, group_offset + 5, 2, FALSE);
+								group_offset += 7;
+							}
+						}
+						else if (packet_type == response_packet) {
+							byte_cnt = (guint32)tvb_get_guint8(tvb, payload_start);
+							proto_tree_add_uint(modbus_tree, hf_modbus_bytecnt, tvb, payload_start, 1,
+									byte_cnt);
+									
+							/* add subtrees to describe each group of packet */
+							group_offset = payload_start + 1;
+							i = 0;
+							while (byte_cnt > 0) {
+								group_byte_cnt = (guint32)tvb_get_guint8(tvb, group_offset);
+								mi = proto_tree_add_text( modbus_tree, tvb, group_offset, group_byte_cnt + 1,
+										"Group %u", i);
+						  		group_tree = proto_item_add_subtree(mi, ett_group_hdr);
+								proto_tree_add_uint(group_tree, hf_modbus_bytecnt, tvb, group_offset, 1,
+										group_byte_cnt);
+								proto_tree_add_item(group_tree, hf_modbus_reftype, tvb, group_offset + 1, 1, FALSE);
+								proto_tree_add_text(group_tree, tvb, group_offset + 2, group_byte_cnt - 1, "Data");
+								group_offset += (group_byte_cnt + 1);
+								byte_cnt -= (group_byte_cnt + 1);
+								i++;
+							}
+						}
+						break;
+						
+					case write_genl_ref:		
+						if ((packet_type == query_packet) || (packet_type == response_packet)) {
+							byte_cnt = (guint32)tvb_get_guint8(tvb, payload_start);
+							proto_tree_add_uint(modbus_tree, hf_modbus_bytecnt, tvb, payload_start, 1,
+									byte_cnt);
+									
+							/* add subtrees to describe each group of packet */
+							group_offset = payload_start + 1;
+							i = 0;
+							while (byte_cnt > 0) {
+								group_word_cnt = tvb_get_ntohs(tvb, group_offset + 5);
+								group_byte_cnt = (2 * group_word_cnt) + 7;
+								mi = proto_tree_add_text( modbus_tree, tvb, group_offset, 
+										group_byte_cnt, "Group %u", i);
+						  		group_tree = proto_item_add_subtree(mi, ett_group_hdr);
+								proto_tree_add_item(group_tree, hf_modbus_reftype, tvb, group_offset, 1, FALSE);
+								proto_tree_add_item(group_tree, hf_modbus_lreference, tvb, group_offset + 1, 4, FALSE);
+								proto_tree_add_uint(group_tree, hf_modbus_wordcnt, tvb, group_offset + 5, 2, 
+										group_word_cnt);
+								proto_tree_add_text(group_tree, tvb, group_offset + 7, group_byte_cnt - 7, "Data");
+								group_offset += group_byte_cnt;
+								byte_cnt -= group_byte_cnt;
+								i++;
+							}
+						}
+						break;
+						
+					case mask_write_reg:		
+						if ((packet_type == query_packet) || (packet_type == response_packet)) {
+							proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
+							proto_tree_add_item(modbus_tree, hf_modbus_andmask, tvb, payload_start + 2, 2, FALSE);
+							proto_tree_add_item(modbus_tree, hf_modbus_ormask, tvb, payload_start + 4, 2, FALSE);
+						}
+						break;
+						
+					case read_write_reg:		
+						if (packet_type == query_packet) {
+							proto_tree_add_item(modbus_tree, hf_modbus_readref, tvb, payload_start, 2, FALSE);
+							proto_tree_add_item(modbus_tree, hf_modbus_readwordcnt, tvb, payload_start + 2, 2, FALSE);
+							proto_tree_add_item(modbus_tree, hf_modbus_writeref, tvb, payload_start + 4, 2, FALSE);
+							proto_tree_add_item(modbus_tree, hf_modbus_writewordcnt, tvb, payload_start + 6, 2, FALSE);
+							byte_cnt = (guint32)tvb_get_guint8(tvb, payload_start + 8);
+							proto_tree_add_uint(modbus_tree, hf_modbus_bytecnt, tvb, payload_start + 8, 1,
+									byte_cnt);
+							proto_tree_add_text(modbus_tree, tvb, payload_start + 9, byte_cnt, "Data");
+						}
+						else if (packet_type == response_packet) {
+							byte_cnt = (guint32)tvb_get_guint8(tvb, payload_start);
+							proto_tree_add_uint(modbus_tree, hf_modbus_bytecnt, tvb, payload_start, 1,
+									byte_cnt);
+							proto_tree_add_text(modbus_tree, tvb, payload_start + 1, byte_cnt, "Data");
+						}
+						break;
+						
+					case read_fifo_queue:		
+						if (packet_type == query_packet)
+							proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
+						else if (packet_type == response_packet) {
+							byte_cnt = (guint32)tvb_get_ntohs(tvb, payload_start);
+							proto_tree_add_uint(modbus_tree, hf_modbus_lbytecnt, tvb, payload_start, 2,
+									byte_cnt);
+							proto_tree_add_item(modbus_tree, hf_modbus_wordcnt, tvb, payload_start + 2, 2, FALSE);
+							proto_tree_add_text(modbus_tree, tvb, payload_start + 4, byte_cnt - 2, "Data");
+						}
+						break;
+						
+					case diagnostics:			
+					case program_484:			
+					case poll_484:				
+					case get_comm_event_ctrs:	
+					case get_comm_event_log:	
+					case program_584_984:		
+					case poll_584_984:			
+					case report_slave_id:		
+					case program_884_u84:		
+					case reset_comm_link:		
+					case program_ConCept:		
+					case firmware_replace:		
+					case program_584_984_2:		
+					case report_local_addr_mb:	
+						/* these function codes are not part of the Modbus/TCP specification */
+					default:					
+						if (payload_len > 0)
+							proto_tree_add_text(modbus_tree, tvb, payload_start, payload_len, "Data");
+						break;
+				}
+			}
 		}
-		offset = offset + sizeof(mbtcp_hdr) + (mh.len - sizeof(modbus_hdr));
+		
+		/* move onto next packet (if there) */
+		offset += packet_len;
 		packet_num++;
-	} while ( tvb_reported_length_remaining(tvb, offset) > 0 );
-
-
-/* Update entries in Info column on summary display */
-	if (check_col(pinfo->cinfo, COL_INFO))
-	{
-		switch ( packet_type ) {
-			case query_packet : 			strcpy(pkt_type_str, "query");
-												break;
-			case response_packet : 		strcpy(pkt_type_str, "response");
-												break;
-			case cannot_classify :		strcpy(err_str, "Unable to classify as query or response.");
-												strcpy(pkt_type_str, "unknown");
-												break;
-			default :
-												break;
+		if (tvb_reported_length_remaining(tvb, offset) > 0) {
+			
+			/* load header structure for next packet */
+			tvb_memcpy(tvb, (guint8 *)&mh, offset, sizeof(mbtcp_hdr));
+			mh.transaction_id				=	g_ntohs(mh.transaction_id);
+			mh.protocol_id					=	g_ntohs(mh.protocol_id);
+			mh.len							=	g_ntohs(mh.len);
+	
+			if ( mh.mdbs_hdr.function_code & 0x80 ) {
+				exception_code = tvb_get_guint8(tvb, offset + sizeof(mbtcp_hdr));
+				mh.mdbs_hdr.function_code ^= 0x80;
+				exception_returned = 1;
+			} else
+				exception_returned = 0;
 		}
-		if ( exception_returned )
-			strcpy(err_str, "Exception returned ");
-		col_add_fstr(pinfo->cinfo, COL_INFO,
-				"%8s [%2u pkt(s)]: trans: %5u; unit: %3u, func: %3u: %s. %s",
-				pkt_type_str, packet_num, mh.transaction_id, (unsigned char) mh.mdbs_hdr.unit_id,
-				(unsigned char) mh.mdbs_hdr.function_code, func_string, err_str);
+		else
+			break;
 	}
-
-/* If this protocol has a sub-dissector call it here, see section 1.8 */
 }
 
 
 /* Register the protocol with Ethereal */
 
-/* this format is require because a script is used to build the C function
-   that calls all the protocol registration.
-*/
-
 void
 proto_register_modbus(void)
 {
@@ -354,44 +569,114 @@
 	static hf_register_info hf[] = {
 		/* Modbus/TCP header fields */
 		{ &hf_mbtcp_transid,
-			{ "transaction identifier",           "modbus_tcp.trans_id",
+			{ "transaction identifier",			"modbus_tcp.trans_id",
 			FT_UINT16, BASE_DEC, NULL, 0x0,
 			"", HFILL }
 		},
 		{ &hf_mbtcp_protid,
-			{ "protocol identifier",           "modbus_tcp.prot_id",
+			{ "protocol identifier",			"modbus_tcp.prot_id",
 			FT_UINT16, BASE_DEC, NULL, 0x0,
 			"", HFILL }
 		},
 		{ &hf_mbtcp_len,
-			{ "length",           "modbus_tcp.len",
+			{ "length",							"modbus_tcp.len",
 			FT_UINT16, BASE_DEC, NULL, 0x0,
 			"", HFILL }
 		},
 		/* Modbus header fields */
 		{ &hf_mbtcp_unitid,
-			{ "unit identifier",           "modbus_tcp.unit_id",
+			{ "unit identifier",           		"modbus_tcp.unit_id",
 			FT_UINT8, BASE_DEC, NULL, 0x0,
 			"", HFILL }
 		},
 		{ &hf_mbtcp_functioncode,
-			{ "function code ",           "modbus_tcp.func_code",
+			{ "function code",            		"modbus_tcp.func_code",
+			FT_UINT8, BASE_DEC, NULL, 0x0,
+			"", HFILL }
+		},
+		{ &hf_modbus_reference,
+			{ "reference number",            	"modbus_tcp.reference_num",
+			FT_UINT16, BASE_DEC, NULL, 0x0,
+			"", HFILL }
+		},
+		{ &hf_modbus_lreference,
+			{ "reference number (32 bit)",   	"modbus_tcp.reference_num_32",
+			FT_UINT32, BASE_DEC, NULL, 0x0,
+			"", HFILL }
+		},
+		{ &hf_modbus_reftype,
+			{ "reference type",   				"modbus_tcp.reference_type",
 			FT_UINT8, BASE_DEC, NULL, 0x0,
 			"", HFILL }
+		},
+		{ &hf_modbus_readref,
+			{ "read reference number",   		"modbus_tcp.read_reference_num",
+			FT_UINT16, BASE_DEC, NULL, 0x0,
+			"", HFILL }
+		},
+		{ &hf_modbus_writeref,
+			{ "write reference number",   		"modbus_tcp.write_reference_num",
+			FT_UINT16, BASE_DEC, NULL, 0x0,
+			"", HFILL }
+		},
+		{ &hf_modbus_wordcnt,
+			{ "word count",            			"modbus_tcp.word_cnt",
+			FT_UINT16, BASE_DEC, NULL, 0x0,
+			"", HFILL }
+		},
+		{ &hf_modbus_readwordcnt,
+			{ "read word count",       			"modbus_tcp.read_word_cnt",
+			FT_UINT16, BASE_DEC, NULL, 0x0,
+			"", HFILL }
+		},
+		{ &hf_modbus_writewordcnt,
+			{ "write word count",       		"modbus_tcp.write_word_cnt",
+			FT_UINT16, BASE_DEC, NULL, 0x0,
+			"", HFILL }
+		},
+		{ &hf_modbus_bitcnt,
+			{ "bit count",            			"modbus_tcp.bit_cnt",
+			FT_UINT16, BASE_DEC, NULL, 0x0,
+			"", HFILL }
+		},
+		{ &hf_modbus_bytecnt,
+			{ "byte count",            			"modbus_tcp.byte_cnt",
+			FT_UINT8, BASE_DEC, NULL, 0x0,
+			"", HFILL }
+		},
+		{ &hf_modbus_lbytecnt,
+			{ "byte count (16-bit)",   			"modbus_tcp.byte_cnt_16",
+			FT_UINT8, BASE_DEC, NULL, 0x0,
+			"", HFILL }
+		},
+		{ &hf_modbus_exceptioncode,
+			{ "exception code",            		"modbus_tcp.exception_code",
+			FT_UINT8, BASE_DEC, NULL, 0x0,
+			"", HFILL }
+		},
+		{ &hf_modbus_andmask,
+			{ "AND mask",            			"modbus_tcp.and_mask",
+			FT_UINT16, BASE_HEX, NULL, 0x0,
+			"", HFILL }
+		},
+		{ &hf_modbus_ormask,
+			{ "OR mask",            			"modbus_tcp.or_mask",
+			FT_UINT16, BASE_HEX, NULL, 0x0,
+			"", HFILL }
 		}
 	};
 
-/* Setup protocol subtree array */
+	/* Setup protocol subtree array */
 	static gint *ett[] = {
 		&ett_mbtcp,
-		&ett_modbus_hdr
+		&ett_modbus_hdr,
+		&ett_group_hdr
 	};
 
-/* Register the protocol name and description */
-	proto_mbtcp = proto_register_protocol("Modbus/TCP",
-	    "Modbus/TCP", "mbtcp");
+	/* Register the protocol name and description */
+	proto_mbtcp = proto_register_protocol("Modbus/TCP", "Modbus/TCP", "mbtcp");
 
-/* Required function calls to register the header fields and subtrees used */
+	/* Required function calls to register the header fields and subtrees used */
 	proto_register_field_array(proto_mbtcp, hf, array_length(hf));
 	proto_register_subtree_array(ett, array_length(ett));
 }