diff options
Diffstat (limited to 'epan/dissectors/packet-mstp.c')
-rw-r--r-- | epan/dissectors/packet-mstp.c | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/epan/dissectors/packet-mstp.c b/epan/dissectors/packet-mstp.c new file mode 100644 index 0000000000..5d2df276e7 --- /dev/null +++ b/epan/dissectors/packet-mstp.c @@ -0,0 +1,324 @@ +/* packet-mstp.c + * Routines for BACnet MS/TP datalink dissection + * Copyright 2008 Steve Karg <skarg@users.sourceforge.net> Alabama + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <glib.h> + +#include <epan/packet.h> +#include <epan/oui.h> +#include <epan/llcsaps.h> +#include "packet-llc.h" +#include "packet-mstp.h" + +/* MS/TP Frame Type */ +/* Frame Types 8 through 127 are reserved by ASHRAE. */ +#define MSTP_TOKEN 0 +#define MSTP_POLL_FOR_MASTER 1 +#define MSTP_REPLY_TO_POLL_FOR_MASTER 2 +#define MSTP_TEST_REQUEST 3 +#define MSTP_TEST_RESPONSE 4 +#define MSTP_BACNET_DATA_EXPECTING_REPLY 5 +#define MSTP_BACNET_DATA_NOT_EXPECTING_REPLY 6 +#define MSTP_REPLY_POSTPONED 7 + +static const value_string +bacnet_mstp_frame_type_name[] = { + {MSTP_TOKEN, "Token"}, + {MSTP_POLL_FOR_MASTER, "Poll For Master"}, + {MSTP_REPLY_TO_POLL_FOR_MASTER, "Reply To Poll For Master"}, + {MSTP_TEST_REQUEST, "Test_Request"}, + {MSTP_TEST_RESPONSE, "Test_Response"}, + {MSTP_BACNET_DATA_EXPECTING_REPLY, "BACnet Data Expecting Reply"}, + {MSTP_BACNET_DATA_NOT_EXPECTING_REPLY, "BACnet Data Not Expecting Reply"}, + {MSTP_REPLY_POSTPONED, "Reply Postponed"}, + /* Frame Types 128 through 255: Proprietary Frames */ + {0, NULL } +}; + +static dissector_handle_t bacnet_handle; +static dissector_handle_t data_handle; + +static int proto_mstp = -1; + +static gint ett_bacnet_mstp = -1; + +static int hf_mstp_preamble_55 = -1; +static int hf_mstp_preamble_FF = -1; +static int hf_mstp_frame_type = -1; +static int hf_mstp_frame_destination = -1; +static int hf_mstp_frame_source = -1; +static int hf_mstp_frame_pdu_len = -1; +static int hf_mstp_frame_crc8 = -1; +static int hf_mstp_frame_crc16 = -1; + +#if defined(BACNET_MSTP_CHECKSUM_VALIDATE) +/* Accumulate "dataValue" into the CRC in crcValue. */ +/* Return value is updated CRC */ +/* The ^ operator means exclusive OR. */ +/* Note: This function is copied directly from the BACnet standard. */ +static guint8 CRC_Calc_Header( + guint8 dataValue, + guint8 crcValue) +{ + guint16 crc; + + crc = crcValue ^ dataValue; /* XOR C7..C0 with D7..D0 */ + + /* Exclusive OR the terms in the table (top down) */ + crc = crc ^ (crc << 1) ^ (crc << 2) ^ (crc << 3) + ^ (crc << 4) ^ (crc << 5) ^ (crc << 6) + ^ (crc << 7); + + /* Combine bits shifted out left hand end */ + return (crc & 0xfe) ^ ((crc >> 8) & 1); +} +#endif + +#if defined(BACNET_MSTP_CHECKSUM_VALIDATE) +/* Accumulate "dataValue" into the CRC in crcValue. */ +/* Return value is updated CRC */ +/* The ^ operator means exclusive OR. */ +/* Note: This function is copied directly from the BACnet standard. */ +static guint16 CRC_Calc_Data( + guint8 dataValue, + guint16 crcValue) +{ + guint16 crcLow; + + crcLow = (crcValue & 0xff) ^ dataValue; /* XOR C7..C0 with D7..D0 */ + + /* Exclusive OR the terms in the table (top down) */ + return (crcValue >> 8) ^ (crcLow << 8) ^ (crcLow << 3) + ^ (crcLow << 12) ^ (crcLow >> 4) + ^ (crcLow & 0x0f) ^ ((crcLow & 0x0f) << 7); +} +#endif + +/* dissects a BACnet MS/TP frame */ +/* preamble 0x55 0xFF is not included in Cimetrics U+4 output */ +void +dissect_mstp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + proto_tree *subtree, gint offset) +{ + guint8 mstp_frame_type = 0; + guint8 mstp_frame_source = 0; + guint8 mstp_frame_destination = 0; + guint16 mstp_frame_pdu_len = 0; + guint16 mstp_tvb_pdu_len = 0; + tvbuff_t *next_tvb = NULL; +#if defined(BACNET_MSTP_CHECKSUM_VALIDATE) + /* used to calculate the crc value */ + guint8 crc8 = 0xFF, framecrc8; + guint16 crc16 = 0xFFFF, framecrc16; + guint8 crcdata, i; + guint16 max_len = 0; +#endif + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "BACnet"); + if (check_col(pinfo->cinfo, COL_INFO)) { + col_set_str(pinfo->cinfo, COL_INFO, "BACnet MS/TP"); + } + mstp_frame_type = tvb_get_guint8(tvb, offset); + mstp_frame_destination = tvb_get_guint8(tvb, offset+1); + mstp_frame_source = tvb_get_guint8(tvb, offset+2); + mstp_frame_pdu_len = tvb_get_ntohs(tvb, offset+3); + if (check_col(pinfo->cinfo, COL_INFO)) { + col_append_fstr(pinfo->cinfo, COL_INFO, " [%02x>%02x] %s", + mstp_frame_source, + mstp_frame_destination, + val_to_str(mstp_frame_type, + bacnet_mstp_frame_type_name, + "Unknown Frame Type")); + } + proto_tree_add_item(subtree, hf_mstp_frame_type, tvb, + offset, 1, TRUE); + proto_tree_add_item(subtree, hf_mstp_frame_destination, tvb, + offset+1, 1, TRUE); + proto_tree_add_item(subtree, hf_mstp_frame_source, tvb, + offset+2, 1, TRUE); + proto_tree_add_item(subtree, hf_mstp_frame_pdu_len, tvb, + offset+3, 2, TRUE); +#if defined(BACNET_MSTP_CHECKSUM_VALIDATE) + /* calculate checksum to validate */ + for (i = 0; i < 5; i++) { + crcdata = tvb_get_guint8(tvb, offset+i); + crc8 = CRC_Calc_Header(crcdata, crc8); + } + crc8 = ~crc8; + framecrc8 = tvb_get_guint8(tvb, offset+5); + if (framecrc8 == crc8) { + proto_tree_add_uint_format(subtree, hf_mstp_frame_crc8, + tvb, offset+5, 1, framecrc8, + "Header CRC: 0x%02x [correct]", framecrc8); + } else { + proto_tree_add_uint_format(subtree, hf_mstp_frame_crc8, + tvb, offset+5, 1, framecrc8, + "Header CRC: 0x%02x [incorrect, should be %02x]", + framecrc8, crc8); + } +#else + proto_tree_add_item(subtree, hf_mstp_frame_crc8, + tvb, offset+5, 1, TRUE); +#endif + + /* dissect BACnet PDU if there is one */ + offset += 6; + mstp_tvb_pdu_len = tvb_length_remaining(tvb, offset); + if (mstp_tvb_pdu_len > 2) { + /* remove the 16-bit crc checksum bytes */ + mstp_tvb_pdu_len -= 2; + next_tvb = tvb_new_subset(tvb, offset, + mstp_tvb_pdu_len, mstp_frame_pdu_len); + if ((mstp_frame_type == MSTP_BACNET_DATA_EXPECTING_REPLY) || + (mstp_frame_type == MSTP_BACNET_DATA_NOT_EXPECTING_REPLY)) { + /* NPDU - call the BACnet NPDU dissector */ + call_dissector(bacnet_handle, next_tvb, pinfo, tree); + } else { + /* Unknown function - dissect the payload as data */ + call_dissector(data_handle, next_tvb, pinfo, tree); + } +#if defined(BACNET_MSTP_CHECKSUM_VALIDATE) + /* 16-bit checksum - calculate to validate */ + max_len = min(mstp_frame_pdu_len, mstp_tvb_pdu_len); + for (i = 0; i < max_len; i++) { + crcdata = tvb_get_guint8(tvb, offset+i); + crc16 = CRC_Calc_Data(crcdata, crc16); + } + crc16 = ~crc16; + /* convert it to on-the-wire format */ + crc16 = g_htons(crc16); + /* get the actual CRC from the frame */ + framecrc16 = tvb_get_ntohs(tvb, offset+mstp_frame_pdu_len); + if (framecrc16 == crc16) { + proto_tree_add_uint_format(subtree, hf_mstp_frame_crc16, + tvb, offset+mstp_frame_pdu_len, 2, framecrc16, + "Data CRC: 0x%04x [correct]", framecrc16); + } else { + proto_tree_add_uint_format(subtree, hf_mstp_frame_crc16, + tvb, offset+mstp_frame_pdu_len, 2, framecrc16, + "Data CRC: 0x%04x [incorrect, should be %04x]", + framecrc16, crc16); + } +#else + proto_tree_add_item(subtree, hf_mstp_frame_crc16, + tvb, offset+mstp_frame_pdu_len, 2, TRUE); +#endif + } +} + +static void +dissect_mstp_wtap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + proto_item *ti; + proto_tree *subtree; + + gint offset = 0; + + ti = proto_tree_add_item(tree, proto_mstp, tvb, offset, 8, FALSE); + subtree = proto_item_add_subtree(ti, ett_bacnet_mstp); + proto_tree_add_item(subtree, hf_mstp_preamble_55, tvb, + offset, 1, TRUE); + proto_tree_add_item(subtree, hf_mstp_preamble_FF, tvb, + offset+1, 1, TRUE); + dissect_mstp(tvb, pinfo, tree, subtree, offset+2); +} + +void +proto_register_mstp(void) +{ + static hf_register_info hf[] = { + { &hf_mstp_preamble_55, + { "Preamble 55", "mstp.preamble_55", + FT_UINT8, BASE_HEX, NULL, 0, + "MS/TP Preamble 55", HFILL } + }, + { &hf_mstp_preamble_FF, + { "Preamble FF", "mstp.preamble_FF", + FT_UINT8, BASE_HEX, NULL, 0, + "MS/TP Preamble FF", HFILL } + }, + { &hf_mstp_frame_type, + { "Frame Type", "mstp.frame_type", + FT_UINT8, BASE_DEC, VALS(bacnet_mstp_frame_type_name), 0, + "MS/TP Frame Type", HFILL } + }, + { &hf_mstp_frame_destination, + { "Destination Address", "mstp.destination", + FT_UINT8, BASE_DEC, NULL, 0, + "Destination MS/TP MAC Address", HFILL } + }, + { &hf_mstp_frame_source, + { "Source Address", "mstp.source", + FT_UINT8, BASE_DEC, NULL, 0, + "Source MS/TP MAC Address", HFILL } + }, + { &hf_mstp_frame_pdu_len, + { "Length", "mstp.length", + FT_UINT16, BASE_DEC, NULL, 0, + "MS/TP Frame Data Length", HFILL } + }, + { &hf_mstp_frame_crc8, + { "Header CRC", "mstp.header_crc", + FT_UINT8, BASE_HEX, NULL, 0, + "MS/TP Header CRC", HFILL } + }, + { &hf_mstp_frame_crc16, + { "Data CRC", "mstp.data_crc", + FT_UINT16, BASE_HEX, NULL, 0, + "MS/TP Data CRC", HFILL } + } + }; + + static gint *ett[] = { + &ett_bacnet_mstp + }; + + proto_mstp = proto_register_protocol("BACnet MS/TP", + "BACnet MS/TP", "mstp"); + + proto_register_field_array(proto_mstp, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + + register_dissector("mstp", dissect_mstp_wtap, proto_mstp); +} + +void +proto_reg_handoff_mstp(void) +{ + dissector_handle_t mstp_handle; + + mstp_handle = find_dissector("mstp"); + dissector_add("wtap_encap", WTAP_ENCAP_BACNET_MS_TP, mstp_handle); + bacnet_handle = find_dissector("bacnet"); + data_handle = find_dissector("data"); +} |