Remove unneeded #include <stdio.h>
[obnox/wireshark/wip.git] / epan / dissectors / packet-mstp.c
1 /* packet-mstp.c
2  * Routines for BACnet MS/TP datalink dissection
3  * Copyright 2008 Steve Karg <skarg@users.sourceforge.net> Alabama
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include <glib.h>
34
35 #include <epan/packet.h>
36 #include <epan/oui.h>
37 #include <epan/llcsaps.h>
38 #include <epan/expert.h>
39 #include "packet-llc.h"
40 #include "packet-mstp.h"
41
42 /* Probably should be a preference, but here for now */
43 #define BACNET_MSTP_SUMMARY_IN_TREE
44 #define BACNET_MSTP_CHECKSUM_VALIDATE
45
46 /* MS/TP Frame Type */
47 /* Frame Types 8 through 127 are reserved by ASHRAE. */
48 #define MSTP_TOKEN 0
49 #define MSTP_POLL_FOR_MASTER 1
50 #define MSTP_REPLY_TO_POLL_FOR_MASTER 2
51 #define MSTP_TEST_REQUEST 3
52 #define MSTP_TEST_RESPONSE 4
53 #define MSTP_BACNET_DATA_EXPECTING_REPLY 5
54 #define MSTP_BACNET_DATA_NOT_EXPECTING_REPLY 6
55 #define MSTP_REPLY_POSTPONED 7
56
57 static const value_string
58 bacnet_mstp_frame_type_name[] = {
59         {MSTP_TOKEN, "Token"},
60         {MSTP_POLL_FOR_MASTER, "Poll For Master"},
61         {MSTP_REPLY_TO_POLL_FOR_MASTER, "Reply To Poll For Master"},
62         {MSTP_TEST_REQUEST, "Test_Request"},
63         {MSTP_TEST_RESPONSE, "Test_Response"},
64         {MSTP_BACNET_DATA_EXPECTING_REPLY, "BACnet Data Expecting Reply"},
65         {MSTP_BACNET_DATA_NOT_EXPECTING_REPLY, "BACnet Data Not Expecting Reply"},
66         {MSTP_REPLY_POSTPONED, "Reply Postponed"},
67         /* Frame Types 128 through 255: Proprietary Frames */
68         {0, NULL }
69 };
70
71 static dissector_handle_t data_handle;
72 static dissector_table_t subdissector_table;
73
74 static int proto_mstp = -1;
75
76 static gint ett_bacnet_mstp = -1;
77 static gint ett_bacnet_mstp_checksum = -1;
78
79 static int hf_mstp_preamble_55 = -1;
80 static int hf_mstp_preamble_FF = -1;
81 static int hf_mstp_frame_type = -1;
82 static int hf_mstp_frame_destination = -1;
83 static int hf_mstp_frame_source = -1;
84 static int hf_mstp_frame_vendor_id = -1;
85 static int hf_mstp_frame_pdu_len = -1;
86 static int hf_mstp_frame_crc8 = -1;
87 static int hf_mstp_frame_crc16 = -1;
88 static int hf_mstp_frame_checksum_bad = -1;
89 static int hf_mstp_frame_checksum_good = -1;
90
91 #if defined(BACNET_MSTP_CHECKSUM_VALIDATE)
92 /* Accumulate "dataValue" into the CRC in crcValue. */
93 /* Return value is updated CRC */
94 /*  The ^ operator means exclusive OR. */
95 /* Note: This function is copied directly from the BACnet standard. */
96 static guint8 CRC_Calc_Header(
97     guint8 dataValue,
98     guint8 crcValue)
99 {
100     guint16 crc;
101
102     crc = crcValue ^ dataValue; /* XOR C7..C0 with D7..D0 */
103
104     /* Exclusive OR the terms in the table (top down) */
105     crc = crc ^ (crc << 1) ^ (crc << 2) ^ (crc << 3)
106         ^ (crc << 4) ^ (crc << 5) ^ (crc << 6)
107         ^ (crc << 7);
108
109     /* Combine bits shifted out left hand end */
110     return (crc & 0xfe) ^ ((crc >> 8) & 1);
111 }
112 #endif
113
114 #if defined(BACNET_MSTP_CHECKSUM_VALIDATE)
115 /* Accumulate "dataValue" into the CRC in crcValue. */
116 /*  Return value is updated CRC */
117 /*  The ^ operator means exclusive OR. */
118 /* Note: This function is copied directly from the BACnet standard. */
119 static guint16 CRC_Calc_Data(
120     guint8 dataValue,
121     guint16 crcValue)
122 {
123     guint16 crcLow;
124
125     crcLow = (crcValue & 0xff) ^ dataValue;     /* XOR C7..C0 with D7..D0 */
126
127     /* Exclusive OR the terms in the table (top down) */
128     return (crcValue >> 8) ^ (crcLow << 8) ^ (crcLow << 3)
129         ^ (crcLow << 12) ^ (crcLow >> 4)
130         ^ (crcLow & 0x0f) ^ ((crcLow & 0x0f) << 7);
131 }
132 #endif
133
134 /* Common frame type text */
135 const gchar *mstp_frame_type_text(guint32 val)
136 {
137         return val_to_str(val,
138                 bacnet_mstp_frame_type_name,
139                 "Unknown Frame Type (%u)");
140 }
141
142 /* dissects a BACnet MS/TP frame */
143 /* preamble 0x55 0xFF is not included in Cimetrics U+4 output */
144 void
145 dissect_mstp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
146         proto_tree *subtree, gint offset)
147 {
148         guint8 mstp_frame_type = 0;
149         guint8 mstp_frame_source = 0;
150         guint8 mstp_frame_destination = 0;
151         guint16 mstp_frame_pdu_len = 0;
152         guint16 mstp_tvb_pdu_len = 0;
153         guint16 vendorid = 0;
154         tvbuff_t *next_tvb = NULL;
155         proto_item *item;
156 #if defined(BACNET_MSTP_CHECKSUM_VALIDATE)
157         /* used to calculate the crc value */
158         guint8 crc8 = 0xFF, framecrc8;
159         guint16 crc16 = 0xFFFF, framecrc16;
160         guint8 crcdata;
161         guint16 i; /* loop counter */
162         guint16 max_len = 0;
163         proto_tree *checksum_tree;
164 #endif
165
166         col_set_str(pinfo->cinfo, COL_PROTOCOL, "BACnet");
167         col_set_str(pinfo->cinfo, COL_INFO, "BACnet MS/TP");
168         mstp_frame_type = tvb_get_guint8(tvb, offset);
169         mstp_frame_destination = tvb_get_guint8(tvb, offset+1);
170         mstp_frame_source = tvb_get_guint8(tvb, offset+2);
171         mstp_frame_pdu_len = tvb_get_ntohs(tvb, offset+3);
172         if (check_col(pinfo->cinfo, COL_INFO)) {
173                 col_append_fstr(pinfo->cinfo, COL_INFO, " %s",
174                         mstp_frame_type_text(mstp_frame_type));
175         }
176         /* Add the items to the tree */
177         proto_tree_add_item(subtree, hf_mstp_frame_type, tvb,
178                         offset, 1, TRUE);
179         proto_tree_add_item(subtree, hf_mstp_frame_destination, tvb,
180                         offset+1, 1, TRUE);
181         proto_tree_add_item(subtree, hf_mstp_frame_source, tvb,
182                         offset+2, 1, TRUE);
183         item = proto_tree_add_item(subtree, hf_mstp_frame_pdu_len, tvb,
184                         offset+3, 2, FALSE);
185         mstp_tvb_pdu_len = tvb_length_remaining(tvb, offset+6);
186         /* check the length - which does not include the crc16 checksum */
187         if (mstp_tvb_pdu_len > 2) {
188                 if (mstp_frame_pdu_len > (mstp_tvb_pdu_len-2)) {
189                         expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR,
190                                 "Length field value goes past the end of the payload");
191                 }
192         }
193 #if defined(BACNET_MSTP_CHECKSUM_VALIDATE)
194         /* calculate checksum to validate */
195         for (i = 0; i < 5; i++) {
196                 crcdata = tvb_get_guint8(tvb, offset+i);
197                 crc8 = CRC_Calc_Header(crcdata, crc8);
198         }
199         crc8 = ~crc8;
200         framecrc8 = tvb_get_guint8(tvb, offset+5);
201         if (framecrc8 == crc8) {
202                 item = proto_tree_add_uint_format(subtree, hf_mstp_frame_crc8,
203                         tvb, offset+5, 1, framecrc8,
204                         "Header CRC: 0x%02x [correct]", framecrc8);
205                 checksum_tree = proto_item_add_subtree(item, ett_bacnet_mstp_checksum);
206                 item = proto_tree_add_boolean(checksum_tree,
207                         hf_mstp_frame_checksum_good,
208                         tvb, offset+5, 1, TRUE);
209                 PROTO_ITEM_SET_GENERATED(item);
210                 item = proto_tree_add_boolean(checksum_tree,
211                         hf_mstp_frame_checksum_bad,
212                         tvb, offset+5, 1, FALSE);
213                 PROTO_ITEM_SET_GENERATED(item);
214         } else {
215                 item = proto_tree_add_uint_format(subtree, hf_mstp_frame_crc8,
216                         tvb, offset+5, 1, framecrc8,
217                         "Header CRC: 0x%02x [incorrect, should be 0x%02x]",
218                         framecrc8, crc8);
219                 checksum_tree = proto_item_add_subtree(item, ett_bacnet_mstp_checksum);
220                 item = proto_tree_add_boolean(checksum_tree,
221                         hf_mstp_frame_checksum_good,
222                         tvb, offset+5, 1, FALSE);
223                 PROTO_ITEM_SET_GENERATED(item);
224                 item = proto_tree_add_boolean(checksum_tree,
225                         hf_mstp_frame_checksum_bad,
226                         tvb, offset+5, 1, TRUE);
227                 PROTO_ITEM_SET_GENERATED(item);
228                 expert_add_info_format(pinfo, item, PI_CHECKSUM, PI_ERROR,
229                         "Bad Checksum");
230         }
231 #else
232         proto_tree_add_item(subtree, hf_mstp_frame_crc8,
233                 tvb, offset+5, 1, TRUE);
234 #endif
235
236         /* dissect BACnet PDU if there is one */
237         offset += 6;
238         if (mstp_tvb_pdu_len > 2) {
239                 /* remove the 16-bit crc checksum bytes */
240                 mstp_tvb_pdu_len -= 2;
241                 if (mstp_frame_type < 128) {
242                         vendorid = 0;
243                         next_tvb = tvb_new_subset(tvb, offset,
244                                 mstp_tvb_pdu_len, mstp_frame_pdu_len);
245                 } else {
246                         /* With Vendor ID */
247                         vendorid = tvb_get_ntohs(tvb, offset);
248                         
249                         /* Write Vendor ID as tree */
250                         proto_tree_add_item(subtree, hf_mstp_frame_vendor_id, tvb,
251                                 offset, 2, FALSE);
252
253                         /* NPDU - call the Vendor specific dissector */
254                         next_tvb = tvb_new_subset(tvb, offset+2,
255                                 mstp_tvb_pdu_len-2, mstp_frame_pdu_len);
256                 }
257
258                 if (!(dissector_try_port(subdissector_table, (vendorid<<16) + mstp_frame_type, 
259                         next_tvb, pinfo, tree))) {
260                                 /* Unknown function - dissect the payload as data */
261                                 call_dissector(data_handle, next_tvb, pinfo, tree);
262                 }
263 #if defined(BACNET_MSTP_CHECKSUM_VALIDATE)
264                 /* 16-bit checksum - calculate to validate */
265                 max_len = min(mstp_frame_pdu_len, mstp_tvb_pdu_len);
266                 for (i = 0; i < max_len; i++) {
267                         crcdata = tvb_get_guint8(tvb, offset+i);
268                         crc16 = CRC_Calc_Data(crcdata, crc16);
269                 }
270                 crc16 = ~crc16;
271                 /* convert it to on-the-wire format */
272                 crc16 = g_htons(crc16);
273                 /* get the actual CRC from the frame */
274                 framecrc16 = tvb_get_ntohs(tvb, offset+mstp_frame_pdu_len);
275                 if (framecrc16 == crc16) {
276                         item = proto_tree_add_uint_format(subtree, hf_mstp_frame_crc16,
277                                 tvb, offset+mstp_frame_pdu_len, 2, framecrc16,
278                                 "Data CRC: 0x%04x [correct]", framecrc16);
279                         checksum_tree = proto_item_add_subtree(item,
280                                 ett_bacnet_mstp_checksum);
281                         item = proto_tree_add_boolean(checksum_tree,
282                                 hf_mstp_frame_checksum_good,
283                                 tvb, offset+mstp_frame_pdu_len, 2, TRUE);
284                         PROTO_ITEM_SET_GENERATED(item);
285                         item = proto_tree_add_boolean(checksum_tree,
286                                 hf_mstp_frame_checksum_bad,
287                                 tvb, offset+mstp_frame_pdu_len, 2, FALSE);
288                         PROTO_ITEM_SET_GENERATED(item);
289                 } else {
290                         item = proto_tree_add_uint_format(subtree, hf_mstp_frame_crc16,
291                                 tvb, offset+mstp_frame_pdu_len, 2, framecrc16,
292                                 "Data CRC: 0x%04x [incorrect, should be 0x%04x]",
293                                 framecrc16, crc16);
294                         checksum_tree = proto_item_add_subtree(item,
295                                 ett_bacnet_mstp_checksum);
296                         item = proto_tree_add_boolean(checksum_tree,
297                                 hf_mstp_frame_checksum_good,
298                                 tvb, offset+mstp_frame_pdu_len, 2, FALSE);
299                         PROTO_ITEM_SET_GENERATED(item);
300                         item = proto_tree_add_boolean(checksum_tree,
301                                 hf_mstp_frame_checksum_bad,
302                                 tvb, offset+mstp_frame_pdu_len, 2, TRUE);
303                         PROTO_ITEM_SET_GENERATED(item);
304                         expert_add_info_format(pinfo, item, PI_CHECKSUM, PI_ERROR,
305                                 "Bad Checksum");
306                 }
307 #else
308                 proto_tree_add_item(subtree, hf_mstp_frame_crc16,
309                         tvb, offset+mstp_frame_pdu_len, 2, TRUE);
310 #endif
311         }
312 }
313
314 static void
315 dissect_mstp_wtap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
316 {
317         proto_item *ti;
318         proto_tree *subtree;
319         gint offset = 0;
320 #ifdef BACNET_MSTP_SUMMARY_IN_TREE
321         guint8 mstp_frame_type = 0;
322         guint8 mstp_frame_source = 0;
323         guint8 mstp_frame_destination = 0;
324 #endif
325
326         /* set the MS/TP MAC address in the source/destination */
327         /* Use AT_ARCNET since it is similar to BACnet MS/TP */
328         SET_ADDRESS(&pinfo->dl_dst,     AT_ARCNET, 1, tvb_get_ptr(tvb, offset+3, 1));
329         SET_ADDRESS(&pinfo->dst,        AT_ARCNET, 1, tvb_get_ptr(tvb, offset+3, 1));
330         SET_ADDRESS(&pinfo->dl_src,     AT_ARCNET, 1, tvb_get_ptr(tvb, offset+4, 1));
331         SET_ADDRESS(&pinfo->src,        AT_ARCNET, 1, tvb_get_ptr(tvb, offset+4, 1));
332
333 #ifdef BACNET_MSTP_SUMMARY_IN_TREE
334         mstp_frame_type = tvb_get_guint8(tvb, offset+2);
335         mstp_frame_destination = tvb_get_guint8(tvb, offset+3);
336         mstp_frame_source = tvb_get_guint8(tvb, offset+4);
337         ti = proto_tree_add_protocol_format(tree, proto_mstp, tvb, offset, 8,
338                 "BACnet MS/TP, Src (%u), Dst (%u), %s",
339                 mstp_frame_source, mstp_frame_destination,
340                 mstp_frame_type_text(mstp_frame_type));
341 #else
342         ti = proto_tree_add_item(tree, proto_mstp, tvb, offset, 8, FALSE);
343 #endif
344         subtree = proto_item_add_subtree(ti, ett_bacnet_mstp);
345         proto_tree_add_item(subtree, hf_mstp_preamble_55, tvb,
346                         offset, 1, TRUE);
347         proto_tree_add_item(subtree, hf_mstp_preamble_FF, tvb,
348                         offset+1, 1, TRUE);
349         dissect_mstp(tvb, pinfo, tree, subtree, offset+2);
350 }
351
352 void
353 proto_register_mstp(void)
354 {
355         static hf_register_info hf[] = {
356                 { &hf_mstp_preamble_55,
357                         { "Preamble 55", "mstp.preamble_55",
358                         FT_UINT8, BASE_HEX, NULL, 0,
359                         "MS/TP Preamble 55", HFILL }
360                 },
361                 { &hf_mstp_preamble_FF,
362                         { "Preamble FF", "mstp.preamble_FF",
363                         FT_UINT8, BASE_HEX, NULL, 0,
364                         "MS/TP Preamble FF", HFILL }
365                 },
366                 { &hf_mstp_frame_type,
367                         { "Frame Type", "mstp.frame_type",
368                         FT_UINT8, BASE_DEC, VALS(bacnet_mstp_frame_type_name), 0,
369                         "MS/TP Frame Type", HFILL }
370                 },
371                 { &hf_mstp_frame_destination,
372                         { "Destination Address", "mstp.dst",
373                         FT_UINT8, BASE_DEC, NULL, 0,
374                         "Destination MS/TP MAC Address", HFILL }
375                 },
376                 { &hf_mstp_frame_source,
377                         { "Source Address", "mstp.src",
378                         FT_UINT8, BASE_DEC, NULL, 0,
379                         "Source MS/TP MAC Address", HFILL }
380                 },
381                 { &hf_mstp_frame_vendor_id,
382                         { "VendorID", "mstp.vendorid",
383                         FT_UINT16, BASE_DEC, NULL, 0,
384                         "MS/TP Vendor ID of proprietary frametypes", HFILL }
385                 },
386                 { &hf_mstp_frame_pdu_len,
387                         { "Length", "mstp.len",
388                         FT_UINT16, BASE_DEC, NULL, 0,
389                         "MS/TP Data Length", HFILL }
390                 },
391                 { &hf_mstp_frame_crc8,
392                         { "Header CRC",  "mstp.hdr_crc",
393                         FT_UINT8, BASE_HEX, NULL, 0,
394                         "MS/TP Header CRC", HFILL }
395                 },
396                 { &hf_mstp_frame_crc16,
397                         { "Data CRC",  "mstp.data_crc",
398                         FT_UINT16, BASE_HEX, NULL, 0,
399                         "MS/TP Data CRC", HFILL }
400                 },
401                 { &hf_mstp_frame_checksum_bad,
402                         { "Bad", "mstp.checksum_bad",
403                         FT_BOOLEAN, BASE_NONE,  NULL, 0x0,
404                         "True: checksum doesn't match packet content; False: matches content or not checked", HFILL }
405                 },
406                 { &hf_mstp_frame_checksum_good,
407                         { "Good", "mstp.checksum_good",
408                         FT_BOOLEAN, BASE_NONE,  NULL, 0x0,
409                         "True: checksum matches packet content; False: doesn't match content or not checked", HFILL }
410                 }
411         };
412
413         static gint *ett[] = {
414                 &ett_bacnet_mstp,
415                 &ett_bacnet_mstp_checksum
416         };
417
418         proto_mstp = proto_register_protocol("BACnet MS/TP",
419             "BACnet MS/TP", "mstp");
420
421         proto_register_field_array(proto_mstp, hf, array_length(hf));
422         proto_register_subtree_array(ett, array_length(ett));
423
424         register_dissector("mstp", dissect_mstp_wtap, proto_mstp);
425                 
426         subdissector_table = register_dissector_table("mstp.vendor_frame_type",
427             "MSTP Vendor specific Frametypes", FT_UINT24, BASE_DEC);
428         /* Table_type: (Vendor ID << 16) + Frametype */
429 }
430
431 void
432
433 proto_reg_handoff_mstp(void)
434 {
435         dissector_handle_t mstp_handle;
436         dissector_handle_t bacnet_handle;
437
438         mstp_handle = find_dissector("mstp");
439         dissector_add("wtap_encap", WTAP_ENCAP_BACNET_MS_TP, mstp_handle);
440
441         bacnet_handle = find_dissector("bacnet");
442         data_handle = find_dissector("data");
443
444         dissector_add("mstp.vendor_frame_type", (0/*VendorID ASHRAE*/ << 16) + MSTP_BACNET_DATA_EXPECTING_REPLY, bacnet_handle);
445         dissector_add("mstp.vendor_frame_type", (0/*VendorID ASHRAE*/ << 16) + MSTP_BACNET_DATA_NOT_EXPECTING_REPLY, bacnet_handle);
446 }