/src/wireshark/epan/dissectors/packet-mstp.c
Line | Count | Source |
1 | | /* packet-mstp.c |
2 | | * Routines for BACnet MS/TP datalink dissection |
3 | | * Copyright 2008 Steve Karg <skarg@users.sourceforge.net> Alabama |
4 | | * |
5 | | * This is described in Clause 9 of ANSI/ASHRAE Standard 135-2024, |
6 | | * BACnet - A Data Communication Protocol for Building Automation |
7 | | * and Control Networks; clause 9 "describes a Multidrop Serial Bus/Token |
8 | | * Passing (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. |
12 | | * |
13 | | * Wireshark - Network traffic analyzer |
14 | | * By Gerald Combs <gerald@wireshark.org> |
15 | | * Copyright 1998 Gerald Combs |
16 | | * |
17 | | * SPDX-License-Identifier: GPL-2.0-or-later |
18 | | */ |
19 | | |
20 | | #include "config.h" |
21 | | |
22 | | #include <epan/packet.h> |
23 | | #include <wiretap/wtap.h> |
24 | | #include <epan/expert.h> |
25 | | #include <epan/address_types.h> |
26 | | #include <epan/to_str.h> |
27 | | #include <epan/crc16-tvb.h> |
28 | | #include <epan/exceptions.h> |
29 | | #include "packet-mstp.h" |
30 | | |
31 | | void proto_register_mstp(void); |
32 | | void proto_reg_handoff_mstp(void); |
33 | | |
34 | | /* Probably should be a preference, but here for now */ |
35 | | #define BACNET_MSTP_SUMMARY_IN_TREE |
36 | | #define BACNET_MSTP_CHECKSUM_VALIDATE |
37 | | |
38 | | /* MS/TP Frame Type */ |
39 | | /* Frame Types 8 through 31 and 35 through 127 are reserved by ASHRAE. */ |
40 | | #define MSTP_TOKEN 0x00 |
41 | | #define MSTP_POLL_FOR_MANAGER 0x01 |
42 | | #define MSTP_REPLY_TO_POLL_FOR_MANAGER 0x02 |
43 | | #define MSTP_TEST_REQUEST 0x03 |
44 | | #define MSTP_TEST_RESPONSE 0x04 |
45 | 16 | #define MSTP_BACNET_DATA_EXPECTING_REPLY 0x05 |
46 | 16 | #define MSTP_BACNET_DATA_NOT_EXPECTING_REPLY 0x06 |
47 | | #define MSTP_REPLY_POSTPONED 0x07 |
48 | 16 | #define MSTP_BACNET_EXTENDED_DATA_EXPECTING_REPLY 0x20 |
49 | 16 | #define MSTP_BACNET_EXTENDED_DATA_NOT_EXPECTING_REPLY 0x21 |
50 | 16 | #define MSTP_IPV6_ENCAPSULATION 0x22 |
51 | | |
52 | | /* Frame Types 32 through 127 are COBS-encoded (Nmin_COBS_type through |
53 | | * Nmax_COBS_type in Clause 9). */ |
54 | 2 | #define MSTP_COBS_TYPE_MIN 32 |
55 | 1 | #define MSTP_COBS_TYPE_MAX 127 |
56 | | |
57 | | /* Valid Length of any COBS-encoded frame: Nmin_COBS_length through |
58 | | * Nmax_COBS_length (Clause 9.5.3). */ |
59 | 3 | #define MSTP_COBS_LENGTH_MIN 5 |
60 | 2 | #define MSTP_COBS_LENGTH_MAX 2043 |
61 | | |
62 | | /* BACnet NPDUs of 502 through 1497 octets go in Frame Types 32 and 33, |
63 | | * smaller ones in Frame Types 5 and 6 (Clauses 9.2 and 9.3; tested by |
64 | | * ANSI/ASHRAE 135.1 12.1.3.20). */ |
65 | 0 | #define MSTP_BACNET_EXTENDED_NPDU_MIN 502 |
66 | 0 | #define MSTP_BACNET_EXTENDED_NPDU_MAX 1497 |
67 | | |
68 | | /* Largest Length of an IPv6 over MS/TP frame: a 1500-octet IPv6 packet |
69 | | * becomes an MSDU of at most 1501 octets, plus at most 6 octets of COBS |
70 | | * overhead, plus 3 (draft-ietf-6lo-rfc8163-bis; RFC 8163 says 1509). */ |
71 | 0 | #define MSTP_IPV6_LENGTH_MAX 1510 |
72 | | |
73 | | |
74 | | static const value_string |
75 | | bacnet_mstp_frame_type_name[] = { |
76 | | {MSTP_TOKEN, "Token"}, |
77 | | {MSTP_POLL_FOR_MANAGER, "Poll For Manager"}, |
78 | | {MSTP_REPLY_TO_POLL_FOR_MANAGER, "Reply To Poll For Manager"}, |
79 | | {MSTP_TEST_REQUEST, "Test_Request"}, |
80 | | {MSTP_TEST_RESPONSE, "Test_Response"}, |
81 | | {MSTP_BACNET_DATA_EXPECTING_REPLY, "BACnet Data Expecting Reply"}, |
82 | | {MSTP_BACNET_DATA_NOT_EXPECTING_REPLY, "BACnet Data Not Expecting Reply"}, |
83 | | {MSTP_REPLY_POSTPONED, "Reply Postponed"}, |
84 | | {MSTP_BACNET_EXTENDED_DATA_EXPECTING_REPLY, "BACnet Extended Data Expecting Reply"}, |
85 | | {MSTP_BACNET_EXTENDED_DATA_NOT_EXPECTING_REPLY, "BACnet Extended Data Not Expecting Reply"}, |
86 | | {MSTP_IPV6_ENCAPSULATION, "IPv6 over MS/TP (LoBAC) Encapsulation"}, |
87 | | /* Frame Types 128 through 255: Proprietary Frames */ |
88 | | {0, NULL } |
89 | | }; |
90 | | |
91 | | static dissector_table_t subdissector_table; |
92 | | |
93 | | static int proto_mstp; |
94 | | |
95 | | static int ett_bacnet_mstp; |
96 | | |
97 | | static int hf_mstp_preamble_55; |
98 | | static int hf_mstp_preamble_FF; |
99 | | static int hf_mstp_frame_type; |
100 | | static int hf_mstp_frame_destination; |
101 | | static int hf_mstp_frame_source; |
102 | | static int hf_mstp_frame_vendor_id; |
103 | | static int hf_mstp_frame_pdu_len; |
104 | | static int hf_mstp_frame_crc8; |
105 | | static int hf_mstp_frame_crc16; |
106 | | static int hf_mstp_frame_crc32k; |
107 | | static int hf_mstp_frame_checksum_status; |
108 | | |
109 | | static expert_field ei_mstp_frame_pdu_len; |
110 | | static expert_field ei_mstp_frame_checksum_bad; |
111 | | static expert_field ei_mstp_cobs_bad; |
112 | | static expert_field ei_mstp_bacnet_npdu_len; |
113 | | static expert_field ei_mstp_ipv6_len; |
114 | | |
115 | | static int mstp_address_type = -1; |
116 | | |
117 | | static dissector_handle_t mstp_handle; |
118 | | |
119 | | #if defined(BACNET_MSTP_CHECKSUM_VALIDATE) |
120 | | /* Accumulate "dataValue" into the CRC in crcValue. */ |
121 | | /* Return value is updated CRC */ |
122 | | /* The ^ operator means exclusive OR. */ |
123 | | /* Note: This function is copied directly from the BACnet standard. */ |
124 | | static uint8_t |
125 | | CRC_Calc_Header( |
126 | | uint8_t dataValue, |
127 | | uint8_t crcValue) |
128 | 5 | { |
129 | 5 | uint16_t crc; |
130 | | |
131 | 5 | crc = crcValue ^ dataValue; /* XOR C7..C0 with D7..D0 */ |
132 | | |
133 | | /* Exclusive OR the terms in the table (top down) */ |
134 | 5 | crc = crc ^ (crc << 1) ^ (crc << 2) ^ (crc << 3) |
135 | 5 | ^ (crc << 4) ^ (crc << 5) ^ (crc << 6) |
136 | 5 | ^ (crc << 7); |
137 | | |
138 | | /* Combine bits shifted out left hand end */ |
139 | 5 | return (crc & 0xfe) ^ ((crc >> 8) & 1); |
140 | 5 | } |
141 | | #endif |
142 | | |
143 | | /* Common frame type text */ |
144 | | const char * |
145 | | mstp_frame_type_text(wmem_allocator_t* scope, uint32_t val) |
146 | 2 | { |
147 | 2 | return val_to_str(scope, val, |
148 | 2 | bacnet_mstp_frame_type_name, |
149 | 2 | "Unknown Frame Type (%u)"); |
150 | 2 | } |
151 | | |
152 | | static int mstp_str_len(const address* addr _U_) |
153 | 0 | { |
154 | 0 | return 5; |
155 | 0 | } |
156 | | |
157 | | static int mstp_to_str(const address* addr, char *buf, int buf_len _U_) |
158 | 0 | { |
159 | 0 | *buf++ = '0'; |
160 | 0 | *buf++ = 'x'; |
161 | 0 | buf = bytes_to_hexstr(buf, (const uint8_t *)addr->data, 1); |
162 | 0 | *buf = '\0'; /* NULL terminate */ |
163 | |
|
164 | 0 | return mstp_str_len(addr); |
165 | 0 | } |
166 | | |
167 | | static const char* mstp_col_filter_str(const address* addr _U_, bool is_src) |
168 | 0 | { |
169 | 0 | if (is_src) |
170 | 0 | return "mstp.src"; |
171 | | |
172 | 0 | return "mstp.dst"; |
173 | 0 | } |
174 | | |
175 | | static int mstp_len(void) |
176 | 0 | { |
177 | 0 | return 1; |
178 | 0 | } |
179 | | |
180 | | /* See ANSI/ASHRAE Standard 135-2024 [BACnet], Annex G.3.1 */ |
181 | 1 | #define CRC32K_INITIAL_VALUE 0xFFFFFFFF |
182 | | /* CRC-32K polynomial, 1 + x**1 + ... + x**30 (+ x**32) */ |
183 | 2.79k | #define CRC32K_POLY 0xEB31D82E |
184 | | |
185 | | /* |
186 | | * Accumulate 'data_value' into the CRC in 'crc_value'. |
187 | | * Return updated CRC. |
188 | | * |
189 | | * Note: crc_value must be set to CRC32K_INITIAL_VALUE |
190 | | * before initial call. |
191 | | */ |
192 | | static uint32_t |
193 | | calc_crc32K(uint8_t data_value, uint32_t crc_value) |
194 | 688 | { |
195 | 688 | int b; |
196 | | |
197 | 6.19k | for (b = 0; b < 8; b++) { |
198 | 5.50k | if ((data_value & 1) ^ (crc_value & 1)) { |
199 | 2.79k | crc_value >>= 1; |
200 | 2.79k | crc_value ^= CRC32K_POLY; |
201 | 2.79k | } else { |
202 | 2.70k | crc_value >>= 1; |
203 | 2.70k | } |
204 | 5.50k | data_value >>= 1; |
205 | 5.50k | } |
206 | 688 | return crc_value; |
207 | 688 | } |
208 | | |
209 | | typedef enum { |
210 | | COBS_OK, |
211 | | COBS_BAD_OCTET, /* an encoded octet equals the mask (an encoded zero) */ |
212 | | COBS_OVERRUN /* a code octet runs past the end of the data */ |
213 | | } cobs_result_t; |
214 | | |
215 | | /* |
216 | | * Decodes 'length' octets of COBS-encoded data at 'from', removing the |
217 | | * 'mask' applied to every encoded octet, and writes the decoded data at |
218 | | * 'to'. Sets '*decoded_len' to the number of octets written. |
219 | | * |
220 | | * On error, sets '*error_offset' to the offset of the octet in error, |
221 | | * and 'to' holds the octets decoded before the error (for a code octet |
222 | | * that runs past the end, the data octets that follow it). |
223 | | * Note: Safe to call with 'to' <= 'from' (decodes in place). |
224 | | * |
225 | | * Based on the reference code in ANSI/ASHRAE 135-2024, extended to |
226 | | * report where decoding fails and to reject encoded zero data octets, |
227 | | * which COBS never produces. |
228 | | */ |
229 | | static cobs_result_t |
230 | | cobs_decode(uint8_t *to, const uint8_t *from, size_t length, uint8_t mask, |
231 | | size_t *decoded_len, size_t *error_offset) |
232 | 1 | { |
233 | 1 | size_t read_index = 0; |
234 | 1 | size_t write_index = 0; |
235 | 1 | cobs_result_t result = COBS_OK; |
236 | 1 | uint8_t code; |
237 | 1 | uint8_t last_code; |
238 | 1 | uint8_t data; |
239 | | |
240 | 16 | while (read_index < length) { |
241 | 16 | code = from[read_index] ^ mask; |
242 | 16 | last_code = code; |
243 | | /* |
244 | | * A code octet equal to zero or greater than the length is |
245 | | * illegal. For the latter, keep the data octets that are there, |
246 | | * for display. |
247 | | */ |
248 | 16 | if (code == 0) { |
249 | 0 | *error_offset = read_index; |
250 | 0 | result = COBS_BAD_OCTET; |
251 | 0 | break; |
252 | 0 | } |
253 | 16 | if (read_index + code > length) { |
254 | 1 | *error_offset = read_index; |
255 | 1 | result = COBS_OVERRUN; |
256 | 1 | code = (uint8_t)(length - read_index); |
257 | 1 | } |
258 | | |
259 | 16 | read_index++; |
260 | | /* |
261 | | * Decode data octets. The code octet is included in the length, |
262 | | * but the terminating zero octet is not. A data octet of zero |
263 | | * cannot occur, since the whole point of COBS encoding is to |
264 | | * remove zeroes. |
265 | | */ |
266 | 688 | while (--code > 0) { |
267 | 672 | data = from[read_index] ^ mask; |
268 | 672 | if (data == 0) { |
269 | 0 | *error_offset = read_index; |
270 | 0 | result = COBS_BAD_OCTET; |
271 | 0 | break; |
272 | 0 | } |
273 | 672 | to[write_index++] = data; |
274 | 672 | read_index++; |
275 | 672 | } |
276 | 16 | if (result != COBS_OK) |
277 | 1 | break; |
278 | | |
279 | | /* |
280 | | * Restore the implicit zero at the end of each decoded block |
281 | | * except when it contains exactly 254 non-zero octets or the |
282 | | * end of data has been reached. |
283 | | */ |
284 | 15 | if ((last_code != 255) && (read_index < length)) |
285 | 15 | to[write_index++] = 0; |
286 | 15 | } |
287 | | |
288 | 1 | *decoded_len = write_index; |
289 | 1 | return result; |
290 | 1 | } |
291 | | |
292 | 1 | #define MSTP_ENC_CRC32K_LEN (5) /* the Encoded CRC-32K field is always 5 octets */ |
293 | 689 | #define MSTP_PREAMBLE_X55 (0x55) |
294 | | |
295 | | static void |
296 | | mstp_cobs_error(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, |
297 | | unsigned offset, cobs_result_t result, const char *field) |
298 | 0 | { |
299 | 0 | if (result == COBS_BAD_OCTET) { |
300 | 0 | proto_tree_add_expert_format(tree, pinfo, &ei_mstp_cobs_bad, tvb, |
301 | 0 | offset, 1, |
302 | 0 | "Octet 0x55 (an encoded zero) in the %s field: octets may be missing, or the frame ended early", |
303 | 0 | field); |
304 | 0 | } else { |
305 | 0 | proto_tree_add_expert_format(tree, pinfo, &ei_mstp_cobs_bad, tvb, |
306 | 0 | offset, 1, |
307 | 0 | "COBS code octet runs past the end of the %s field: octets may be missing or corrupted", |
308 | 0 | field); |
309 | 0 | } |
310 | 0 | } |
311 | | |
312 | | /* |
313 | | * Dissects the Encoded Data and Encoded CRC-32K fields of a COBS-encoded |
314 | | * frame (Frame Types 32 through 127). The Length field is two octets less |
315 | | * than the encoded data plus the 5-octet Encoded CRC-32K, so that devices |
316 | | * that do not know about COBS-encoded frames can skip them as unknown. |
317 | | * The CRC-32K is calculated over the encoded data. |
318 | | */ |
319 | | static void |
320 | | dissect_mstp_cobs(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, |
321 | | proto_tree *subtree, unsigned offset, uint8_t frame_type, |
322 | | uint32_t frame_pdu_len, proto_item *len_item) |
323 | 1 | { |
324 | 1 | unsigned encoded_len = frame_pdu_len + 2; |
325 | 1 | unsigned captured_len; |
326 | 1 | unsigned data_len; |
327 | 1 | unsigned scan_len; |
328 | 1 | unsigned bad_offset = 0; |
329 | 1 | const char *bad_field = NULL; |
330 | 1 | cobs_result_t bad_result = COBS_OK; |
331 | 1 | uint8_t *buf; |
332 | 1 | size_t decoded_len = 0; |
333 | 1 | size_t error_offset = 0; |
334 | 1 | size_t crc_len = 0; |
335 | 1 | size_t crc_error_offset = 0; |
336 | 1 | cobs_result_t result; |
337 | 1 | uint32_t crc32k = CRC32K_INITIAL_VALUE; |
338 | 1 | tvbuff_t *decoded_tvb; |
339 | 1 | tvbuff_t *payload_tvb; |
340 | 1 | unsigned i; |
341 | | |
342 | | /* Too short to hold the Encoded CRC-32K (dissect_mstp() reports it). */ |
343 | 1 | if (encoded_len < MSTP_ENC_CRC32K_LEN) { |
344 | 0 | call_data_dissector(tvb_new_subset_length(tvb, offset, encoded_len), |
345 | 0 | pinfo, tree); |
346 | 0 | return; |
347 | 0 | } |
348 | | |
349 | | /* |
350 | | * Decode what was captured, even if the frame is shorter than the |
351 | | * Length field says (the length check in dissect_mstp() flags that). |
352 | | */ |
353 | 1 | captured_len = MIN(tvb_captured_length_remaining(tvb, offset), encoded_len); |
354 | 1 | if (captured_len == 0) |
355 | 0 | return; |
356 | 1 | data_len = MIN(captured_len, encoded_len - MSTP_ENC_CRC32K_LEN); |
357 | 1 | buf = (uint8_t *)tvb_memdup(pinfo->pool, tvb, offset, captured_len); |
358 | | |
359 | | /* |
360 | | * COBS never produces an encoded zero (0x55), so the first 0x55 marks |
361 | | * where the frame went wrong, typically because octets were lost and |
362 | | * the next frame's preamble was read as part of this one. Only the |
363 | | * octets before it are decoded. |
364 | | */ |
365 | 689 | for (scan_len = 0; scan_len < captured_len; scan_len++) { |
366 | 688 | if (buf[scan_len] == MSTP_PREAMBLE_X55) |
367 | 0 | break; |
368 | 688 | } |
369 | | |
370 | | /* |
371 | | * The CRC-32K covers the encoded data, so calculate it before |
372 | | * decoding in place. |
373 | | */ |
374 | 689 | for (i = 0; i < data_len; i++) |
375 | 688 | crc32k = calc_crc32K(buf[i], crc32k); |
376 | | |
377 | 1 | result = cobs_decode(buf, buf, MIN(data_len, scan_len), |
378 | 1 | MSTP_PREAMBLE_X55, &decoded_len, &error_offset); |
379 | | |
380 | 1 | if (scan_len < captured_len) { |
381 | 0 | bad_result = COBS_BAD_OCTET; |
382 | 0 | bad_offset = scan_len; |
383 | 0 | bad_field = (scan_len < data_len) ? "Encoded Data" : "Encoded CRC-32K"; |
384 | 1 | } else if (result != COBS_OK) { |
385 | | /* Running out of data is expected in a frame that was cut short. */ |
386 | 1 | if (!(result == COBS_OVERRUN && captured_len < encoded_len)) { |
387 | 0 | bad_result = result; |
388 | 0 | bad_offset = (unsigned)error_offset; |
389 | 0 | bad_field = "Encoded Data"; |
390 | 0 | } |
391 | 1 | } else if (captured_len == encoded_len) { |
392 | 0 | result = cobs_decode(buf + decoded_len, buf + data_len, |
393 | 0 | MSTP_ENC_CRC32K_LEN, MSTP_PREAMBLE_X55, |
394 | 0 | &crc_len, &crc_error_offset); |
395 | 0 | if (result == COBS_OK && crc_len == sizeof(uint32_t)) { |
396 | 0 | decoded_tvb = tvb_new_child_real_data(tvb, buf, |
397 | 0 | (unsigned)(decoded_len + crc_len), |
398 | 0 | (unsigned)(decoded_len + crc_len)); |
399 | 0 | add_new_data_source(pinfo, decoded_tvb, "Decoded Data and CRC32K"); |
400 | | |
401 | | /* |
402 | | * Compares received CRC-32K to computed CRC-32K. |
403 | | * The CRC-32K is sent least significant octet first. Like |
404 | | * the Ethernet FCS, show it as the octets appear on the wire. |
405 | | */ |
406 | 0 | proto_tree_add_checksum(subtree, decoded_tvb, (unsigned)decoded_len, |
407 | 0 | hf_mstp_frame_crc32k, hf_mstp_frame_checksum_status, |
408 | 0 | &ei_mstp_frame_checksum_bad, pinfo, |
409 | 0 | GUINT32_SWAP_LE_BE(~crc32k), ENC_BIG_ENDIAN, |
410 | 0 | PROTO_CHECKSUM_VERIFY); |
411 | |
|
412 | 0 | if ((frame_type == MSTP_BACNET_EXTENDED_DATA_EXPECTING_REPLY || |
413 | 0 | frame_type == MSTP_BACNET_EXTENDED_DATA_NOT_EXPECTING_REPLY) && |
414 | 0 | (decoded_len < MSTP_BACNET_EXTENDED_NPDU_MIN || |
415 | 0 | decoded_len > MSTP_BACNET_EXTENDED_NPDU_MAX)) { |
416 | 0 | expert_add_info_format(pinfo, len_item, &ei_mstp_bacnet_npdu_len, |
417 | 0 | "BACnet NPDU of %u octets: Frame Types 32 and 33 carry NPDUs of %u to %u octets", |
418 | 0 | (unsigned)decoded_len, (unsigned)MSTP_BACNET_EXTENDED_NPDU_MIN, |
419 | 0 | (unsigned)MSTP_BACNET_EXTENDED_NPDU_MAX); |
420 | 0 | } |
421 | |
|
422 | 0 | if (decoded_len > 0) { |
423 | 0 | payload_tvb = tvb_new_subset_length(decoded_tvb, 0, |
424 | 0 | (unsigned)decoded_len); |
425 | | /* Dissect the payload even if the CRC-32K is bad. */ |
426 | 0 | if (!dissector_try_uint(subdissector_table, |
427 | 0 | (0/*VendorID ASHRAE*/ << 16) + frame_type, |
428 | 0 | payload_tvb, pinfo, tree)) { |
429 | 0 | call_data_dissector(payload_tvb, pinfo, tree); |
430 | 0 | } |
431 | 0 | } |
432 | 0 | return; |
433 | 0 | } |
434 | 0 | bad_result = (result == COBS_OK) ? COBS_OVERRUN : result; |
435 | 0 | bad_offset = data_len + (unsigned)crc_error_offset; |
436 | 0 | bad_field = "Encoded CRC-32K"; |
437 | 0 | } |
438 | | |
439 | | /* Report where decoding failed, and show the octets decoded so far. */ |
440 | 1 | if (bad_field != NULL) |
441 | 0 | mstp_cobs_error(subtree, pinfo, tvb, offset + bad_offset, bad_result, |
442 | 0 | bad_field); |
443 | 1 | if (decoded_len > 0) { |
444 | 1 | decoded_tvb = tvb_new_child_real_data(tvb, buf, |
445 | 1 | (unsigned)decoded_len, (unsigned)decoded_len); |
446 | 1 | add_new_data_source(pinfo, decoded_tvb, "Decoded Data (partial)"); |
447 | 1 | call_data_dissector(decoded_tvb, pinfo, tree); |
448 | 1 | } else { |
449 | 0 | call_data_dissector(tvb_new_subset_length(tvb, offset, captured_len), |
450 | 0 | pinfo, tree); |
451 | 0 | } |
452 | 1 | } |
453 | | |
454 | | /* dissects a BACnet MS/TP frame */ |
455 | | /* preamble 0x55 0xFF is not included in Cimetrics U+4 output */ |
456 | | void |
457 | | dissect_mstp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, |
458 | | proto_tree *subtree, int offset) |
459 | 1 | { |
460 | 1 | uint8_t mstp_frame_type = 0; |
461 | 1 | uint32_t mstp_frame_pdu_len = 0; /* PDU length reported in the header */ |
462 | 1 | uint16_t mstp_tvb_pdu_len = 0; /* Reported space in parent tvb for PDU */ |
463 | 1 | uint16_t vendorid = 0; |
464 | 1 | tvbuff_t *next_tvb = NULL; |
465 | 1 | proto_item *item; |
466 | 1 | #if defined(BACNET_MSTP_CHECKSUM_VALIDATE) |
467 | | /* used to calculate the crc value */ |
468 | 1 | uint8_t crc8 = 0xFF; |
469 | 1 | uint16_t crc16; |
470 | 1 | uint8_t crcdata; |
471 | 1 | uint16_t i; /* loop counter */ |
472 | 1 | #endif |
473 | | |
474 | 1 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "BACnet"); |
475 | 1 | col_set_str(pinfo->cinfo, COL_INFO, "BACnet MS/TP"); |
476 | 1 | mstp_frame_type = tvb_get_uint8(tvb, offset); |
477 | 1 | col_append_fstr(pinfo->cinfo, COL_INFO, " %s", |
478 | 1 | mstp_frame_type_text(pinfo->pool, mstp_frame_type)); |
479 | | |
480 | | /* Add the items to the tree */ |
481 | 1 | proto_tree_add_item(subtree, hf_mstp_frame_type, tvb, |
482 | 1 | offset, 1, ENC_LITTLE_ENDIAN); |
483 | 1 | proto_tree_add_item(subtree, hf_mstp_frame_destination, tvb, |
484 | 1 | offset+1, 1, ENC_LITTLE_ENDIAN); |
485 | 1 | proto_tree_add_item(subtree, hf_mstp_frame_source, tvb, |
486 | 1 | offset+2, 1, ENC_LITTLE_ENDIAN); |
487 | 1 | item = proto_tree_add_item_ret_uint(subtree, hf_mstp_frame_pdu_len, tvb, |
488 | 1 | offset+3, 2, ENC_BIG_ENDIAN, &mstp_frame_pdu_len); |
489 | 1 | mstp_tvb_pdu_len = tvb_reported_length_remaining(tvb, offset+6); |
490 | | /* check the length - which does not include the crc16 checksum */ |
491 | 1 | if (mstp_frame_pdu_len > 0) { |
492 | 1 | if (mstp_frame_pdu_len + 2 > mstp_tvb_pdu_len) { |
493 | | /* We should get a ContainedBoundsError later. */ |
494 | 1 | expert_add_info(pinfo, item, &ei_mstp_frame_pdu_len); |
495 | 1 | } |
496 | 1 | } |
497 | 1 | #if defined(BACNET_MSTP_CHECKSUM_VALIDATE) |
498 | | /* calculate checksum to validate */ |
499 | 6 | for (i = 0; i < 5; i++) { |
500 | 5 | crcdata = tvb_get_uint8(tvb, offset+i); |
501 | 5 | crc8 = CRC_Calc_Header(crcdata, crc8); |
502 | 5 | } |
503 | 1 | crc8 = ~crc8; |
504 | 1 | proto_tree_add_checksum(subtree, tvb, offset+5, hf_mstp_frame_crc8, hf_mstp_frame_checksum_status, &ei_mstp_frame_checksum_bad, pinfo, crc8, |
505 | 1 | ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY); |
506 | | #else |
507 | | proto_tree_add_checksum(subtree, tvb, offset+5, hf_mstp_frame_crc8, hf_mstp_frame_checksum_status, &ei_mstp_frame_checksum_bad, pinfo, 0, |
508 | | ENC_BIG_ENDIAN, PROTO_CHECKSUM_NO_FLAGS); |
509 | | #endif |
510 | | |
511 | | /* dissect BACnet PDU if there is one */ |
512 | 1 | offset += 6; |
513 | | |
514 | 1 | if (mstp_frame_type >= MSTP_COBS_TYPE_MIN && |
515 | 1 | mstp_frame_type <= MSTP_COBS_TYPE_MAX) { |
516 | 1 | if (mstp_frame_pdu_len < MSTP_COBS_LENGTH_MIN || |
517 | 1 | mstp_frame_pdu_len > MSTP_COBS_LENGTH_MAX) { |
518 | 1 | expert_add_info_format(pinfo, item, &ei_mstp_frame_pdu_len, |
519 | 1 | "Length %u is outside %u to %u, the range for COBS-encoded frames", |
520 | 1 | mstp_frame_pdu_len, (unsigned)MSTP_COBS_LENGTH_MIN, |
521 | 1 | (unsigned)MSTP_COBS_LENGTH_MAX); |
522 | 1 | } else if (mstp_frame_type == MSTP_IPV6_ENCAPSULATION && |
523 | 0 | mstp_frame_pdu_len > MSTP_IPV6_LENGTH_MAX) { |
524 | 0 | expert_add_info(pinfo, item, &ei_mstp_ipv6_len); |
525 | 0 | } |
526 | | /* COBS-encoded frames must be decoded before they are dissected */ |
527 | 1 | if (mstp_frame_pdu_len) { |
528 | 1 | dissect_mstp_cobs(tvb, pinfo, tree, subtree, offset, |
529 | 1 | mstp_frame_type, mstp_frame_pdu_len, item); |
530 | 1 | } |
531 | 1 | } |
532 | 0 | else if (mstp_frame_pdu_len) { |
533 | | /* frame_pdu_len does not include the 16-bit crc bytes */ |
534 | 0 | if (mstp_frame_type < 128) { |
535 | 0 | vendorid = 0; |
536 | 0 | next_tvb = tvb_new_subset_length(tvb, offset, |
537 | 0 | mstp_frame_pdu_len); |
538 | 0 | } else { |
539 | | /* With Vendor ID */ |
540 | 0 | vendorid = tvb_get_ntohs(tvb, offset); |
541 | | |
542 | | /* Write Vendor ID as tree */ |
543 | 0 | proto_tree_add_item(subtree, hf_mstp_frame_vendor_id, tvb, |
544 | 0 | offset, 2, ENC_BIG_ENDIAN); |
545 | |
|
546 | 0 | if (mstp_frame_pdu_len < 2) { |
547 | 0 | expert_add_info_format(pinfo, item, &ei_mstp_frame_pdu_len, "Length field value is not large enough for Vendor ID"); |
548 | | /* Remaining length can't be negative. */ |
549 | 0 | THROW(ReportedBoundsError); |
550 | 0 | } |
551 | | /* NPDU - call the Vendor specific dissector */ |
552 | 0 | next_tvb = tvb_new_subset_length(tvb, offset+2, |
553 | 0 | mstp_frame_pdu_len - 2); |
554 | 0 | } |
555 | |
|
556 | 0 | if (!(dissector_try_uint(subdissector_table, (vendorid<<16) + mstp_frame_type, |
557 | 0 | next_tvb, pinfo, tree))) { |
558 | | /* Unknown function - dissect the payload as data */ |
559 | 0 | call_data_dissector(next_tvb, pinfo, tree); |
560 | 0 | } |
561 | 0 | #if defined(BACNET_MSTP_CHECKSUM_VALIDATE) |
562 | | /* 16-bit checksum - calculate to validate */ |
563 | | /* It is sent least significant octet first */ |
564 | 0 | crc16 = crc16_ccitt_tvb_offset(tvb, offset, mstp_frame_pdu_len); |
565 | |
|
566 | 0 | proto_tree_add_checksum(subtree, tvb, offset+mstp_frame_pdu_len, hf_mstp_frame_crc16, hf_mstp_frame_checksum_status, &ei_mstp_frame_checksum_bad, pinfo, crc16, |
567 | 0 | ENC_LITTLE_ENDIAN, PROTO_CHECKSUM_VERIFY); |
568 | | #else |
569 | | proto_tree_add_checksum(subtree, tvb, offset+mstp_frame_pdu_len, hf_mstp_frame_crc16, hf_mstp_frame_checksum_status, &ei_mstp_frame_checksum_bad, pinfo, 0, |
570 | | ENC_LITTLE_ENDIAN, PROTO_CHECKSUM_NO_FLAGS); |
571 | | #endif |
572 | 0 | } |
573 | 1 | } |
574 | | |
575 | | static int |
576 | | dissect_mstp_wtap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
577 | 1 | { |
578 | 1 | proto_item *ti; |
579 | 1 | proto_tree *subtree; |
580 | 1 | unsigned offset = 0; |
581 | 1 | #ifdef BACNET_MSTP_SUMMARY_IN_TREE |
582 | 1 | uint8_t mstp_frame_type = 0; |
583 | 1 | uint8_t mstp_frame_source = 0; |
584 | 1 | uint8_t mstp_frame_destination = 0; |
585 | 1 | #endif |
586 | | |
587 | | /* set the MS/TP MAC address in the source/destination */ |
588 | 1 | set_address_tvb(&pinfo->dl_dst, mstp_address_type, 1, tvb, offset+3); |
589 | 1 | copy_address_shallow(&pinfo->dst, &pinfo->dl_dst); |
590 | 1 | set_address_tvb(&pinfo->dl_src, mstp_address_type, 1, tvb, offset+4); |
591 | 1 | copy_address_shallow(&pinfo->src, &pinfo->dl_src); |
592 | | |
593 | 1 | #ifdef BACNET_MSTP_SUMMARY_IN_TREE |
594 | 1 | mstp_frame_type = tvb_get_uint8(tvb, offset+2); |
595 | 1 | mstp_frame_destination = tvb_get_uint8(tvb, offset+3); |
596 | 1 | mstp_frame_source = tvb_get_uint8(tvb, offset+4); |
597 | 1 | ti = proto_tree_add_protocol_format(tree, proto_mstp, tvb, offset, 8, |
598 | 1 | "BACnet MS/TP, Src (%u), Dst (%u), %s", |
599 | 1 | mstp_frame_source, mstp_frame_destination, |
600 | 1 | mstp_frame_type_text(pinfo->pool, mstp_frame_type)); |
601 | | #else |
602 | | ti = proto_tree_add_item(tree, proto_mstp, tvb, offset, 8, ENC_NA); |
603 | | #endif |
604 | 1 | subtree = proto_item_add_subtree(ti, ett_bacnet_mstp); |
605 | 1 | proto_tree_add_item(subtree, hf_mstp_preamble_55, tvb, |
606 | 1 | offset, 1, ENC_LITTLE_ENDIAN); |
607 | 1 | proto_tree_add_item(subtree, hf_mstp_preamble_FF, tvb, |
608 | 1 | offset+1, 1, ENC_LITTLE_ENDIAN); |
609 | 1 | dissect_mstp(tvb, pinfo, tree, subtree, offset+2); |
610 | 1 | return tvb_captured_length(tvb); |
611 | 1 | } |
612 | | |
613 | | void |
614 | | proto_register_mstp(void) |
615 | 16 | { |
616 | 16 | static hf_register_info hf[] = { |
617 | 16 | { &hf_mstp_preamble_55, |
618 | 16 | { "Preamble 55", "mstp.preamble_55", |
619 | 16 | FT_UINT8, BASE_HEX, NULL, 0, |
620 | 16 | "MS/TP Preamble 55", HFILL } |
621 | 16 | }, |
622 | 16 | { &hf_mstp_preamble_FF, |
623 | 16 | { "Preamble FF", "mstp.preamble_FF", |
624 | 16 | FT_UINT8, BASE_HEX, NULL, 0, |
625 | 16 | "MS/TP Preamble FF", HFILL } |
626 | 16 | }, |
627 | 16 | { &hf_mstp_frame_type, |
628 | 16 | { "Frame Type", "mstp.frame_type", |
629 | 16 | FT_UINT8, BASE_DEC, VALS(bacnet_mstp_frame_type_name), 0, |
630 | 16 | "MS/TP Frame Type", HFILL } |
631 | 16 | }, |
632 | 16 | { &hf_mstp_frame_destination, |
633 | 16 | { "Destination Address", "mstp.dst", |
634 | 16 | FT_UINT8, BASE_DEC, NULL, 0, |
635 | 16 | "Destination MS/TP MAC Address", HFILL } |
636 | 16 | }, |
637 | 16 | { &hf_mstp_frame_source, |
638 | 16 | { "Source Address", "mstp.src", |
639 | 16 | FT_UINT8, BASE_DEC, NULL, 0, |
640 | 16 | "Source MS/TP MAC Address", HFILL } |
641 | 16 | }, |
642 | 16 | { &hf_mstp_frame_vendor_id, |
643 | 16 | { "VendorID", "mstp.vendorid", |
644 | 16 | FT_UINT16, BASE_DEC, NULL, 0, |
645 | 16 | "MS/TP Vendor ID of proprietary frametypes", HFILL } |
646 | 16 | }, |
647 | 16 | { &hf_mstp_frame_pdu_len, |
648 | 16 | { "Length", "mstp.len", |
649 | 16 | FT_UINT16, BASE_DEC, NULL, 0, |
650 | 16 | "MS/TP Data Length", HFILL } |
651 | 16 | }, |
652 | 16 | { &hf_mstp_frame_crc8, |
653 | 16 | { "Header CRC", "mstp.hdr_crc", |
654 | 16 | FT_UINT8, BASE_HEX, NULL, 0, |
655 | 16 | "MS/TP Header CRC", HFILL } |
656 | 16 | }, |
657 | 16 | { &hf_mstp_frame_crc16, |
658 | 16 | { "Data CRC", "mstp.data_crc", |
659 | 16 | FT_UINT16, BASE_HEX, NULL, 0, |
660 | 16 | "MS/TP Data CRC", HFILL } |
661 | 16 | }, |
662 | 16 | { &hf_mstp_frame_crc32k, |
663 | 16 | { "Extended Data CRC", "mstp.ext_data_crc", |
664 | 16 | FT_UINT32, BASE_HEX, NULL, 0, |
665 | 16 | "MS/TP Extended Data CRC (CRC-32K)", HFILL } |
666 | 16 | }, |
667 | 16 | { &hf_mstp_frame_checksum_status, |
668 | 16 | { "Checksum status", "mstp.checksum.status", |
669 | 16 | FT_UINT8, BASE_NONE, VALS(proto_checksum_vals), 0x0, |
670 | 16 | NULL, HFILL } |
671 | 16 | }, |
672 | 16 | }; |
673 | | |
674 | 16 | static int *ett[] = { |
675 | 16 | &ett_bacnet_mstp, |
676 | 16 | }; |
677 | | |
678 | 16 | static ei_register_info ei[] = { |
679 | 16 | { &ei_mstp_frame_pdu_len, { "mstp.len.bad", PI_MALFORMED, PI_ERROR, "Length field value goes past the end of the payload", EXPFILL }}, |
680 | 16 | { &ei_mstp_frame_checksum_bad, { "mstp.checksum_bad.expert", PI_CHECKSUM, PI_WARN, "Bad Checksum", EXPFILL }}, |
681 | 16 | { &ei_mstp_cobs_bad, { "mstp.cobs.bad", PI_MALFORMED, PI_ERROR, "Invalid COBS encoding", EXPFILL }}, |
682 | 16 | { &ei_mstp_bacnet_npdu_len, { "mstp.bacnet_npdu_len.unexpected", PI_PROTOCOL, PI_WARN, "BACnet NPDU length outside the range for Frame Types 32 and 33", EXPFILL }}, |
683 | 16 | { &ei_mstp_ipv6_len, { "mstp.ipv6_len.too_long", PI_PROTOCOL, PI_WARN, "Length above 1510, the largest IPv6 over MS/TP frame", EXPFILL }}, |
684 | 16 | }; |
685 | | |
686 | 16 | expert_module_t* expert_mstp; |
687 | | |
688 | 16 | proto_mstp = proto_register_protocol("BACnet MS/TP", "BACnet MS/TP", "mstp"); |
689 | | |
690 | 16 | proto_register_field_array(proto_mstp, hf, array_length(hf)); |
691 | 16 | proto_register_subtree_array(ett, array_length(ett)); |
692 | 16 | expert_mstp = expert_register_protocol(proto_mstp); |
693 | 16 | expert_register_field_array(expert_mstp, ei, array_length(ei)); |
694 | | |
695 | 16 | mstp_handle = register_dissector("mstp", dissect_mstp_wtap, proto_mstp); |
696 | | |
697 | 16 | subdissector_table = register_dissector_table("mstp.vendor_frame_type", |
698 | 16 | "MSTP Vendor specific Frametypes", proto_mstp, FT_UINT24, BASE_DEC); |
699 | | /* Table_type: (Vendor ID << 16) + Frametype */ |
700 | | |
701 | 16 | mstp_address_type = address_type_dissector_register("AT_MSTP", "BACnet MS/TP Address", mstp_to_str, mstp_str_len, NULL, mstp_col_filter_str, mstp_len, NULL, NULL); |
702 | 16 | } |
703 | | |
704 | | void |
705 | | proto_reg_handoff_mstp(void) |
706 | 16 | { |
707 | 16 | dissector_handle_t bacnet_handle; |
708 | | |
709 | 16 | dissector_add_uint("wtap_encap", WTAP_ENCAP_BACNET_MS_TP, mstp_handle); |
710 | 16 | dissector_add_uint("wtap_encap", WTAP_ENCAP_BACNET_MS_TP_WITH_PHDR, mstp_handle); |
711 | | |
712 | 16 | bacnet_handle = find_dissector("bacnet"); |
713 | | |
714 | 16 | dissector_add_uint("mstp.vendor_frame_type", (0/*VendorID ASHRAE*/ << 16) + MSTP_BACNET_DATA_EXPECTING_REPLY, bacnet_handle); |
715 | 16 | dissector_add_uint("mstp.vendor_frame_type", (0/*VendorID ASHRAE*/ << 16) + MSTP_BACNET_DATA_NOT_EXPECTING_REPLY, bacnet_handle); |
716 | 16 | dissector_add_uint("mstp.vendor_frame_type", (0/*VendorID ASHRAE*/ << 16) + MSTP_BACNET_EXTENDED_DATA_EXPECTING_REPLY, bacnet_handle); |
717 | 16 | dissector_add_uint("mstp.vendor_frame_type", (0/*VendorID ASHRAE*/ << 16) + MSTP_BACNET_EXTENDED_DATA_NOT_EXPECTING_REPLY, bacnet_handle); |
718 | 16 | dissector_add_uint("mstp.vendor_frame_type", (0/*VendorID ASHRAE*/ << 16) + MSTP_IPV6_ENCAPSULATION, |
719 | 16 | find_dissector_add_dependency("6lowpan", proto_mstp)); |
720 | 16 | } |
721 | | |
722 | | /* |
723 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
724 | | * |
725 | | * Local variables: |
726 | | * c-basic-offset: 8 |
727 | | * tab-width: 8 |
728 | | * indent-tabs-mode: t |
729 | | * End: |
730 | | * |
731 | | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
732 | | * :indentSize=8:tabSize=8:noTabs=false: |
733 | | */ |