Rename function and variables for dissecting an NT service Account Control
[obnox/wireshark/wip.git] / packet-mbtcp.c
1 /* packet-mbtcp.c
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
6  *
7  * See
8  *
9  *      http://www.modicon.com/openmbus/
10  *
11  * for information on Modbus/TCP.
12  *
13  * $Id: packet-mbtcp.c,v 1.8 2002/01/21 07:36:37 guy Exp $
14  *
15  * Ethereal - Network traffic analyzer
16  * By Gerald Combs <gerald@ethereal.com>
17  * Copyright 1998 Gerald Combs
18  * 
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.
23  * 
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.
28  * 
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.
32  */
33
34 /*      TODO:
35  *      Analysis of the payload of the Modbus packet.
36  *              --      Based on the function code in the header, and the fact that the packet is 
37  *                      either a query or a response, the different fields in the payload can be 
38  *                      interpreted and displayed.
39  */
40
41 #ifdef HAVE_CONFIG_H
42 # include "config.h"
43 #endif
44
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48
49 #ifdef HAVE_SYS_TYPES_H
50 # include <sys/types.h>
51 #endif
52
53 #ifdef HAVE_NETINET_IN_H
54 # include <netinet/in.h>
55 #endif
56
57 #include <glib.h>
58
59 #ifdef NEED_SNPRINTF_H
60 # include "snprintf.h"
61 #endif
62
63 #include <epan/packet.h>
64
65 #define DEBUG  
66
67 #define TCP_PORT_MBTCP          502     /* Modbus/TCP located on TCP port 502 */
68
69 /* Modbus protocol function codes */
70 #define read_coils                              1
71 #define read_input_discretes    2
72 #define read_mult_regs                  3
73 #define read_input_regs                 4
74 #define write_coil                              5
75 #define write_single_reg                6
76 #define read_except_stat                7
77 #define diagnostics                             8
78 #define program_484                             9
79 #define poll_484                                        10
80 #define get_comm_event_ctrs     11
81 #define get_comm_event_log              12
82 #define program_584_984                 13
83 #define poll_584_984                            14
84 #define force_mult_coils                15
85 #define write_mult_regs                 16
86 #define report_slave_id                 17
87 #define program_884_u84                 18
88 #define reset_comm_link                 19
89 #define read_genl_ref                   20
90 #define write_genl_ref                  21
91 #define mask_write_reg                  22
92 #define read_write_reg                  23
93 #define read_fifo_queue                 24
94 #define program_ConCept                 40
95 #define firmware_replace                125
96 #define program_584_984_2               126
97 #define report_local_addr_mb    127
98
99 /* Modbus protocol exception codes */
100 #define illegal_function                0x01
101 #define illegal_address                 0x02
102 #define illegal_value                   0x03
103 #define illegal_response                0x04
104 #define acknowledge                             0x05
105 #define slave_busy                              0x06
106 #define negative_ack                            0x07
107 #define memory_err                              0x08
108 #define gateway_unavailable     0x0a
109 #define gateway_trgt_fail               0x0b
110
111 /* return codes of function classifying packets as query/response */
112 #define query_packet                            0
113 #define response_packet                 1
114 #define cannot_classify                 2
115
116 /* Modbus header */
117 typedef struct _modbus_hdr {
118         gchar           unit_id;                        /* unit identifier (previously slave addr) */
119         gchar           function_code; /* Modbus function code */
120 } modbus_hdr;
121
122 /* Modbus/TCP header, containing the Modbus header */
123 typedef struct _mbtcp_hdr {
124         guint16         transaction_id; /* copied by svr, usually 0 */
125         guint16                 protocol_id;            /* always 0 */
126         guint16         len;                                    /* len of data that follows */
127         modbus_hdr      mdbs_hdr;                       /* mdbus hdr directly after mdbs/tcp hdr *
128                                                                                          * in packet */
129 } mbtcp_hdr;
130
131 /* Initialize the protocol and registered fields */
132 static int proto_mbtcp = -1;
133 static int hf_mbtcp_transid = -1;
134 static int hf_mbtcp_protid = -1;
135 static int hf_mbtcp_len = -1;
136 static int hf_mbtcp_unitid = -1;
137 static int hf_mbtcp_functioncode = -1;
138
139 /* Initialize the subtree pointers */
140 static gint ett_mbtcp = -1;
141 static gint ett_modbus_hdr = -1;
142         
143 static int
144 classify_packet(packet_info *pinfo)
145 {
146         /* see if nature of packets can be derived from src/dst ports */
147         /* if so, return as found */
148         if ( ( 502 == pinfo->srcport && 502 != pinfo->destport ) ||
149                   ( 502 != pinfo->srcport && 502 == pinfo->destport ) ) {
150                 /* the slave is receiving queries on port 502 */
151                 if ( 502 == pinfo->srcport )  
152                         return response_packet;
153                 else if ( 502 == pinfo->destport ) 
154                         return query_packet;
155         }
156         /* else, cannot classify */
157         return cannot_classify;
158 }
159
160 /* returns string describing function, as given on p6 of 
161  * "Open Modbus/TCP Specification", release 1 by Andy Swales. */
162 static char *
163 function_string(guint16 func_code)
164 {
165         switch ( func_code ) {
166                 case read_coils:                                return "Read coils";                                                            break;
167                 case read_input_discretes:      return "Read input discretes";                          break;
168                 case read_mult_regs:                    return "Read multiple registers";                       break;
169                 case read_input_regs:           return "Read input registers";                          break;
170                 case write_coil:                                return "Write coil";                                                            break;
171                 case write_single_reg:          return "Write single register";                         break;
172                 case read_except_stat:          return "Read exception status";                         break;
173                 case diagnostics:                               return "Diagnostics";                                                   break;
174                 case program_484:                               return "Program (484)";                                                 break;
175                 case poll_484:                                  return "Poll (484)";                                                            break;
176                 case get_comm_event_ctrs:       return "Get Comm. Event Counters";                      break;
177                 case get_comm_event_log:        return "Get Comm. Event Log";                                   break;
178                 case program_584_984:           return "Program (584/984)";                                     break;
179                 case poll_584_984:                      return "Poll (584/984)";                                                break;
180                 case force_mult_coils:          return "Force Multiple Coils";                          break;
181                 case write_mult_regs:           return "Write Multiple Registers";                      break;
182                 case report_slave_id:           return "Report Slave ID";                                               break;
183                 case program_884_u84:           return "Program 884/u84";                                               break;
184                 case reset_comm_link:           return "Reset Comm. Link (884/u84)";            break;
185                 case read_genl_ref:                     return "Read General Reference";                                break;
186                 case write_genl_ref:                    return "Write General Reference";                       break;
187                 case mask_write_reg:                    return "Mask Write Register";                                   break;
188                 case read_write_reg:                    return "Read Write Register";                                   break;
189                 case read_fifo_queue:           return "Read FIFO Queue";                                               break;
190                 case program_ConCept:           return "Program (ConCept)";                                     break;
191                 case firmware_replace:          return "Firmware replacement";                          break;
192                 case program_584_984_2:         return "Program (584/984)";                                     break;
193                 case report_local_addr_mb:      return "Report local address (Modbus)"; break;
194                 default:                                                        return "Unknown function";                                              break;
195         }
196 }
197 static char *
198 exception_string(guint8 exception_code)
199 {
200         switch( exception_code ) {
201                 case illegal_function:          return "Illegal function";                              break;
202                 case illegal_address:           return "Illegal data address";          break;
203                 case illegal_value:                     return "Illegal data value";                    break;
204                 case illegal_response:          return "Illegal response length";       break;
205                 case acknowledge:                               return "Acknowledge";                                   break;
206                 case slave_busy:                                return "Slave device busy";                     break;
207                 case negative_ack:                      return "Negative acknowledge";          break;
208                 case memory_err:                                return "Memory parity error";                   break;
209                 case gateway_unavailable:       return "Gateway path unavailable";      break;
210                 case gateway_trgt_fail:         return "Gateway target device failed to respond";       break;
211                 default:                                                        return "Unknown exception code";                break;
212         }
213 }
214
215 /* Code to actually dissect the packets */
216 static void
217 dissect_mbtcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
218 {
219 /* Set up structures needed to add the protocol subtree and manage it */
220         mbtcp_hdr       mh;
221         proto_item      *mi, *mf;
222         proto_tree      *mbtcp_tree, *modbus_tree;
223         int                     offset = 0;
224         gint                    packet_end, packet_len;
225         char                    *func_string = "", pkt_type_str[9] = "";
226         char                    err_str[100] = "";
227         int                     packet_type;
228         guint32         packet_num = 0; /* num to uniquely identify different mbtcp 
229                                                                                                  * packets in one TCP packet */
230         guint8          exception_code = 0, exception_returned = 0;
231         
232 /* Make entries in Protocol column on summary display */
233         if (check_col(pinfo->cinfo, COL_PROTOCOL)) 
234                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "Modbus/TCP");
235
236         if (check_col(pinfo->cinfo, COL_INFO))
237                 col_clear(pinfo->cinfo, COL_INFO);
238
239 /* Make entries in Info column on summary display (updated after building proto tree) */
240         tvb_memcpy(tvb, (guint8 *)&mh, offset, sizeof(mbtcp_hdr));
241         mh.transaction_id                               =       ntohs(mh.transaction_id);
242         mh.protocol_id                                  =       ntohs(mh.protocol_id);
243         mh.len                                                  =       ntohs(mh.len);
244         if ( mh.mdbs_hdr.function_code & 0x80 ) {
245                 mh.mdbs_hdr.function_code ^= 0x80;
246                 exception_returned = 1;
247         }
248         func_string = function_string(mh.mdbs_hdr.function_code);
249         if (check_col(pinfo->cinfo, COL_INFO))
250         {
251                 packet_type = classify_packet(pinfo);
252                 switch ( packet_type ) {
253                         case query_packet :                     strcpy(pkt_type_str, "query");  
254                                                                                                 break;
255                         case response_packet :          strcpy(pkt_type_str, "response");  
256                                                                                                 break;
257                         case cannot_classify :          strcpy(err_str, "Unable to classify as query or response.");
258                                                                                                 strcpy(pkt_type_str, "unknown");
259                                                                                                 break;
260                         default :
261                                                                                                 break;
262                 }
263                 if ( exception_returned )
264                         strcpy(err_str, "Exception returned ");
265                 col_add_fstr(pinfo->cinfo, COL_INFO, 
266                                 "%8s [%2u pkt(s)]: trans: %5u; unit: %3u, func: %3u: %s. %s", 
267                                 pkt_type_str, 1, mh.transaction_id, (unsigned char) mh.mdbs_hdr.unit_id, 
268                                 (unsigned char) mh.mdbs_hdr.function_code, func_string, err_str);
269         }       
270
271         /* build up protocol tree */
272         do {
273         /* Avoids alignment problems on many architectures. */
274                 tvb_memcpy(tvb, (guint8 *)&mh, offset, sizeof(mbtcp_hdr));
275                 mh.transaction_id                               =       ntohs(mh.transaction_id);
276                 mh.protocol_id                                  =       ntohs(mh.protocol_id);
277                 mh.len                                                  =       ntohs(mh.len);
278                         
279                 if ( mh.mdbs_hdr.function_code & 0x80 ) {
280                         tvb_memcpy(tvb, (guint8 *)&exception_code, offset + sizeof(mbtcp_hdr), 1);
281                         mh.mdbs_hdr.function_code ^= 0x80;
282                         exception_returned = 1;
283                 } else 
284                         exception_code = 0;
285                 
286                 packet_type = classify_packet(pinfo);
287                 
288                 /* if a tree exists, perform operations to add fields to it */
289                 if (tree) {
290                         packet_len = sizeof(mbtcp_hdr) - sizeof(modbus_hdr) + mh.len;
291                         mi = proto_tree_add_protocol_format(tree, proto_mbtcp, tvb, offset, 
292                                         packet_len, "Modbus/TCP");
293                         mbtcp_tree = proto_item_add_subtree(mi, ett_mbtcp);
294         
295                         /* Add items to protocol tree */
296                         /* Modbus/TCP */
297                         proto_tree_add_uint(mbtcp_tree, hf_mbtcp_transid, tvb, offset, 2, 
298                                         mh.transaction_id);
299                         proto_tree_add_uint(mbtcp_tree, hf_mbtcp_protid, tvb, offset + 2, 2, 
300                                         mh.protocol_id);
301                         proto_tree_add_uint(mbtcp_tree, hf_mbtcp_len, tvb, offset + 4, 2, 
302                                         mh.len);
303                         /* Modbus */
304                         packet_end = mh.len;
305                         mf = proto_tree_add_text(mbtcp_tree, tvb, offset + 6, packet_end, 
306                                         "Modbus");
307                         modbus_tree = proto_item_add_subtree(mf, ett_modbus_hdr);       
308                         proto_tree_add_item(modbus_tree, hf_mbtcp_unitid, tvb, offset + 6, 1, 
309                                         mh.mdbs_hdr.unit_id);
310                         mi = proto_tree_add_item(modbus_tree, hf_mbtcp_functioncode, tvb, offset + 7, 1, 
311                                         mh.mdbs_hdr.function_code);
312                         func_string = function_string(mh.mdbs_hdr.function_code);
313                         if ( 0 == exception_code ) 
314                                 proto_item_set_text(mi, "function %u:  %s", mh.mdbs_hdr.function_code, 
315                                                 func_string);
316                         else  
317                                 proto_item_set_text(mi, "function %u:  %s.  Exception: %s",     
318                                                 mh.mdbs_hdr.function_code, func_string, exception_string(exception_code)); 
319                         
320                         packet_end = mh.len - 2;
321                         proto_tree_add_text(modbus_tree, tvb, offset + 8, packet_end, 
322                                         "Modbus data");
323                 }
324                 offset = offset + sizeof(mbtcp_hdr) + (mh.len - sizeof(modbus_hdr));
325                 packet_num++;
326         } while ( tvb_reported_length_remaining(tvb, offset) > 0 );
327
328         
329 /* Update entries in Info column on summary display */
330         if (check_col(pinfo->cinfo, COL_INFO))
331         {
332                 switch ( packet_type ) {
333                         case query_packet :                     strcpy(pkt_type_str, "query");  
334                                                                                                 break;
335                         case response_packet :          strcpy(pkt_type_str, "response");  
336                                                                                                 break;
337                         case cannot_classify :          strcpy(err_str, "Unable to classify as query or response.");
338                                                                                                 strcpy(pkt_type_str, "unknown");
339                                                                                                 break;
340                         default :
341                                                                                                 break;
342                 }
343                 if ( exception_returned )
344                         strcpy(err_str, "Exception returned ");
345                 col_add_fstr(pinfo->cinfo, COL_INFO, 
346                                 "%8s [%2u pkt(s)]: trans: %5u; unit: %3u, func: %3u: %s. %s", 
347                                 pkt_type_str, packet_num, mh.transaction_id, (unsigned char) mh.mdbs_hdr.unit_id, 
348                                 (unsigned char) mh.mdbs_hdr.function_code, func_string, err_str); 
349         }
350
351 /* If this protocol has a sub-dissector call it here, see section 1.8 */
352 }
353
354
355 /* Register the protocol with Ethereal */
356
357 /* this format is require because a script is used to build the C function
358    that calls all the protocol registration.
359 */
360
361 void
362 proto_register_modbus(void)
363 {                 
364
365 /* Setup list of header fields  See Section 1.6.1 for details*/
366         static hf_register_info hf[] = {
367                 /* Modbus/TCP header fields */
368                 { &hf_mbtcp_transid,
369                         { "transaction identifier",           "modbus_tcp.trans_id",
370                         FT_UINT16, BASE_DEC, NULL, 0x0,          
371                         "", HFILL }
372                 },
373                 { &hf_mbtcp_protid,
374                         { "protocol identifier",           "modbus_tcp.prot_id",
375                         FT_UINT16, BASE_DEC, NULL, 0x0,          
376                         "", HFILL }
377                 },
378                 { &hf_mbtcp_len,
379                         { "length",           "modbus_tcp.len",
380                         FT_UINT16, BASE_DEC, NULL, 0x0,          
381                         "", HFILL }
382                 },
383                 /* Modbus header fields */
384                 { &hf_mbtcp_unitid,
385                         { "unit identifier",           "modbus_tcp.unit_id",
386                         FT_UINT8, BASE_DEC, NULL, 0x0,          
387                         "", HFILL }
388                 },
389                 { &hf_mbtcp_functioncode,
390                         { "function code ",           "modbus_tcp.func_code",
391                         FT_UINT8, BASE_DEC, NULL, 0x0,          
392                         "", HFILL }
393                 }
394         };
395
396 /* Setup protocol subtree array */
397         static gint *ett[] = {
398                 &ett_mbtcp,
399                 &ett_modbus_hdr
400         };
401
402 /* Register the protocol name and description */
403         proto_mbtcp = proto_register_protocol("Modbus/TCP",
404             "Modbus/TCP", "mbtcp");
405
406 /* Required function calls to register the header fields and subtrees used */
407         proto_register_field_array(proto_mbtcp, hf, array_length(hf));
408         proto_register_subtree_array(ett, array_length(ett));
409 }
410
411
412 /* If this dissector uses sub-dissector registration add a registration routine.
413    This format is required because a script is used to find these routines and
414    create the code that calls these routines.
415  */
416 void
417 proto_reg_handoff_mbtcp(void)
418 {
419         dissector_handle_t mbtcp_handle;
420
421         mbtcp_handle = create_dissector_handle(dissect_mbtcp, proto_mbtcp);
422         dissector_add("tcp.port", TCP_PORT_MBTCP, mbtcp_handle);
423 }