2 * Routines for BACnet (NPDU) dissection
3 * Copyright 2001, Hartmut Mueller <hartmut@abmlinux.org>, FH Dortmund
5 * $Id: packet-bacnet.c,v 1.17 2003/01/25 00:06:12 guy Exp $
7 * Ethereal - Network traffic analyzer
8 * By Gerald Combs <gerald@ethereal.com>
9 * Copyright 1998 Gerald Combs
11 * Copied from README.developer,v 1.23
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.
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.
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.
38 #include <epan/packet.h>
42 static dissector_handle_t bacapp_handle;
43 static dissector_handle_t data_handle;
46 bacnet_mesgtyp_name (guint8 bacnet_mesgtyp){
47 static const char *type_names[] = {
48 "Who-Is-Router-To-Network",
49 "I-Am-Router-To-Network",
50 "I-Could-Be-Router-To-Network",
51 "Reject-Message-To-Network",
52 "Router-Busy-To-Network",
53 "Router-Available-To-Network",
54 "Initialize-Routing-Table",
55 "Initialize-Routing-Table-Ack",
56 "Establish-Connection-To-Network",
57 "Disconnect-Connection-To-Network"
59 if(bacnet_mesgtyp < 0x0a) {
60 return type_names[bacnet_mesgtyp];
62 return (bacnet_mesgtyp < 0x80)? "Reserved for Use by ASHRAE" : "Vendor Proprietary Message";
67 bacnet_rejectreason_name (guint8 bacnet_rejectreason) {
68 static const char *type_names[] = {
70 "The router is not directly connected to DNET and cannot find a router to DNET on any directly connected network using Who-Is-Router-To-Network messages.",
71 "The router is busy and unable to accept messages for the specified DNET at the present time.",
72 "It is an unknown network layer message type.",
73 "The message is too long to be routed to this DNET.",
74 "The router is no longer directly connected to DNET but can reconnect if requested.",
75 "The router is no longer directly connected to DNET and cannot reconnect even if requested."
77 return (bacnet_rejectreason > 6)? "Invalid Rejection Reason.": type_names[bacnet_rejectreason];
80 /* Network Layer Control Information */
81 #define BAC_CONTROL_NET 0x80
82 #define BAC_CONTROL_RES1 0x40
83 #define BAC_CONTROL_DEST 0x20
84 #define BAC_CONTROL_RES2 0x10
85 #define BAC_CONTROL_SRC 0x08
86 #define BAC_CONTROL_EXPECT 0x04
87 #define BAC_CONTROL_PRIO_HIGH 0x02
88 #define BAC_CONTROL_PRIO_LOW 0x01
90 /* Network Layer Message Types */
91 #define BAC_NET_WHO_R 0x00
92 #define BAC_NET_IAM_R 0x01
93 #define BAC_NET_ICB_R 0x02
94 #define BAC_NET_REJ 0x03
95 #define BAC_NET_R_BUSY 0x04
96 #define BAC_NET_R_AVA 0x05
97 #define BAC_NET_INIT_RTAB 0x06
98 #define BAC_NET_INIT_RTAB_ACK 0x07
99 #define BAC_NET_EST_CON 0x08
100 #define BAC_NET_DISC_CON 0x09
102 static const true_false_string control_net_set_high = {
103 "network layer message, message type field present.",
104 "BACnet APDU, message type field absent."
107 static const true_false_string control_res_high = {
108 "Shall be zero, but is one.",
109 "Shall be zero and is zero."
111 static const true_false_string control_dest_high = {
112 "DNET, DLEN and Hop Count present. If DLEN=0: broadcast, dest. address field absent.",
113 "DNET, DLEN, DADR and Hop Count absent."
116 static const true_false_string control_src_high = {
117 "SNET, SLEN and SADR present, SLEN=0 invalid, SLEN specifies length of SADR",
118 "SNET, SLEN and SADR absent"
121 static const true_false_string control_expect_high = {
122 "BACnet-Confirmed-Request-PDU, a segment of BACnet-ComplexACK-PDU or Network Message expecting a reply present.",
123 "Other than a BACnet-Confirmed-Request-PDU, segment of BACnet-ComplexACK-PDU or network layer message expecting a reply present."
126 static const true_false_string control_prio_high_high = {
127 "Life Safety or Critical Equipment message.",
128 "Not a Life Safety or Critical Equipment message."
131 static const true_false_string control_prio_low_high = {
137 static int proto_bacnet = -1;
138 static int hf_bacnet_version = -1;
139 static int hf_bacnet_control = -1;
140 static int hf_bacnet_control_net = -1;
141 static int hf_bacnet_control_res1 = -1;
142 static int hf_bacnet_control_dest = -1;
143 static int hf_bacnet_control_res2 = -1;
144 static int hf_bacnet_control_src = -1;
145 static int hf_bacnet_control_expect = -1;
146 static int hf_bacnet_control_prio_high = -1;
147 static int hf_bacnet_control_prio_low = -1;
148 static int hf_bacnet_dnet = -1;
149 static int hf_bacnet_dlen = -1;
150 static int hf_bacnet_dadr_eth = -1;
151 static int hf_bacnet_dadr_tmp = -1;
152 static int hf_bacnet_snet = -1;
153 static int hf_bacnet_slen = -1;
154 static int hf_bacnet_sadr_eth = -1;
155 static int hf_bacnet_sadr_tmp = -1;
156 static int hf_bacnet_hopc = -1;
157 static int hf_bacnet_mesgtyp = -1;
158 static int hf_bacnet_vendor = -1;
159 static int hf_bacnet_perf = -1;
160 static int hf_bacnet_rejectreason = -1;
161 static int hf_bacnet_rportnum = -1;
162 static int hf_bacnet_portid = -1;
163 static int hf_bacnet_pinfolen = -1;
164 static int hf_bacnet_pinfo = -1;
166 static gint ett_bacnet = -1;
167 static gint ett_bacnet_control = -1;
170 dissect_bacnet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
174 proto_tree *bacnet_tree;
175 proto_tree *control_tree;
178 guint8 bacnet_version;
179 guint8 bacnet_control;
182 guint8 bacnet_mesgtyp;
183 guint8 bacnet_rejectreason;
184 guint8 bacnet_rportnum;
185 guint8 bacnet_pinfolen;
190 if (check_col(pinfo->cinfo, COL_PROTOCOL))
191 col_set_str(pinfo->cinfo, COL_PROTOCOL, "BACnet-NPDU");
193 if (check_col(pinfo->cinfo, COL_INFO))
194 col_set_str(pinfo->cinfo, COL_INFO, "Building Automation and Control Network NPDU");
197 bacnet_version = tvb_get_guint8(tvb, offset);
198 bacnet_control = tvb_get_guint8(tvb, offset+1);
202 bacnet_rejectreason = 0;
210 /* I don't know the length of the NPDU by know. Setting the length after dissection */
211 ti = proto_tree_add_item(tree, proto_bacnet, tvb, 0, -1, FALSE);
213 bacnet_tree = proto_item_add_subtree(ti, ett_bacnet);
215 proto_tree_add_uint_format(bacnet_tree, hf_bacnet_version, tvb,
217 bacnet_version,"Version: 0x%02x (%s)",bacnet_version,
218 (bacnet_version == 0x01)?"ASHRAE 135-1995":"unknown");
220 ct = proto_tree_add_uint_format(bacnet_tree, hf_bacnet_control,
222 bacnet_control,"Control: 0x%02x",bacnet_control);
223 control_tree = proto_item_add_subtree(ct,
225 proto_tree_add_boolean(control_tree, hf_bacnet_control_net,
226 tvb, offset, 1, bacnet_control);
227 proto_tree_add_boolean(control_tree, hf_bacnet_control_res1, tvb,
228 offset, 1, bacnet_control);
229 proto_tree_add_boolean(control_tree, hf_bacnet_control_dest, tvb,
230 offset, 1, bacnet_control);
231 proto_tree_add_boolean(control_tree, hf_bacnet_control_res2, tvb,
232 offset, 1, bacnet_control);
233 proto_tree_add_boolean(control_tree, hf_bacnet_control_src, tvb,
234 offset, 1, bacnet_control);
235 proto_tree_add_boolean(control_tree, hf_bacnet_control_expect, tvb,
236 offset, 1, bacnet_control);
237 proto_tree_add_boolean(control_tree, hf_bacnet_control_prio_high,
238 tvb, offset, 1, bacnet_control);
239 proto_tree_add_boolean(control_tree, hf_bacnet_control_prio_low,
240 tvb, offset, 1, bacnet_control);
242 if (bacnet_control & BAC_CONTROL_DEST) { /* DNET, DLEN, DADR */
243 proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
244 tvb, offset, 2, FALSE);
246 bacnet_dlen = tvb_get_guint8(tvb, offset);
247 /* DLEN = 0 is broadcast on dest.network */
248 if( bacnet_dlen == 0) {
249 /* append to hf_bacnet_dlen: broadcast */
250 proto_tree_add_uint_format(bacnet_tree,
251 hf_bacnet_dlen, tvb, offset, 1, bacnet_dlen,
252 "Destination MAC Layer Address Length: %d indicates Broadcast on Destination Network",
256 } else if (bacnet_dlen==6) {
257 proto_tree_add_uint(bacnet_tree, hf_bacnet_dlen,
258 tvb, offset, 1, bacnet_dlen);
261 proto_tree_add_item(bacnet_tree,
262 hf_bacnet_dadr_eth, tvb, offset,
264 offset += bacnet_dlen;
265 } else if (bacnet_dlen<7) {
266 proto_tree_add_uint(bacnet_tree, hf_bacnet_dlen,
267 tvb, offset, 1, bacnet_dlen);
269 /* Other MAC formats should be included here */
270 proto_tree_add_item(bacnet_tree,
271 hf_bacnet_dadr_tmp, tvb, offset,
273 offset += bacnet_dlen;
275 proto_tree_add_uint_format(bacnet_tree,
276 hf_bacnet_dlen, tvb, offset, 1, bacnet_dlen,
277 "Destination MAC Layer Address Length: %d invalid!",
281 if (bacnet_control & BAC_CONTROL_SRC) { /* SNET, SLEN, SADR */
283 proto_tree_add_uint(bacnet_tree, hf_bacnet_snet,
284 tvb, offset, 2, tvb_get_ntohs(tvb, offset));
286 bacnet_slen = tvb_get_guint8(tvb, offset);
287 if( bacnet_slen == 0) { /* SLEN = 0 invalid */
288 proto_tree_add_uint_format(bacnet_tree,
289 hf_bacnet_slen, tvb, offset, 1, bacnet_slen,
290 "Source MAC Layer Address Length: %d invalid!",
293 } else if (bacnet_slen==6) {
295 proto_tree_add_uint(bacnet_tree, hf_bacnet_slen,
296 tvb, offset, 1, bacnet_slen);
299 proto_tree_add_item(bacnet_tree,
300 hf_bacnet_sadr_eth, tvb, offset,
302 offset += bacnet_slen;
303 } else if (bacnet_slen<6) { /* LON,ARCNET,MS/TP MAC */
305 proto_tree_add_uint(bacnet_tree, hf_bacnet_slen,
306 tvb, offset, 1, bacnet_slen);
308 /* Other MAC formats should be included here */
309 proto_tree_add_item(bacnet_tree,
310 hf_bacnet_sadr_tmp, tvb, offset,
312 offset += bacnet_slen;
314 proto_tree_add_uint_format(bacnet_tree,
315 hf_bacnet_slen, tvb, offset, 1, bacnet_slen,
316 "Source MAC Layer Address Length: %d invalid!",
321 if (bacnet_control & BAC_CONTROL_DEST) { /* Hopcount */
322 proto_tree_add_item(bacnet_tree, hf_bacnet_hopc,
323 tvb, offset, 1, FALSE);
326 /* Network Layer Message Type */
327 if (bacnet_control & BAC_CONTROL_NET) {
328 bacnet_mesgtyp = tvb_get_guint8(tvb, offset);
329 proto_tree_add_uint_format(bacnet_tree,
330 hf_bacnet_mesgtyp, tvb, offset, 1, bacnet_mesgtyp,
331 "Network Layer Message Type: %02x (%s)", bacnet_mesgtyp,
332 bacnet_mesgtyp_name(bacnet_mesgtyp));
336 * The standard says: "If Bit 7 of the control octet is 1 and
337 * the Message Type field contains a value in the range
338 * X'80' - X'FF', then a Vendor ID field shall be present (...)."
339 * We should not go any further in dissecting the packet if it's
340 * not present, but we don't know about that: No length field...
342 if ((bacnet_mesgtyp > 0x7f) && (bacnet_control == BAC_CONTROL_NET)) {
343 proto_tree_add_item(bacnet_tree, hf_bacnet_vendor,
344 tvb, offset, 2, FALSE);
346 /* attention: doesnt work here because of if(tree) */
347 call_dissector(data_handle,
348 tvb_new_subset(tvb, offset, -1, -1), pinfo, tree);
350 /* Performance Index (in I-Could-Be-Router-To-Network) */
351 if (bacnet_mesgtyp == BAC_NET_ICB_R) {
352 proto_tree_add_item(bacnet_tree, hf_bacnet_perf,
353 tvb, offset, 1, FALSE);
356 /* Reason, DNET (in Reject-Message-To-Network) */
357 if (bacnet_mesgtyp == BAC_NET_REJ) {
358 bacnet_rejectreason = tvb_get_guint8(tvb, offset);
359 proto_tree_add_uint_format(bacnet_tree,
360 hf_bacnet_rejectreason,
362 bacnet_rejectreason, "Rejection Reason: %d (%s)",
364 bacnet_rejectreason_name(bacnet_rejectreason));
366 proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
367 tvb, offset, 2, FALSE);
370 /* N*DNET (in Router-Busy-To-Network,Router-Available-To-Network) */
371 if ((bacnet_mesgtyp == BAC_NET_R_BUSY) ||
372 (bacnet_mesgtyp == BAC_NET_R_AVA) || (bacnet_mesgtyp == BAC_NET_IAM_R) ) {
373 while(tvb_reported_length_remaining(tvb, offset) > 1 ) {
374 proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
375 tvb, offset, 2, FALSE);
379 /* Initialize-Routing-Table */
380 if ( (bacnet_mesgtyp == BAC_NET_INIT_RTAB) ||
381 (bacnet_mesgtyp == BAC_NET_INIT_RTAB_ACK) ) {
382 bacnet_rportnum = tvb_get_guint8(tvb, offset);
383 /* number of ports */
384 proto_tree_add_uint(bacnet_tree, hf_bacnet_rportnum,
385 tvb, offset, 1, bacnet_rportnum);
387 for(i=0; i>bacnet_rportnum; i++) {
389 proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
390 tvb, offset, 2, FALSE);
393 proto_tree_add_item(bacnet_tree, hf_bacnet_portid,
394 tvb, offset, 1, FALSE);
396 /* Port Info Length */
397 bacnet_pinfolen = tvb_get_guint8(tvb, offset);
398 proto_tree_add_uint(bacnet_tree, hf_bacnet_pinfolen,
399 tvb, offset, 1, bacnet_pinfolen);
401 for(j=0; j>bacnet_pinfolen; j++){
403 proto_tree_add_item(bacnet_tree, hf_bacnet_pinfo,
404 tvb, offset, 1, FALSE);
410 proto_item_set_len(ti, offset);
413 /* dissect BACnet APDU
415 next_tvb = tvb_new_subset(tvb,offset,-1,-1);
416 if (bacnet_control & BAC_CONTROL_NET) {
417 /* Unknown function - dissect the payload as data */
418 call_dissector(data_handle, next_tvb, pinfo, tree);
420 /* APDU - call the APDU dissector */
421 call_dissector(bacapp_handle, next_tvb, pinfo, tree);
426 proto_register_bacnet(void)
428 static hf_register_info hf[] = {
429 { &hf_bacnet_version,
430 { "Version", "bacnet.version",
431 FT_UINT8, BASE_DEC, NULL, 0,
432 "BACnet Version", HFILL }
434 { &hf_bacnet_control,
435 { "Control", "bacnet.control",
436 FT_UINT8, BASE_HEX, NULL, 0xff,
437 "BACnet Control", HFILL }
439 { &hf_bacnet_control_net,
441 "bacnet.control_net",
442 FT_BOOLEAN, 8, TFS(&control_net_set_high),
443 BAC_CONTROL_NET, "BACnet Control", HFILL }
445 { &hf_bacnet_control_res1,
447 "bacnet.control_res1",
448 FT_BOOLEAN, 8, TFS(&control_res_high),
449 BAC_CONTROL_RES1, "BACnet Control", HFILL }
451 { &hf_bacnet_control_dest,
452 { "Destination Specifier",
453 "bacnet.control_dest",
454 FT_BOOLEAN, 8, TFS(&control_dest_high),
455 BAC_CONTROL_DEST, "BACnet Control", HFILL }
457 { &hf_bacnet_control_res2,
459 "bacnet.control_res2",
460 FT_BOOLEAN, 8, TFS(&control_res_high),
461 BAC_CONTROL_RES2, "BACnet Control", HFILL }
463 { &hf_bacnet_control_src,
464 { "Source specifier",
465 "bacnet.control_src",
466 FT_BOOLEAN, 8, TFS(&control_src_high),
467 BAC_CONTROL_SRC, "BACnet Control", HFILL }
469 { &hf_bacnet_control_expect,
471 "bacnet.control_expect",
472 FT_BOOLEAN, 8, TFS(&control_expect_high),
473 BAC_CONTROL_EXPECT, "BACnet Control", HFILL }
475 { &hf_bacnet_control_prio_high,
477 "bacnet.control_prio_high",
478 FT_BOOLEAN, 8, TFS(&control_prio_high_high),
479 BAC_CONTROL_PRIO_HIGH, "BACnet Control", HFILL }
481 { &hf_bacnet_control_prio_low,
483 "bacnet.control_prio_low",
484 FT_BOOLEAN, 8, TFS(&control_prio_low_high),
485 BAC_CONTROL_PRIO_LOW, "BACnet Control", HFILL }
488 { "Destination Network Address", "bacnet.dnet",
489 FT_UINT16, BASE_HEX, NULL, 0,
490 "Destination Network Address", HFILL }
493 { "Destination MAC Layer Address Length", "bacnet.dlen",
494 FT_UINT8, BASE_DEC, NULL, 0,
495 "Destination MAC Layer Address Length", HFILL }
497 { &hf_bacnet_dadr_eth,
498 { "Destination ISO 8802-3 MAC Address", "bacnet.dadr_eth",
499 FT_ETHER, BASE_HEX, NULL, 0,
500 "Destination ISO 8802-3 MAC Address", HFILL }
502 { &hf_bacnet_dadr_tmp,
503 { "Unknown Destination MAC", "bacnet.dadr_tmp",
504 FT_BYTES, BASE_HEX, NULL, 0,
505 "Unknown Destination MAC", HFILL }
508 { "Source Network Address", "bacnet.snet",
509 FT_UINT16, BASE_HEX, NULL, 0,
510 "Source Network Address", HFILL }
513 { "Source MAC Layer Address Length", "bacnet.slen",
514 FT_UINT8, BASE_DEC, NULL, 0,
515 "Source MAC Layer Address Length", HFILL }
517 { &hf_bacnet_sadr_eth,
518 { "SADR", "bacnet.sadr_eth",
519 FT_ETHER, BASE_HEX, NULL, 0,
520 "Source ISO 8802-3 MAC Address", HFILL }
522 { &hf_bacnet_sadr_tmp,
523 { "Unknown Source MAC", "bacnet.sadr_tmp",
524 FT_BYTES, BASE_HEX, NULL, 0,
525 "Unknown Source MAC", HFILL }
528 { "Hop Count", "bacnet.hopc",
529 FT_UINT8, BASE_DEC, NULL, 0,
532 { &hf_bacnet_mesgtyp,
533 { "Message Type", "bacnet.mesgtyp",
534 FT_UINT8, BASE_HEX, NULL, 0,
535 "Message Type", HFILL }
538 { "Vendor ID", "bacnet.vendor",
539 FT_UINT16, BASE_HEX, NULL, 0,
543 { "Performance Index", "bacnet.perf",
544 FT_UINT8, BASE_DEC, NULL, 0,
545 "Performance Index", HFILL }
547 { &hf_bacnet_rejectreason,
548 { "Reject Reason", "bacnet.rejectreason",
549 FT_UINT8, BASE_DEC, NULL, 0,
550 "Reject Reason", HFILL }
552 { &hf_bacnet_rportnum,
553 { "Number of Port Mappings", "bacnet.rportnum",
554 FT_UINT8, BASE_DEC, NULL, 0,
555 "Number of Port Mappings", HFILL }
557 { &hf_bacnet_pinfolen,
558 { "Port Info Length", "bacnet.pinfolen",
559 FT_UINT8, BASE_DEC, NULL, 0,
560 "Port Info Length", HFILL }
563 { "Port ID", "bacnet.portid",
564 FT_UINT8, BASE_HEX, NULL, 0,
568 { "Port Info", "bacnet.pinfo",
569 FT_UINT8, BASE_HEX, NULL, 0,
574 static gint *ett[] = {
579 proto_bacnet = proto_register_protocol("Building Automation and Control Network NPDU",
582 proto_register_field_array(proto_bacnet, hf, array_length(hf));
583 proto_register_subtree_array(ett, array_length(ett));
585 register_dissector("bacnet", dissect_bacnet, proto_bacnet);
589 proto_reg_handoff_bacnet(void)
591 dissector_handle_t bacnet_handle;
593 bacnet_handle = find_dissector("bacnet");
594 dissector_add("bvlc.function", 0x04, bacnet_handle);
595 dissector_add("bvlc.function", 0x09, bacnet_handle);
596 dissector_add("bvlc.function", 0x0a, bacnet_handle);
597 dissector_add("bvlc.function", 0x0b, bacnet_handle);
598 dissector_add("llc.dsap", SAP_BACNET, bacnet_handle);
599 bacapp_handle = find_dissector("bacapp");
600 data_handle = find_dissector("data");