2 * Routines for Modbus/TCP dissection
3 * By Riaan Swart <rswart@cs.sun.ac.za>
4 * Copyright 2001, Institute for Applied Computer Science
5 * University of Stellenbosch
9 * http://www.modicon.com/openmbus/
11 * for information on Modbus/TCP.
15 * Ethereal - Network traffic analyzer
16 * By Gerald Combs <gerald@ethereal.com>
17 * Copyright 1998 Gerald Combs
19 * This program is free software; you can redistribute it and/or
20 * modify it under the terms of the GNU General Public License
21 * as published by the Free Software Foundation; either version 2
22 * of the License, or (at your option) any later version.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
44 #include <epan/packet.h>
48 #define TCP_PORT_MBTCP 502 /* Modbus/TCP located on TCP port 502 */
50 /* Modbus protocol function codes */
52 #define read_input_discretes 2
53 #define read_mult_regs 3
54 #define read_input_regs 4
56 #define write_single_reg 6
57 #define read_except_stat 7
61 #define get_comm_event_ctrs 11
62 #define get_comm_event_log 12
63 #define program_584_984 13
64 #define poll_584_984 14
65 #define force_mult_coils 15
66 #define write_mult_regs 16
67 #define report_slave_id 17
68 #define program_884_u84 18
69 #define reset_comm_link 19
70 #define read_genl_ref 20
71 #define write_genl_ref 21
72 #define mask_write_reg 22
73 #define read_write_reg 23
74 #define read_fifo_queue 24
75 #define program_ConCept 40
76 #define firmware_replace 125
77 #define program_584_984_2 126
78 #define report_local_addr_mb 127
80 /* Modbus protocol exception codes */
81 #define illegal_function 0x01
82 #define illegal_address 0x02
83 #define illegal_value 0x03
84 #define illegal_response 0x04
85 #define acknowledge 0x05
86 #define slave_busy 0x06
87 #define negative_ack 0x07
88 #define memory_err 0x08
89 #define gateway_unavailable 0x0a
90 #define gateway_trgt_fail 0x0b
92 /* return codes of function classifying packets as query/response */
93 #define query_packet 0
94 #define response_packet 1
95 #define cannot_classify 2
98 typedef struct _modbus_hdr {
99 guint8 unit_id; /* unit identifier (previously slave addr) */
100 guint8 function_code; /* Modbus function code */
103 /* Modbus/TCP header, containing the Modbus header */
104 typedef struct _mbtcp_hdr {
105 guint16 transaction_id; /* copied by svr, usually 0 */
106 guint16 protocol_id; /* always 0 */
107 guint16 len; /* len of data that follows */
108 modbus_hdr mdbs_hdr; /* mdbus hdr directly after mdbs/tcp hdr *
112 /* Initialize the protocol and registered fields */
113 static int proto_mbtcp = -1;
114 static int hf_mbtcp_transid = -1;
115 static int hf_mbtcp_protid = -1;
116 static int hf_mbtcp_len = -1;
117 static int hf_mbtcp_unitid = -1;
118 static int hf_mbtcp_functioncode = -1;
119 static int hf_modbus_reference = -1;
120 static int hf_modbus_lreference = -1;
121 static int hf_modbus_reftype = -1;
122 static int hf_modbus_readref = -1;
123 static int hf_modbus_writeref = -1;
124 static int hf_modbus_wordcnt = -1;
125 static int hf_modbus_readwordcnt = -1;
126 static int hf_modbus_writewordcnt = -1;
127 static int hf_modbus_bytecnt = -1;
128 static int hf_modbus_lbytecnt = -1;
129 static int hf_modbus_bitcnt = -1;
130 static int hf_modbus_exceptioncode = -1;
131 static int hf_modbus_andmask = -1;
132 static int hf_modbus_ormask = -1;
134 /* Initialize the subtree pointers */
135 static gint ett_mbtcp = -1;
136 static gint ett_modbus_hdr = -1;
137 static gint ett_group_hdr = -1;
140 classify_packet(packet_info *pinfo)
142 /* see if nature of packets can be derived from src/dst ports */
143 /* if so, return as found */
144 if ( ( 502 == pinfo->srcport && 502 != pinfo->destport ) ||
145 ( 502 != pinfo->srcport && 502 == pinfo->destport ) ) {
146 /* the slave is receiving queries on port 502 */
147 if ( 502 == pinfo->srcport )
148 return response_packet;
149 else if ( 502 == pinfo->destport )
152 /* else, cannot classify */
153 return cannot_classify;
156 /* Translate function to string, as given on p6 of
157 * "Open Modbus/TCP Specification", release 1 by Andy Swales. */
158 static const value_string function_code_vals[] = {
159 { read_coils, "Read coils" },
160 { read_input_discretes, "Read input discretes" },
161 { read_mult_regs, "Read multiple registers" },
162 { read_input_regs, "Read input registers" },
163 { write_coil, "Write coil" },
164 { write_single_reg, "Write single register" },
165 { read_except_stat, "Read exception status" },
166 { diagnostics, "Diagnostics" },
167 { program_484, "Program (484)" },
168 { poll_484, "Poll (484)" },
169 { get_comm_event_ctrs, "Get Comm. Event Counters" },
170 { get_comm_event_log, "Get Comm. Event Log" },
171 { program_584_984, "Program (584/984)" },
172 { poll_584_984, "Poll (584/984)" },
173 { force_mult_coils, "Force Multiple Coils" },
174 { write_mult_regs, "Write Multiple Registers" },
175 { report_slave_id, "Report Slave ID" },
176 { program_884_u84, "Program 884/u84" },
177 { reset_comm_link, "Reset Comm. Link (884/u84)" },
178 { read_genl_ref, "Read General Reference" },
179 { write_genl_ref, "Write General Reference" },
180 { mask_write_reg, "Mask Write Register" },
181 { read_write_reg, "Read Write Register" },
182 { read_fifo_queue, "Read FIFO Queue" },
183 { program_ConCept, "Program (ConCept)" },
184 { firmware_replace, "Firmware replacement" },
185 { program_584_984_2, "Program (584/984)" },
186 { report_local_addr_mb, "Report local address (Modbus)" },
190 static const value_string exception_code_vals[] = {
191 { illegal_function, "Illegal function" },
192 { illegal_address, "Illegal data address" },
193 { illegal_value, "Illegal data value" },
194 { illegal_response, "Illegal response length" },
195 { acknowledge, "Acknowledge" },
196 { slave_busy, "Slave device busy" },
197 { negative_ack, "Negative acknowledge" },
198 { memory_err, "Memory parity error" },
199 { gateway_unavailable, "Gateway path unavailable" },
200 { gateway_trgt_fail, "Gateway target device failed to respond" },
204 /* Code to actually dissect the packets */
206 dissect_mbtcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
208 /* Set up structures needed to add the protocol subtree and manage it */
211 proto_tree *mbtcp_tree, *modbus_tree, *group_tree;
212 int offset, group_offset, packet_type;
214 gint packet_len, payload_start, payload_len;
215 char *func_string = "", pkt_type_str[9] = "";
216 char err_str[100] = "";
217 guint32 byte_cnt, group_byte_cnt, group_word_cnt;
218 guint32 packet_num; /* num to uniquely identify different mbtcp
219 * packets in one TCP packet */
220 guint8 exception_code;
221 gboolean exception_returned;
223 /* Make entries in Protocol column on summary display */
224 if (check_col(pinfo->cinfo, COL_PROTOCOL))
225 col_set_str(pinfo->cinfo, COL_PROTOCOL, "Modbus/TCP");
227 if (check_col(pinfo->cinfo, COL_INFO))
228 col_clear(pinfo->cinfo, COL_INFO);
230 /* Make entries in Info column on summary display */
232 tvb_memcpy(tvb, (guint8 *)&mh, offset, sizeof(mbtcp_hdr));
233 mh.transaction_id = g_ntohs(mh.transaction_id);
234 mh.protocol_id = g_ntohs(mh.protocol_id);
235 mh.len = g_ntohs(mh.len);
236 if ( mh.mdbs_hdr.function_code & 0x80 ) {
237 exception_code = tvb_get_guint8(tvb, offset + sizeof(mbtcp_hdr));
238 mh.mdbs_hdr.function_code ^= 0x80;
239 exception_returned = TRUE;
243 exception_returned = FALSE;
245 func_string = val_to_str(mh.mdbs_hdr.function_code, function_code_vals,
246 "Unknown function (%u)");
247 if (check_col(pinfo->cinfo, COL_INFO))
249 packet_type = classify_packet(pinfo);
250 switch ( packet_type ) {
251 case query_packet : strcpy(pkt_type_str, "query");
253 case response_packet : strcpy(pkt_type_str, "response");
255 case cannot_classify : strcpy(err_str, "Unable to classify as query or response.");
256 strcpy(pkt_type_str, "unknown");
261 if ( exception_returned )
262 strcpy(err_str, "Exception returned ");
263 col_add_fstr(pinfo->cinfo, COL_INFO,
264 "%8s [%2u pkt(s)]: trans: %5u; unit: %3u, func: %3u: %s. %s",
265 pkt_type_str, 1, mh.transaction_id, (unsigned char) mh.mdbs_hdr.unit_id,
266 (unsigned char) mh.mdbs_hdr.function_code, func_string, err_str);
269 /* build up protocol tree and iterate over multiple packets */
272 packet_type = classify_packet(pinfo);
273 packet_len = sizeof(mbtcp_hdr) - sizeof(modbus_hdr) + mh.len;
275 /* if a tree exists, perform operations to add fields to it */
277 mi = proto_tree_add_protocol_format(tree, proto_mbtcp, tvb, offset,
278 packet_len, "Modbus/TCP");
279 mbtcp_tree = proto_item_add_subtree(mi, ett_mbtcp);
281 /* Add items to protocol tree specific to Modbus/TCP Modbus/TCP */
282 proto_tree_add_uint(mbtcp_tree, hf_mbtcp_transid, tvb, offset, 2,
284 proto_tree_add_uint(mbtcp_tree, hf_mbtcp_protid, tvb, offset + 2, 2,
286 proto_tree_add_uint(mbtcp_tree, hf_mbtcp_len, tvb, offset + 4, 2,
289 /* Add items to protocol tree specific to Modbus generic */
290 mf = proto_tree_add_text(mbtcp_tree, tvb, offset + 6, mh.len,
292 modbus_tree = proto_item_add_subtree(mf, ett_modbus_hdr);
293 proto_tree_add_uint(modbus_tree, hf_mbtcp_unitid, tvb, offset + 6, 1,
294 mh.mdbs_hdr.unit_id);
295 mi = proto_tree_add_uint(modbus_tree, hf_mbtcp_functioncode, tvb, offset + 7, 1,
296 mh.mdbs_hdr.function_code);
298 /** detail payload as a function of exception/function code */
299 func_string = val_to_str(mh.mdbs_hdr.function_code,
300 function_code_vals, "Unknown function");
301 payload_start = offset + 8;
302 payload_len = mh.len - sizeof(modbus_hdr);
303 if (exception_returned) {
304 proto_item_set_text(mi, "function %u: %s. Exception: %s",
305 mh.mdbs_hdr.function_code,
307 val_to_str(exception_code,
309 "Unknown exception code (%u)"));
310 proto_tree_add_uint(modbus_tree, hf_modbus_exceptioncode, tvb, payload_start, 1,
314 proto_item_set_text(mi, "function %u: %s", mh.mdbs_hdr.function_code,
316 switch (mh.mdbs_hdr.function_code) {
319 case read_input_discretes:
320 if (packet_type == query_packet) {
321 proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
322 proto_tree_add_item(modbus_tree, hf_modbus_bitcnt, tvb, payload_start + 2, 2, FALSE);
324 else if (packet_type == response_packet) {
325 byte_cnt = (guint32)tvb_get_guint8(tvb, payload_start);
326 proto_tree_add_uint(modbus_tree, hf_modbus_bytecnt, tvb, payload_start, 1, byte_cnt);
327 proto_tree_add_text(modbus_tree, tvb, payload_start + 1, byte_cnt, "Data");
332 case read_input_regs:
333 if (packet_type == query_packet) {
334 proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
335 proto_tree_add_item(modbus_tree, hf_modbus_wordcnt, tvb, payload_start + 2, 2, FALSE);
337 else if (packet_type == response_packet) {
338 byte_cnt = (guint32)tvb_get_guint8(tvb, payload_start);
339 proto_tree_add_uint(modbus_tree, hf_modbus_bytecnt, tvb, payload_start, 1, byte_cnt);
340 proto_tree_add_text(modbus_tree, tvb, payload_start + 1, byte_cnt, "Data");
345 if (packet_type == query_packet) {
346 proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
347 proto_tree_add_text(modbus_tree, tvb, payload_start + 2, 1, "Data");
348 proto_tree_add_text(modbus_tree, tvb, payload_start + 3, 1, "Padding");
350 else if (packet_type == response_packet) {
351 proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
352 proto_tree_add_text(modbus_tree, tvb, payload_start + 2, 1, "Data");
353 proto_tree_add_text(modbus_tree, tvb, payload_start + 3, 1, "Padding");
357 case write_single_reg:
358 if (packet_type == query_packet) {
359 proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
360 proto_tree_add_text(modbus_tree, tvb, payload_start + 2, 2, "Data");
362 else if (packet_type == response_packet) {
363 proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
364 proto_tree_add_text(modbus_tree, tvb, payload_start + 2, 2, "Data");
368 case read_except_stat:
369 if (packet_type == response_packet)
370 proto_tree_add_text(modbus_tree, tvb, payload_start, 1, "Data");
373 case force_mult_coils:
374 if (packet_type == query_packet) {
375 proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
376 proto_tree_add_item(modbus_tree, hf_modbus_bitcnt, tvb, payload_start + 2, 2, FALSE);
377 byte_cnt = (guint32)tvb_get_guint8(tvb, payload_start + 4);
378 proto_tree_add_uint(modbus_tree, hf_modbus_bytecnt, tvb, payload_start + 4, 1,
380 proto_tree_add_text(modbus_tree, tvb, payload_start + 5, byte_cnt, "Data");
382 else if (packet_type == response_packet) {
383 proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
384 proto_tree_add_item(modbus_tree, hf_modbus_bitcnt, tvb, payload_start + 2, 2, FALSE);
388 case write_mult_regs:
389 if (packet_type == query_packet) {
390 proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
391 proto_tree_add_item(modbus_tree, hf_modbus_wordcnt, tvb, payload_start + 2, 2, FALSE);
392 byte_cnt = (guint32)tvb_get_guint8(tvb, payload_start + 4);
393 proto_tree_add_uint(modbus_tree, hf_modbus_bytecnt, tvb, payload_start + 4, 1,
395 proto_tree_add_text(modbus_tree, tvb, payload_start + 5, byte_cnt, "Data");
397 else if (packet_type == response_packet) {
398 proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
399 proto_tree_add_item(modbus_tree, hf_modbus_wordcnt, tvb, payload_start + 2, 2, FALSE);
404 if (packet_type == query_packet) {
405 byte_cnt = (guint32)tvb_get_guint8(tvb, payload_start);
406 proto_tree_add_uint(modbus_tree, hf_modbus_bytecnt, tvb, payload_start, 1,
409 /* add subtrees to describe each group of packet */
410 group_offset = payload_start + 1;
411 for (i = 0; i < byte_cnt / 7; i++) {
412 mi = proto_tree_add_text( modbus_tree, tvb, group_offset, 7,
414 group_tree = proto_item_add_subtree(mi, ett_group_hdr);
415 proto_tree_add_item(group_tree, hf_modbus_reftype, tvb, group_offset, 1, FALSE);
416 proto_tree_add_item(group_tree, hf_modbus_lreference, tvb, group_offset + 1, 4, FALSE);
417 proto_tree_add_item(group_tree, hf_modbus_wordcnt, tvb, group_offset + 5, 2, FALSE);
421 else if (packet_type == response_packet) {
422 byte_cnt = (guint32)tvb_get_guint8(tvb, payload_start);
423 proto_tree_add_uint(modbus_tree, hf_modbus_bytecnt, tvb, payload_start, 1,
426 /* add subtrees to describe each group of packet */
427 group_offset = payload_start + 1;
429 while (byte_cnt > 0) {
430 group_byte_cnt = (guint32)tvb_get_guint8(tvb, group_offset);
431 mi = proto_tree_add_text( modbus_tree, tvb, group_offset, group_byte_cnt + 1,
433 group_tree = proto_item_add_subtree(mi, ett_group_hdr);
434 proto_tree_add_uint(group_tree, hf_modbus_bytecnt, tvb, group_offset, 1,
436 proto_tree_add_item(group_tree, hf_modbus_reftype, tvb, group_offset + 1, 1, FALSE);
437 proto_tree_add_text(group_tree, tvb, group_offset + 2, group_byte_cnt - 1, "Data");
438 group_offset += (group_byte_cnt + 1);
439 byte_cnt -= (group_byte_cnt + 1);
446 if ((packet_type == query_packet) || (packet_type == response_packet)) {
447 byte_cnt = (guint32)tvb_get_guint8(tvb, payload_start);
448 proto_tree_add_uint(modbus_tree, hf_modbus_bytecnt, tvb, payload_start, 1,
451 /* add subtrees to describe each group of packet */
452 group_offset = payload_start + 1;
454 while (byte_cnt > 0) {
455 group_word_cnt = tvb_get_ntohs(tvb, group_offset + 5);
456 group_byte_cnt = (2 * group_word_cnt) + 7;
457 mi = proto_tree_add_text( modbus_tree, tvb, group_offset,
458 group_byte_cnt, "Group %u", i);
459 group_tree = proto_item_add_subtree(mi, ett_group_hdr);
460 proto_tree_add_item(group_tree, hf_modbus_reftype, tvb, group_offset, 1, FALSE);
461 proto_tree_add_item(group_tree, hf_modbus_lreference, tvb, group_offset + 1, 4, FALSE);
462 proto_tree_add_uint(group_tree, hf_modbus_wordcnt, tvb, group_offset + 5, 2,
464 proto_tree_add_text(group_tree, tvb, group_offset + 7, group_byte_cnt - 7, "Data");
465 group_offset += group_byte_cnt;
466 byte_cnt -= group_byte_cnt;
473 if ((packet_type == query_packet) || (packet_type == response_packet)) {
474 proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
475 proto_tree_add_item(modbus_tree, hf_modbus_andmask, tvb, payload_start + 2, 2, FALSE);
476 proto_tree_add_item(modbus_tree, hf_modbus_ormask, tvb, payload_start + 4, 2, FALSE);
481 if (packet_type == query_packet) {
482 proto_tree_add_item(modbus_tree, hf_modbus_readref, tvb, payload_start, 2, FALSE);
483 proto_tree_add_item(modbus_tree, hf_modbus_readwordcnt, tvb, payload_start + 2, 2, FALSE);
484 proto_tree_add_item(modbus_tree, hf_modbus_writeref, tvb, payload_start + 4, 2, FALSE);
485 proto_tree_add_item(modbus_tree, hf_modbus_writewordcnt, tvb, payload_start + 6, 2, FALSE);
486 byte_cnt = (guint32)tvb_get_guint8(tvb, payload_start + 8);
487 proto_tree_add_uint(modbus_tree, hf_modbus_bytecnt, tvb, payload_start + 8, 1,
489 proto_tree_add_text(modbus_tree, tvb, payload_start + 9, byte_cnt, "Data");
491 else if (packet_type == response_packet) {
492 byte_cnt = (guint32)tvb_get_guint8(tvb, payload_start);
493 proto_tree_add_uint(modbus_tree, hf_modbus_bytecnt, tvb, payload_start, 1,
495 proto_tree_add_text(modbus_tree, tvb, payload_start + 1, byte_cnt, "Data");
499 case read_fifo_queue:
500 if (packet_type == query_packet)
501 proto_tree_add_item(modbus_tree, hf_modbus_reference, tvb, payload_start, 2, FALSE);
502 else if (packet_type == response_packet) {
503 byte_cnt = (guint32)tvb_get_ntohs(tvb, payload_start);
504 proto_tree_add_uint(modbus_tree, hf_modbus_lbytecnt, tvb, payload_start, 2,
506 proto_tree_add_item(modbus_tree, hf_modbus_wordcnt, tvb, payload_start + 2, 2, FALSE);
507 proto_tree_add_text(modbus_tree, tvb, payload_start + 4, byte_cnt - 2, "Data");
514 case get_comm_event_ctrs:
515 case get_comm_event_log:
516 case program_584_984:
518 case report_slave_id:
519 case program_884_u84:
520 case reset_comm_link:
521 case program_ConCept:
522 case firmware_replace:
523 case program_584_984_2:
524 case report_local_addr_mb:
525 /* these function codes are not part of the Modbus/TCP specification */
528 proto_tree_add_text(modbus_tree, tvb, payload_start, payload_len, "Data");
534 /* move onto next packet (if there) */
535 offset += packet_len;
537 if (tvb_reported_length_remaining(tvb, offset) > 0) {
539 /* load header structure for next packet */
540 tvb_memcpy(tvb, (guint8 *)&mh, offset, sizeof(mbtcp_hdr));
541 mh.transaction_id = g_ntohs(mh.transaction_id);
542 mh.protocol_id = g_ntohs(mh.protocol_id);
543 mh.len = g_ntohs(mh.len);
545 if ( mh.mdbs_hdr.function_code & 0x80 ) {
546 exception_code = tvb_get_guint8(tvb, offset + sizeof(mbtcp_hdr));
547 mh.mdbs_hdr.function_code ^= 0x80;
548 exception_returned = TRUE;
550 exception_returned = FALSE;
558 /* Register the protocol with Ethereal */
561 proto_register_modbus(void)
564 /* Setup list of header fields See Section 1.6.1 for details*/
565 static hf_register_info hf[] = {
566 /* Modbus/TCP header fields */
568 { "transaction identifier", "modbus_tcp.trans_id",
569 FT_UINT16, BASE_DEC, NULL, 0x0,
573 { "protocol identifier", "modbus_tcp.prot_id",
574 FT_UINT16, BASE_DEC, NULL, 0x0,
578 { "length", "modbus_tcp.len",
579 FT_UINT16, BASE_DEC, NULL, 0x0,
582 /* Modbus header fields */
584 { "unit identifier", "modbus_tcp.unit_id",
585 FT_UINT8, BASE_DEC, NULL, 0x0,
588 { &hf_mbtcp_functioncode,
589 { "function code", "modbus_tcp.func_code",
590 FT_UINT8, BASE_DEC, VALS(function_code_vals), 0x0,
593 { &hf_modbus_reference,
594 { "reference number", "modbus_tcp.reference_num",
595 FT_UINT16, BASE_DEC, NULL, 0x0,
598 { &hf_modbus_lreference,
599 { "reference number (32 bit)", "modbus_tcp.reference_num_32",
600 FT_UINT32, BASE_DEC, NULL, 0x0,
603 { &hf_modbus_reftype,
604 { "reference type", "modbus_tcp.reference_type",
605 FT_UINT8, BASE_DEC, NULL, 0x0,
608 { &hf_modbus_readref,
609 { "read reference number", "modbus_tcp.read_reference_num",
610 FT_UINT16, BASE_DEC, NULL, 0x0,
613 { &hf_modbus_writeref,
614 { "write reference number", "modbus_tcp.write_reference_num",
615 FT_UINT16, BASE_DEC, NULL, 0x0,
618 { &hf_modbus_wordcnt,
619 { "word count", "modbus_tcp.word_cnt",
620 FT_UINT16, BASE_DEC, NULL, 0x0,
623 { &hf_modbus_readwordcnt,
624 { "read word count", "modbus_tcp.read_word_cnt",
625 FT_UINT16, BASE_DEC, NULL, 0x0,
628 { &hf_modbus_writewordcnt,
629 { "write word count", "modbus_tcp.write_word_cnt",
630 FT_UINT16, BASE_DEC, NULL, 0x0,
634 { "bit count", "modbus_tcp.bit_cnt",
635 FT_UINT16, BASE_DEC, NULL, 0x0,
638 { &hf_modbus_bytecnt,
639 { "byte count", "modbus_tcp.byte_cnt",
640 FT_UINT8, BASE_DEC, NULL, 0x0,
643 { &hf_modbus_lbytecnt,
644 { "byte count (16-bit)", "modbus_tcp.byte_cnt_16",
645 FT_UINT8, BASE_DEC, NULL, 0x0,
648 { &hf_modbus_exceptioncode,
649 { "exception code", "modbus_tcp.exception_code",
650 FT_UINT8, BASE_DEC, VALS(exception_code_vals), 0x0,
653 { &hf_modbus_andmask,
654 { "AND mask", "modbus_tcp.and_mask",
655 FT_UINT16, BASE_HEX, NULL, 0x0,
659 { "OR mask", "modbus_tcp.or_mask",
660 FT_UINT16, BASE_HEX, NULL, 0x0,
665 /* Setup protocol subtree array */
666 static gint *ett[] = {
672 /* Register the protocol name and description */
673 proto_mbtcp = proto_register_protocol("Modbus/TCP", "Modbus/TCP", "mbtcp");
675 /* Required function calls to register the header fields and subtrees used */
676 proto_register_field_array(proto_mbtcp, hf, array_length(hf));
677 proto_register_subtree_array(ett, array_length(ett));
681 /* If this dissector uses sub-dissector registration add a registration routine.
682 This format is required because a script is used to find these routines and
683 create the code that calls these routines.
686 proto_reg_handoff_mbtcp(void)
688 dissector_handle_t mbtcp_handle;
690 mbtcp_handle = create_dissector_handle(dissect_mbtcp, proto_mbtcp);
691 dissector_add("tcp.port", TCP_PORT_MBTCP, mbtcp_handle);