From Didier Gautheron:
[obnox/wireshark/wip.git] / epan / dissectors / packet-kingfisher.c
1 /* packet-kingfisher.c
2  * Routines for kingfisher packet dissection
3  * By Rob Casey 2007
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * Copied from packet-pop.c
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <epan/packet.h>
33 #include <epan/emem.h>
34 #include <epan/conversation.h>
35
36 #define SUPPORT_KINGFISHER_SERIES_2
37
38 #define TCP_PORT_KINGFISHER         4058
39 #define UDP_PORT_KINGFISHER         4058
40 #ifdef SUPPORT_KINGFISHER_SERIES_2
41 #define TCP_PORT_KINGFISHER_OLD     473
42 #define UDP_PORT_KINGFISHER_OLD     473
43 #endif
44
45 static int proto_kingfisher = -1;
46 static int hf_kingfisher_version = -1;
47 static int hf_kingfisher_system = -1;
48 static int hf_kingfisher_length = -1;
49 static int hf_kingfisher_from = -1;
50 static int hf_kingfisher_target = -1;
51 static int hf_kingfisher_via = -1;
52 static int hf_kingfisher_message = -1;
53 static int hf_kingfisher_function = -1;
54 static int hf_kingfisher_checksum = -1;
55
56 static dissector_handle_t kingfisher_conv_handle;
57
58
59 typedef struct _kingfisher_packet_t
60 {
61     guint8      version;
62     guint8      system;
63     guint16     from;
64     guint16     target;
65     guint16     via;
66     guint8      length;
67     guint8      message;
68     guint8      function;
69     guint16     checksum;
70 } kingfisher_packet_t;
71
72 static gint ett_kingfisher = -1;
73
74 static const value_string function_code_vals[] =
75 {
76     { 0x00, "Acknowledgement" },
77     { 0x01, "Negative Acknowledgement" },
78     { 0x02, "No Access" },
79     { 0x03, "Message Buffer Full" },
80     { 0x0a, "Get Data Frame" },
81     { 0x0b, "Send Data Frame" },
82     { 0x0c, "Get Data Blocks" },
83     { 0x0d, "Send Data Blocks" },
84     { 0x0e, "Check RTU Update" },
85     { 0x0f, "Send RTU Update" },
86     { 0x10, "Get Multiple Data" },
87     { 0x11, "Send Multiple Data" },
88     { 0x12, "Get Multiple Network Data" },
89     { 0x13, "Send Multiple Network Data" },
90     { 0x1e, "Cold Start" },
91     { 0x1f, "Warm Start" },
92     { 0x21, "Program Control" },
93     { 0x22, "Get RTU Status" },
94     { 0x23, "Send RTU Status" },
95     { 0x24, "Set RTC" },
96     { 0x25, "Swap Master CPU" },
97     { 0x26, "Send I/O Module Message" },
98     { 0x28, "Get Diagnostic Information" },
99     { 0x29, "Send Diagnostic Information" },
100     { 0x2b, "Send Pager Information" },
101     { 0x2c, "Get Pager Information" },
102     { 0x2d, "Send Port Data Information" },
103     { 0x2e, "Get Port Data Information" },
104     { 0x2f, "Send RTU Data Information" },
105     { 0x30, "Get RTU Data Information" },
106     { 0x31, "Unlock Port" },
107     { 0x33, "Carrier Test" },
108     { 0x34, "Program Flash RAM" },
109     { 0x35, "Get I/O Values" },
110     { 0x36, "Send I/O Values" },
111     { 0x37, "Synchronise Clock" },
112     { 0x38, "Send Communications Module Message" },
113     { 0x39, "Get Communications Module Message" },
114     { 0x3a, "Get Driver Information" },
115     { 0x3b, "Send Driver Information" },
116     { 0x3c, "Communications Analyser" },
117     { 0x41, "Dial Site" },
118     { 0x42, "Hang-up Site" },
119     { 0x46, "Send File" },
120     { 0x47, "Get File" },
121     { 0x50, "Get Event Logging" },
122     { 0x51, "Send Event Logging" },
123     { 0x80, "Acknowledgement" },
124     { 0x81, "Negative Acknowledgement" },
125     { 0x84, "Get Named Variable" },
126     { 0x85, "Send Named Variable" },
127     { 0x87, "Get Module Information" },
128     { 0x88, "Send Module Information" },
129     { 0x89, "Get I/O Values" },
130     { 0x8a, "Send I/O Values" },
131     { 0x9e, "Cold Start" },
132     { 0x9f, "Warm Start" },
133     { 0xa2, "Get RTU Status" },
134     { 0xa3, "Send RTU Status" },
135     { 0xa4, "Set RTC" },
136     { 0xa8, "Get Diagnostic Information" },
137     { 0xa9, "Send Diagnostic Information" },
138     { 0xd1, "Set Event Log" },
139     { 0xd2, "Clear Event Log" },
140     { 0xd3, "Get Number of Events" },
141     { 0xd4, "Send Number of Events" },
142     { 0xd5, "Get Event Log" },
143     { 0xd6, "Continue Event Log" },
144     { 0xd7, "Send Event Log" },
145     { 0xe0, "Send File Start" },
146     { 0xe1, "Send File Start Acknowledgement" },
147     { 0xe2, "Send File Data" },
148     { 0xe3, "Send File Data Acknowledgement" },
149     {0, NULL}
150 };
151
152
153 static unsigned short
154 kingfisher_checksum(tvbuff_t *tvb, int offset)
155 {
156     gint c, i, j, len;
157     unsigned short crc;
158
159     crc = 0;
160     len = tvb_reported_length_remaining(tvb, offset) - 2;
161     for( i = 1; i < len; i++ )
162     {
163         c = ( ( unsigned char ) tvb_get_guint8( tvb, i ) ) & 0xff;
164         for( j = 0; j < 8; ++j )
165         {
166             if( crc & 0x8000 )
167             {
168                 crc <<= 1;
169                 crc += ( ( ( c <<= 1 ) & 0x100 ) != 0 );
170                 crc ^= 0x1021;
171             }
172             else
173             {
174                 crc <<= 1;
175                 crc += ( ( ( c <<= 1 ) & 0x100 ) != 0 );
176             }
177         }
178     }
179     return crc;
180 }
181
182
183 static gboolean
184 dissect_kingfisher(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean is_conv_dissector)
185 {
186     kingfisher_packet_t *kfp;
187     proto_tree *kingfisher_tree=NULL;
188     proto_item *item=NULL;
189     const char *func_string = NULL;
190     unsigned short checksum;
191     int message;
192
193
194     /* There can be one byte reply packets. we only test for these when we
195        are called from the conversation dissector since that is the only time
196        we can be certain this is kingfisher
197      */
198     if(is_conv_dissector && (tvb_reported_length(tvb)==1)){
199         /*
200           Perform a check to see if the message is a single byte acknowledgement 
201           message - Note that in this instance there is no information in the packet 
202           with regard to source or destination RTU address which can be used in the 
203           population of dissector fields.
204          */
205         switch(tvb_get_guint8(tvb, 0)){
206         case 0x00:
207         case 0x01:
208         case 0x80:
209         case 0x81:
210             col_set_str(pinfo->cinfo, COL_PROTOCOL, "Kingfisher");
211             func_string = val_to_str(tvb_get_guint8(tvb, 0), function_code_vals, "Unknown function");
212             col_clear(pinfo->cinfo, COL_INFO);
213             col_add_fstr(pinfo->cinfo, COL_INFO, "(%s)", func_string);
214             proto_tree_add_protocol_format(tree, proto_kingfisher, tvb, 0, -1, "Kingfisher Protocol, %s", func_string);
215             return TRUE;
216         }
217         /* othervise it is way too short to be kingfisger */
218         return FALSE;
219     }
220
221
222
223
224
225     kfp=ep_alloc(sizeof(kingfisher_packet_t));
226
227     /* Verify that it looks like kingfisher */
228     /* the packet must be at least 9 bytes */
229     if(tvb_reported_length(tvb)<9){
230         return FALSE;
231     }
232
233
234     /* the function code must be known */
235     kfp->function = tvb_get_guint8( tvb, 6 );
236     if (match_strval(kfp->function, function_code_vals) == NULL) {
237         /* This appears not to be a kingfisher packet */
238         return FALSE;
239     }
240
241     /* verify the length */
242     kfp->length = tvb_get_guint8(tvb, 2);
243     if((kfp->length+1) != (guint8)tvb_length(tvb)){
244         return FALSE;
245     }
246
247     /* verify the checksum */
248     kfp->checksum = tvb_get_ntohs(tvb, kfp->length - 1);
249     checksum = kingfisher_checksum(tvb, 0);
250     if(kfp->checksum!=checksum){
251         return FALSE;
252     }
253
254
255     kfp->version = (kfp->function & 0x80)?3:2;
256     kfp->system = tvb_get_guint8( tvb, 0 );
257     kfp->message = tvb_get_guint8( tvb, 5 );
258
259     kfp->target = tvb_get_guint8( tvb, 1 );
260     kfp->from = tvb_get_guint8( tvb, 3 );
261     kfp->via = tvb_get_guint8( tvb, 4 );
262
263     if( kfp->version == 3 )
264     {
265         kfp->target |= ( tvb_get_guint8( tvb, 7 ) << 8 );
266         kfp->from   |= ( tvb_get_guint8( tvb, 8 ) << 8 );
267         kfp->via    |= ( tvb_get_guint8( tvb, 9 ) << 8 );
268     }
269
270
271     /* Ok  this does look like Kingfisher, so lets dissect it */
272     func_string = val_to_str(kfp->function, function_code_vals, "Unknown function");
273
274     col_set_str(pinfo->cinfo, COL_PROTOCOL, "Kingfisher");
275     col_clear(pinfo->cinfo, COL_INFO);
276     col_add_fstr(pinfo->cinfo, COL_INFO, "%u > %u (%s)", kfp->from, kfp->target, func_string);
277
278
279     message = (kfp->message & 0x0f) | ((kfp->message & 0xf0) >> 4);
280
281     if(tree){
282         item = proto_tree_add_protocol_format(tree, proto_kingfisher, tvb, 0, -1, "Kingfisher Protocol, From RTU: %d, Target RTU: %d", kfp->from, kfp->target );
283         kingfisher_tree = proto_item_add_subtree( item, ett_kingfisher );
284     }
285
286     /* version */
287     proto_tree_add_uint(kingfisher_tree, hf_kingfisher_version, tvb, 6, 1, kfp->version);
288
289     /* system id */
290     proto_tree_add_uint_format(kingfisher_tree, hf_kingfisher_system, tvb, 0, 1, kfp->system, "System Identifier: %u (0x%02X)", kfp->system, kfp->system);
291
292     /* target rtu */
293     proto_tree_add_uint_format(kingfisher_tree, hf_kingfisher_target, tvb, 1, 1, kfp->target, "Target RTU: %u (0x%02X)", kfp->target, kfp->target);
294
295     /* length */
296     proto_tree_add_uint_format(kingfisher_tree, hf_kingfisher_length, tvb, 2, 1, kfp->length, "Length: %u (0x%02X)", kfp->length, kfp->length);
297
298     /* from rtu */
299     proto_tree_add_uint_format(kingfisher_tree, hf_kingfisher_from, tvb, 3, 1, kfp->from, "From RTU: %u (0x%02X)", kfp->from, kfp->from);
300
301     /* via rtu */
302     proto_tree_add_uint_format(kingfisher_tree, hf_kingfisher_via, tvb, 4, 1, kfp->via, "Via RTU: %u (0x%02X)", kfp->via, kfp->via);
303
304     /* message number */
305     proto_tree_add_uint_format(kingfisher_tree, hf_kingfisher_message, tvb, 5, 1, kfp->message, "Message Number: %u (0x%02X, %s)", message, kfp->message, ((kfp->message & 0xf0)?"Response":"Request"));
306
307     /* message function code */
308     proto_tree_add_uint_format(kingfisher_tree, hf_kingfisher_function, tvb, 6, 1, kfp->function, "Message Function Code: %u (0x%02X, %s)", kfp->function, kfp->function, func_string);
309
310     /* message data */
311     if(kfp->length > ((kfp->version==3)?11:8)){
312         proto_tree_add_text(kingfisher_tree, tvb, ((kfp->version==3)?10:7), kfp->length - ((kfp->version==3)?11:8), "Message Data");
313     }
314
315     /* checksum */
316     proto_tree_add_uint_format(kingfisher_tree, hf_kingfisher_checksum, tvb, kfp->length-1, 2, kfp->checksum, "Checksum: 0x%04X [%s]", kfp->checksum, ((checksum != kfp->checksum)?"incorrect":"correct"));
317
318
319
320     return TRUE;
321 }
322
323
324 static gboolean
325 dissect_kingfisher_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
326 {
327     gboolean was_kingfisher;
328
329
330     was_kingfisher=dissect_kingfisher(tvb, pinfo, tree, FALSE);
331
332     if(was_kingfisher){
333         conversation_t *conversation;
334
335         /* Ok this was a genuine kingfisher packet. Now create a conversation
336            dissector for this tcp/udp socket and attach a conversation
337            dissector to it.
338          */
339         /*
340          * Do we have a conversation for this connection?
341          */
342         conversation = find_conversation(pinfo->fd->num,
343                                    &pinfo->src, &pinfo->dst,
344                                    pinfo->ptype,
345                                    pinfo->srcport, pinfo->destport, 0);
346         if (conversation == NULL) {
347             /* We don't yet have a conversation, so create one. */
348             conversation = conversation_new(pinfo->fd->num,
349                                    &pinfo->src, &pinfo->dst,
350                                    pinfo->ptype,
351                                    pinfo->srcport, pinfo->destport, 0);
352         }
353         conversation_set_dissector(conversation, kingfisher_conv_handle);
354     }
355
356     return was_kingfisher;
357 }
358
359 static gboolean
360 dissect_kingfisher_conv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
361 {
362     return dissect_kingfisher(tvb, pinfo, tree, TRUE);
363 }
364
365 void
366 proto_register_kingfisher( void )
367 {
368     static hf_register_info hf[] =
369     {
370             { &hf_kingfisher_version,       { "Version", "kingfisher.version", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
371             { &hf_kingfisher_system,        { "System Identifier", "kingfisher.system", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
372             { &hf_kingfisher_length,        { "Length", "kingfisher.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
373             { &hf_kingfisher_from,          { "From RTU", "kingfisher.from", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
374             { &hf_kingfisher_target,        { "Target RTU", "kingfisher.target", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
375             { &hf_kingfisher_via,           { "Via RTU", "kingfisher.via", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
376             { &hf_kingfisher_message,       { "Message Number", "kingfisher.message", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
377             { &hf_kingfisher_function,      { "Function Code", "kingfisher.function", FT_UINT8, BASE_DEC, VALS( function_code_vals ), 0x0, NULL, HFILL } },
378             { &hf_kingfisher_checksum,      { "Checksum", "kingfisher.checksum", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
379     };
380
381     static gint *ett[] = { 
382         &ett_kingfisher
383     };
384
385     proto_kingfisher = proto_register_protocol( "Kingfisher", "Kingfisher", "kf" );
386     proto_register_field_array( proto_kingfisher, hf, array_length( hf ) );
387     proto_register_subtree_array( ett, array_length( ett ) );
388 }
389
390
391 void
392 proto_reg_handoff_kingfisher( void )
393 {
394     dissector_handle_t kingfisher_handle=NULL;
395
396     kingfisher_handle = new_create_dissector_handle(dissect_kingfisher_heur, proto_kingfisher);
397     dissector_add("tcp.port", TCP_PORT_KINGFISHER, kingfisher_handle);
398     dissector_add("udp.port", UDP_PORT_KINGFISHER, kingfisher_handle);
399
400 #ifdef SUPPORT_KINGFISHER_SERIES_2
401     dissector_add("tcp.port", TCP_PORT_KINGFISHER_OLD, kingfisher_handle);
402     dissector_add("udp.port", UDP_PORT_KINGFISHER_OLD, kingfisher_handle);
403 #endif
404     kingfisher_conv_handle = new_create_dissector_handle(dissect_kingfisher_conv, proto_kingfisher);
405
406 }