MSWSP: add two more Property Sets
[wireshark-wip.git] / epan / dissectors / packet-mstp.c
blobe15c4923ac7c99d35d26f7d224bd0b5617646e30
1 /* packet-mstp.c
2 * Routines for BACnet MS/TP datalink dissection
3 * Copyright 2008 Steve Karg <skarg@users.sourceforge.net> Alabama
5 * This is described in Clause 9 of ANSI/ASHRAE Standard 135-2004,
6 * BACnet - A Data Communication Protocol for Building Automation
7 * and Contrl Networks; clause 9 "describes a Master-Slave/Token-Passing
8 * (MS/TP) data link protocol, which provides the same services to the
9 * network layer as ISO 8802-2 Logical Link Control. It uses services
10 * provided by the EIA-485 physical layer." See section 9.3 for the
11 * frame format.
13 * $Id$
15 * Wireshark - Network traffic analyzer
16 * By Gerald Combs <gerald@wireshark.org>
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
34 #include "config.h"
36 #include <glib.h>
38 #include <epan/packet.h>
39 #include <epan/oui.h>
40 #include <epan/llcsaps.h>
41 #include <epan/expert.h>
42 #include "packet-llc.h"
43 #include "packet-mstp.h"
45 /* Probably should be a preference, but here for now */
46 #define BACNET_MSTP_SUMMARY_IN_TREE
47 #define BACNET_MSTP_CHECKSUM_VALIDATE
49 /* MS/TP Frame Type */
50 /* Frame Types 8 through 127 are reserved by ASHRAE. */
51 #define MSTP_TOKEN 0
52 #define MSTP_POLL_FOR_MASTER 1
53 #define MSTP_REPLY_TO_POLL_FOR_MASTER 2
54 #define MSTP_TEST_REQUEST 3
55 #define MSTP_TEST_RESPONSE 4
56 #define MSTP_BACNET_DATA_EXPECTING_REPLY 5
57 #define MSTP_BACNET_DATA_NOT_EXPECTING_REPLY 6
58 #define MSTP_REPLY_POSTPONED 7
60 static const value_string
61 bacnet_mstp_frame_type_name[] = {
62 {MSTP_TOKEN, "Token"},
63 {MSTP_POLL_FOR_MASTER, "Poll For Master"},
64 {MSTP_REPLY_TO_POLL_FOR_MASTER, "Reply To Poll For Master"},
65 {MSTP_TEST_REQUEST, "Test_Request"},
66 {MSTP_TEST_RESPONSE, "Test_Response"},
67 {MSTP_BACNET_DATA_EXPECTING_REPLY, "BACnet Data Expecting Reply"},
68 {MSTP_BACNET_DATA_NOT_EXPECTING_REPLY, "BACnet Data Not Expecting Reply"},
69 {MSTP_REPLY_POSTPONED, "Reply Postponed"},
70 /* Frame Types 128 through 255: Proprietary Frames */
71 {0, NULL }
74 static dissector_handle_t data_handle;
75 static dissector_table_t subdissector_table;
77 static int proto_mstp = -1;
79 static gint ett_bacnet_mstp = -1;
80 static gint ett_bacnet_mstp_checksum = -1;
82 static int hf_mstp_preamble_55 = -1;
83 static int hf_mstp_preamble_FF = -1;
84 static int hf_mstp_frame_type = -1;
85 static int hf_mstp_frame_destination = -1;
86 static int hf_mstp_frame_source = -1;
87 static int hf_mstp_frame_vendor_id = -1;
88 static int hf_mstp_frame_pdu_len = -1;
89 static int hf_mstp_frame_crc8 = -1;
90 static int hf_mstp_frame_crc16 = -1;
91 static int hf_mstp_frame_checksum_bad = -1;
92 static int hf_mstp_frame_checksum_good = -1;
94 static expert_field ei_mstp_frame_pdu_len = EI_INIT;
95 static expert_field ei_mstp_frame_checksum_bad = EI_INIT;
98 #if defined(BACNET_MSTP_CHECKSUM_VALIDATE)
99 /* Accumulate "dataValue" into the CRC in crcValue. */
100 /* Return value is updated CRC */
101 /* The ^ operator means exclusive OR. */
102 /* Note: This function is copied directly from the BACnet standard. */
103 static guint8
104 CRC_Calc_Header(
105 guint8 dataValue,
106 guint8 crcValue)
108 guint16 crc;
110 crc = crcValue ^ dataValue; /* XOR C7..C0 with D7..D0 */
112 /* Exclusive OR the terms in the table (top down) */
113 crc = crc ^ (crc << 1) ^ (crc << 2) ^ (crc << 3)
114 ^ (crc << 4) ^ (crc << 5) ^ (crc << 6)
115 ^ (crc << 7);
117 /* Combine bits shifted out left hand end */
118 return (crc & 0xfe) ^ ((crc >> 8) & 1);
120 #endif
122 #if defined(BACNET_MSTP_CHECKSUM_VALIDATE)
123 /* Accumulate "dataValue" into the CRC in crcValue. */
124 /* Return value is updated CRC */
125 /* The ^ operator means exclusive OR. */
126 /* Note: This function is copied directly from the BACnet standard. */
127 static guint16
128 CRC_Calc_Data(
129 guint8 dataValue,
130 guint16 crcValue)
132 guint16 crcLow;
134 crcLow = (crcValue & 0xff) ^ dataValue; /* XOR C7..C0 with D7..D0 */
136 /* Exclusive OR the terms in the table (top down) */
137 return (crcValue >> 8) ^ (crcLow << 8) ^ (crcLow << 3)
138 ^ (crcLow << 12) ^ (crcLow >> 4)
139 ^ (crcLow & 0x0f) ^ ((crcLow & 0x0f) << 7);
141 #endif
143 /* Common frame type text */
144 const gchar *
145 mstp_frame_type_text(guint32 val)
147 return val_to_str(val,
148 bacnet_mstp_frame_type_name,
149 "Unknown Frame Type (%u)");
152 /* dissects a BACnet MS/TP frame */
153 /* preamble 0x55 0xFF is not included in Cimetrics U+4 output */
154 void
155 dissect_mstp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
156 proto_tree *subtree, gint offset)
158 guint8 mstp_frame_type = 0;
159 guint16 mstp_frame_pdu_len = 0;
160 guint16 mstp_tvb_pdu_len = 0;
161 guint16 vendorid = 0;
162 tvbuff_t *next_tvb = NULL;
163 proto_item *item;
164 #if defined(BACNET_MSTP_CHECKSUM_VALIDATE)
165 /* used to calculate the crc value */
166 guint8 crc8 = 0xFF, framecrc8;
167 guint16 crc16 = 0xFFFF, framecrc16;
168 guint8 crcdata;
169 guint16 i; /* loop counter */
170 guint16 max_len = 0;
171 proto_tree *checksum_tree;
172 #endif
174 col_set_str(pinfo->cinfo, COL_PROTOCOL, "BACnet");
175 col_set_str(pinfo->cinfo, COL_INFO, "BACnet MS/TP");
176 mstp_frame_type = tvb_get_guint8(tvb, offset);
177 mstp_frame_pdu_len = tvb_get_ntohs(tvb, offset+3);
178 col_append_fstr(pinfo->cinfo, COL_INFO, " %s",
179 mstp_frame_type_text(mstp_frame_type));
181 /* Add the items to the tree */
182 proto_tree_add_item(subtree, hf_mstp_frame_type, tvb,
183 offset, 1, ENC_LITTLE_ENDIAN);
184 proto_tree_add_item(subtree, hf_mstp_frame_destination, tvb,
185 offset+1, 1, ENC_LITTLE_ENDIAN);
186 proto_tree_add_item(subtree, hf_mstp_frame_source, tvb,
187 offset+2, 1, ENC_LITTLE_ENDIAN);
188 item = proto_tree_add_item(subtree, hf_mstp_frame_pdu_len, tvb,
189 offset+3, 2, ENC_BIG_ENDIAN);
190 mstp_tvb_pdu_len = tvb_length_remaining(tvb, offset+6);
191 /* check the length - which does not include the crc16 checksum */
192 if (mstp_tvb_pdu_len > 2) {
193 if (mstp_frame_pdu_len > (mstp_tvb_pdu_len-2)) {
194 expert_add_info(pinfo, item, &ei_mstp_frame_pdu_len);
197 #if defined(BACNET_MSTP_CHECKSUM_VALIDATE)
198 /* calculate checksum to validate */
199 for (i = 0; i < 5; i++) {
200 crcdata = tvb_get_guint8(tvb, offset+i);
201 crc8 = CRC_Calc_Header(crcdata, crc8);
203 crc8 = ~crc8;
204 framecrc8 = tvb_get_guint8(tvb, offset+5);
205 if (framecrc8 == crc8) {
206 item = proto_tree_add_uint_format_value(subtree, hf_mstp_frame_crc8,
207 tvb, offset+5, 1, framecrc8,
208 "0x%02x [correct]", framecrc8);
209 checksum_tree = proto_item_add_subtree(item, ett_bacnet_mstp_checksum);
210 item = proto_tree_add_boolean(checksum_tree,
211 hf_mstp_frame_checksum_good,
212 tvb, offset+5, 1, TRUE);
213 PROTO_ITEM_SET_GENERATED(item);
214 item = proto_tree_add_boolean(checksum_tree,
215 hf_mstp_frame_checksum_bad,
216 tvb, offset+5, 1, FALSE);
217 PROTO_ITEM_SET_GENERATED(item);
218 } else {
219 item = proto_tree_add_uint_format_value(subtree, hf_mstp_frame_crc8,
220 tvb, offset+5, 1, framecrc8,
221 "0x%02x [incorrect, should be 0x%02x]",
222 framecrc8, crc8);
223 checksum_tree = proto_item_add_subtree(item, ett_bacnet_mstp_checksum);
224 item = proto_tree_add_boolean(checksum_tree,
225 hf_mstp_frame_checksum_good,
226 tvb, offset+5, 1, FALSE);
227 PROTO_ITEM_SET_GENERATED(item);
228 item = proto_tree_add_boolean(checksum_tree,
229 hf_mstp_frame_checksum_bad,
230 tvb, offset+5, 1, TRUE);
231 PROTO_ITEM_SET_GENERATED(item);
232 expert_add_info(pinfo, item, &ei_mstp_frame_checksum_bad);
234 #else
235 proto_tree_add_item(subtree, hf_mstp_frame_crc8,
236 tvb, offset+5, 1, ENC_LITTLE_ENDIAN);
237 #endif
239 /* dissect BACnet PDU if there is one */
240 offset += 6;
241 if (mstp_tvb_pdu_len > 2) {
242 /* remove the 16-bit crc checksum bytes */
243 mstp_tvb_pdu_len -= 2;
244 if (mstp_frame_type < 128) {
245 vendorid = 0;
246 next_tvb = tvb_new_subset(tvb, offset,
247 mstp_tvb_pdu_len, mstp_frame_pdu_len);
248 } else {
249 /* With Vendor ID */
250 vendorid = tvb_get_ntohs(tvb, offset);
252 /* Write Vendor ID as tree */
253 proto_tree_add_item(subtree, hf_mstp_frame_vendor_id, tvb,
254 offset, 2, ENC_BIG_ENDIAN);
256 /* NPDU - call the Vendor specific dissector */
257 next_tvb = tvb_new_subset(tvb, offset+2,
258 mstp_tvb_pdu_len-2, mstp_frame_pdu_len);
261 if (!(dissector_try_uint(subdissector_table, (vendorid<<16) + mstp_frame_type,
262 next_tvb, pinfo, tree))) {
263 /* Unknown function - dissect the payload as data */
264 call_dissector(data_handle, next_tvb, pinfo, tree);
266 #if defined(BACNET_MSTP_CHECKSUM_VALIDATE)
267 /* 16-bit checksum - calculate to validate */
268 max_len = MIN(mstp_frame_pdu_len, mstp_tvb_pdu_len);
269 for (i = 0; i < max_len; i++) {
270 crcdata = tvb_get_guint8(tvb, offset+i);
271 crc16 = CRC_Calc_Data(crcdata, crc16);
273 crc16 = ~crc16;
274 /* convert it to on-the-wire format */
275 crc16 = g_htons(crc16);
276 /* get the actual CRC from the frame */
277 framecrc16 = tvb_get_ntohs(tvb, offset+mstp_frame_pdu_len);
278 if (framecrc16 == crc16) {
279 item = proto_tree_add_uint_format_value(subtree, hf_mstp_frame_crc16,
280 tvb, offset+mstp_frame_pdu_len, 2, framecrc16,
281 "0x%04x [correct]", framecrc16);
282 checksum_tree = proto_item_add_subtree(item,
283 ett_bacnet_mstp_checksum);
284 item = proto_tree_add_boolean(checksum_tree,
285 hf_mstp_frame_checksum_good,
286 tvb, offset+mstp_frame_pdu_len, 2, TRUE);
287 PROTO_ITEM_SET_GENERATED(item);
288 item = proto_tree_add_boolean(checksum_tree,
289 hf_mstp_frame_checksum_bad,
290 tvb, offset+mstp_frame_pdu_len, 2, FALSE);
291 PROTO_ITEM_SET_GENERATED(item);
292 } else {
293 item = proto_tree_add_uint_format_value(subtree, hf_mstp_frame_crc16,
294 tvb, offset+mstp_frame_pdu_len, 2, framecrc16,
295 "0x%04x [incorrect, should be 0x%04x]",
296 framecrc16, crc16);
297 checksum_tree = proto_item_add_subtree(item,
298 ett_bacnet_mstp_checksum);
299 item = proto_tree_add_boolean(checksum_tree,
300 hf_mstp_frame_checksum_good,
301 tvb, offset+mstp_frame_pdu_len, 2, FALSE);
302 PROTO_ITEM_SET_GENERATED(item);
303 item = proto_tree_add_boolean(checksum_tree,
304 hf_mstp_frame_checksum_bad,
305 tvb, offset+mstp_frame_pdu_len, 2, TRUE);
306 PROTO_ITEM_SET_GENERATED(item);
307 expert_add_info(pinfo, item, &ei_mstp_frame_checksum_bad);
309 #else
310 proto_tree_add_item(subtree, hf_mstp_frame_crc16,
311 tvb, offset+mstp_frame_pdu_len, 2, ENC_LITTLE_ENDIAN);
312 #endif
316 static void
317 dissect_mstp_wtap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
319 proto_item *ti;
320 proto_tree *subtree;
321 gint offset = 0;
322 #ifdef BACNET_MSTP_SUMMARY_IN_TREE
323 guint8 mstp_frame_type = 0;
324 guint8 mstp_frame_source = 0;
325 guint8 mstp_frame_destination = 0;
326 #endif
328 /* set the MS/TP MAC address in the source/destination */
329 /* Use AT_ARCNET since it is similar to BACnet MS/TP */
330 TVB_SET_ADDRESS(&pinfo->dl_dst, AT_ARCNET, tvb, offset+3, 1);
331 TVB_SET_ADDRESS(&pinfo->dst, AT_ARCNET, tvb, offset+3, 1);
332 TVB_SET_ADDRESS(&pinfo->dl_src, AT_ARCNET, tvb, offset+4, 1);
333 TVB_SET_ADDRESS(&pinfo->src, AT_ARCNET, tvb, offset+4, 1);
335 #ifdef BACNET_MSTP_SUMMARY_IN_TREE
336 mstp_frame_type = tvb_get_guint8(tvb, offset+2);
337 mstp_frame_destination = tvb_get_guint8(tvb, offset+3);
338 mstp_frame_source = tvb_get_guint8(tvb, offset+4);
339 ti = proto_tree_add_protocol_format(tree, proto_mstp, tvb, offset, 8,
340 "BACnet MS/TP, Src (%u), Dst (%u), %s",
341 mstp_frame_source, mstp_frame_destination,
342 mstp_frame_type_text(mstp_frame_type));
343 #else
344 ti = proto_tree_add_item(tree, proto_mstp, tvb, offset, 8, ENC_NA);
345 #endif
346 subtree = proto_item_add_subtree(ti, ett_bacnet_mstp);
347 proto_tree_add_item(subtree, hf_mstp_preamble_55, tvb,
348 offset, 1, ENC_LITTLE_ENDIAN);
349 proto_tree_add_item(subtree, hf_mstp_preamble_FF, tvb,
350 offset+1, 1, ENC_LITTLE_ENDIAN);
351 dissect_mstp(tvb, pinfo, tree, subtree, offset+2);
354 void
355 proto_register_mstp(void)
357 static hf_register_info hf[] = {
358 { &hf_mstp_preamble_55,
359 { "Preamble 55", "mstp.preamble_55",
360 FT_UINT8, BASE_HEX, NULL, 0,
361 "MS/TP Preamble 55", HFILL }
363 { &hf_mstp_preamble_FF,
364 { "Preamble FF", "mstp.preamble_FF",
365 FT_UINT8, BASE_HEX, NULL, 0,
366 "MS/TP Preamble FF", HFILL }
368 { &hf_mstp_frame_type,
369 { "Frame Type", "mstp.frame_type",
370 FT_UINT8, BASE_DEC, VALS(bacnet_mstp_frame_type_name), 0,
371 "MS/TP Frame Type", HFILL }
373 { &hf_mstp_frame_destination,
374 { "Destination Address", "mstp.dst",
375 FT_UINT8, BASE_DEC, NULL, 0,
376 "Destination MS/TP MAC Address", HFILL }
378 { &hf_mstp_frame_source,
379 { "Source Address", "mstp.src",
380 FT_UINT8, BASE_DEC, NULL, 0,
381 "Source MS/TP MAC Address", HFILL }
383 { &hf_mstp_frame_vendor_id,
384 { "VendorID", "mstp.vendorid",
385 FT_UINT16, BASE_DEC, NULL, 0,
386 "MS/TP Vendor ID of proprietary frametypes", HFILL }
388 { &hf_mstp_frame_pdu_len,
389 { "Length", "mstp.len",
390 FT_UINT16, BASE_DEC, NULL, 0,
391 "MS/TP Data Length", HFILL }
393 { &hf_mstp_frame_crc8,
394 { "Header CRC", "mstp.hdr_crc",
395 FT_UINT8, BASE_HEX, NULL, 0,
396 "MS/TP Header CRC", HFILL }
398 { &hf_mstp_frame_crc16,
399 { "Data CRC", "mstp.data_crc",
400 FT_UINT16, BASE_HEX, NULL, 0,
401 "MS/TP Data CRC", HFILL }
403 { &hf_mstp_frame_checksum_bad,
404 { "Bad", "mstp.checksum_bad",
405 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
406 "True: checksum doesn't match packet content; False: matches content or not checked", HFILL }
408 { &hf_mstp_frame_checksum_good,
409 { "Good", "mstp.checksum_good",
410 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
411 "True: checksum matches packet content; False: doesn't match content or not checked", HFILL }
415 static gint *ett[] = {
416 &ett_bacnet_mstp,
417 &ett_bacnet_mstp_checksum
420 static ei_register_info ei[] = {
421 { &ei_mstp_frame_pdu_len, { "mstp.len.bad", PI_MALFORMED, PI_ERROR, "Length field value goes past the end of the payload", EXPFILL }},
422 { &ei_mstp_frame_checksum_bad, { "mstp.checksum_bad.expert", PI_CHECKSUM, PI_WARN, "Bad Checksum", EXPFILL }},
425 expert_module_t* expert_mstp;
427 proto_mstp = proto_register_protocol("BACnet MS/TP",
428 "BACnet MS/TP", "mstp");
430 proto_register_field_array(proto_mstp, hf, array_length(hf));
431 proto_register_subtree_array(ett, array_length(ett));
432 expert_mstp = expert_register_protocol(proto_mstp);
433 expert_register_field_array(expert_mstp, ei, array_length(ei));
435 register_dissector("mstp", dissect_mstp_wtap, proto_mstp);
437 subdissector_table = register_dissector_table("mstp.vendor_frame_type",
438 "MSTP Vendor specific Frametypes", FT_UINT24, BASE_DEC);
439 /* Table_type: (Vendor ID << 16) + Frametype */
442 void
443 proto_reg_handoff_mstp(void)
445 dissector_handle_t mstp_handle;
446 dissector_handle_t bacnet_handle;
448 mstp_handle = find_dissector("mstp");
449 dissector_add_uint("wtap_encap", WTAP_ENCAP_BACNET_MS_TP, mstp_handle);
450 dissector_add_uint("wtap_encap", WTAP_ENCAP_BACNET_MS_TP_WITH_PHDR, mstp_handle);
452 bacnet_handle = find_dissector("bacnet");
453 data_handle = find_dissector("data");
455 dissector_add_uint("mstp.vendor_frame_type", (0/*VendorID ASHRAE*/ << 16) + MSTP_BACNET_DATA_EXPECTING_REPLY, bacnet_handle);
456 dissector_add_uint("mstp.vendor_frame_type", (0/*VendorID ASHRAE*/ << 16) + MSTP_BACNET_DATA_NOT_EXPECTING_REPLY, bacnet_handle);