2 * Routines for BACnet (NPDU) dissection
3 * Copyright 2001, Hartmut Mueller <hartmut@abmlinux.org>, FH Dortmund
5 * $Id: packet-bacnet.c,v 1.18 2004/05/12 19:55:14 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;
208 /* I don't know the length of the NPDU by now. Setting the length after dissection */
209 ti = proto_tree_add_item(tree, proto_bacnet, tvb, 0, -1, FALSE);
211 bacnet_tree = proto_item_add_subtree(ti, ett_bacnet);
213 proto_tree_add_uint_format(bacnet_tree, hf_bacnet_version, tvb,
215 bacnet_version,"Version: 0x%02x (%s)",bacnet_version,
216 (bacnet_version == 0x01)?"ASHRAE 135-1995":"unknown");
218 ct = proto_tree_add_uint(bacnet_tree, hf_bacnet_control,
219 tvb, offset, 1, bacnet_control);
220 control_tree = proto_item_add_subtree(ct, ett_bacnet_control);
221 proto_tree_add_boolean(control_tree, hf_bacnet_control_net,
222 tvb, offset, 1, bacnet_control);
223 proto_tree_add_boolean(control_tree, hf_bacnet_control_res1, tvb,
224 offset, 1, bacnet_control);
225 proto_tree_add_boolean(control_tree, hf_bacnet_control_dest, tvb,
226 offset, 1, bacnet_control);
227 proto_tree_add_boolean(control_tree, hf_bacnet_control_res2, tvb,
228 offset, 1, bacnet_control);
229 proto_tree_add_boolean(control_tree, hf_bacnet_control_src, tvb,
230 offset, 1, bacnet_control);
231 proto_tree_add_boolean(control_tree, hf_bacnet_control_expect, tvb,
232 offset, 1, bacnet_control);
233 proto_tree_add_boolean(control_tree, hf_bacnet_control_prio_high,
234 tvb, offset, 1, bacnet_control);
235 proto_tree_add_boolean(control_tree, hf_bacnet_control_prio_low,
236 tvb, offset, 1, bacnet_control);
238 if (bacnet_control & BAC_CONTROL_DEST) { /* DNET, DLEN, DADR */
239 proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
240 tvb, offset, 2, FALSE);
242 bacnet_dlen = tvb_get_guint8(tvb, offset);
243 /* DLEN = 0 is broadcast on dest.network */
244 if( bacnet_dlen == 0) {
245 /* append to hf_bacnet_dlen: broadcast */
246 proto_tree_add_uint_format(bacnet_tree,
247 hf_bacnet_dlen, tvb, offset, 1, bacnet_dlen,
248 "Destination MAC Layer Address Length: %d indicates Broadcast on Destination Network",
252 } else if (bacnet_dlen==6) {
253 proto_tree_add_uint(bacnet_tree, hf_bacnet_dlen,
254 tvb, offset, 1, bacnet_dlen);
257 proto_tree_add_item(bacnet_tree,
258 hf_bacnet_dadr_eth, tvb, offset,
260 offset += bacnet_dlen;
261 } else if (bacnet_dlen<7) {
262 proto_tree_add_uint(bacnet_tree, hf_bacnet_dlen,
263 tvb, offset, 1, bacnet_dlen);
265 /* Other MAC formats should be included here */
266 proto_tree_add_item(bacnet_tree,
267 hf_bacnet_dadr_tmp, tvb, offset,
269 offset += bacnet_dlen;
271 proto_tree_add_uint_format(bacnet_tree,
272 hf_bacnet_dlen, tvb, offset, 1, bacnet_dlen,
273 "Destination MAC Layer Address Length: %d invalid!",
277 if (bacnet_control & BAC_CONTROL_SRC) { /* SNET, SLEN, SADR */
279 proto_tree_add_uint(bacnet_tree, hf_bacnet_snet,
280 tvb, offset, 2, tvb_get_ntohs(tvb, offset));
282 bacnet_slen = tvb_get_guint8(tvb, offset);
283 if( bacnet_slen == 0) { /* SLEN = 0 invalid */
284 proto_tree_add_uint_format(bacnet_tree,
285 hf_bacnet_slen, tvb, offset, 1, bacnet_slen,
286 "Source MAC Layer Address Length: %d invalid!",
289 } else if (bacnet_slen==6) {
291 proto_tree_add_uint(bacnet_tree, hf_bacnet_slen,
292 tvb, offset, 1, bacnet_slen);
295 proto_tree_add_item(bacnet_tree,
296 hf_bacnet_sadr_eth, tvb, offset,
298 offset += bacnet_slen;
299 } else if (bacnet_slen<6) { /* LON,ARCNET,MS/TP MAC */
301 proto_tree_add_uint(bacnet_tree, hf_bacnet_slen,
302 tvb, offset, 1, bacnet_slen);
304 /* Other MAC formats should be included here */
305 proto_tree_add_item(bacnet_tree,
306 hf_bacnet_sadr_tmp, tvb, offset,
308 offset += bacnet_slen;
310 proto_tree_add_uint_format(bacnet_tree,
311 hf_bacnet_slen, tvb, offset, 1, bacnet_slen,
312 "Source MAC Layer Address Length: %d invalid!",
317 if (bacnet_control & BAC_CONTROL_DEST) { /* Hopcount */
318 proto_tree_add_item(bacnet_tree, hf_bacnet_hopc,
319 tvb, offset, 1, FALSE);
322 /* Network Layer Message Type */
323 if (bacnet_control & BAC_CONTROL_NET) {
324 bacnet_mesgtyp = tvb_get_guint8(tvb, offset);
325 proto_tree_add_uint_format(bacnet_tree,
326 hf_bacnet_mesgtyp, tvb, offset, 1, bacnet_mesgtyp,
327 "Network Layer Message Type: %02x (%s)", bacnet_mesgtyp,
328 bacnet_mesgtyp_name(bacnet_mesgtyp));
332 * The standard says: "If Bit 7 of the control octet is 1 and
333 * the Message Type field contains a value in the range
334 * X'80' - X'FF', then a Vendor ID field shall be present (...)."
335 * We should not go any further in dissecting the packet if it's
336 * not present, but we don't know about that: No length field...
338 if ((bacnet_mesgtyp > 0x7f) && (bacnet_control == BAC_CONTROL_NET)) {
339 proto_tree_add_item(bacnet_tree, hf_bacnet_vendor,
340 tvb, offset, 2, FALSE);
342 /* attention: doesnt work here because of if(tree) */
343 call_dissector(data_handle,
344 tvb_new_subset(tvb, offset, -1, -1), pinfo, tree);
346 /* Performance Index (in I-Could-Be-Router-To-Network) */
347 if (bacnet_mesgtyp == BAC_NET_ICB_R) {
348 proto_tree_add_item(bacnet_tree, hf_bacnet_perf,
349 tvb, offset, 1, FALSE);
352 /* Reason, DNET (in Reject-Message-To-Network) */
353 if (bacnet_mesgtyp == BAC_NET_REJ) {
354 bacnet_rejectreason = tvb_get_guint8(tvb, offset);
355 proto_tree_add_uint_format(bacnet_tree,
356 hf_bacnet_rejectreason,
358 bacnet_rejectreason, "Rejection Reason: %d (%s)",
360 bacnet_rejectreason_name(bacnet_rejectreason));
362 proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
363 tvb, offset, 2, FALSE);
366 /* N*DNET (in Router-Busy-To-Network,Router-Available-To-Network) */
367 if ((bacnet_mesgtyp == BAC_NET_R_BUSY) ||
368 (bacnet_mesgtyp == BAC_NET_R_AVA) || (bacnet_mesgtyp == BAC_NET_IAM_R) ) {
369 while(tvb_reported_length_remaining(tvb, offset) > 1 ) {
370 proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
371 tvb, offset, 2, FALSE);
375 /* Initialize-Routing-Table */
376 if ( (bacnet_mesgtyp == BAC_NET_INIT_RTAB) ||
377 (bacnet_mesgtyp == BAC_NET_INIT_RTAB_ACK) ) {
378 bacnet_rportnum = tvb_get_guint8(tvb, offset);
379 /* number of ports */
380 proto_tree_add_uint(bacnet_tree, hf_bacnet_rportnum,
381 tvb, offset, 1, bacnet_rportnum);
383 for(i=0; i>bacnet_rportnum; i++) {
385 proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
386 tvb, offset, 2, FALSE);
389 proto_tree_add_item(bacnet_tree, hf_bacnet_portid,
390 tvb, offset, 1, FALSE);
392 /* Port Info Length */
393 bacnet_pinfolen = tvb_get_guint8(tvb, offset);
394 proto_tree_add_uint(bacnet_tree, hf_bacnet_pinfolen,
395 tvb, offset, 1, bacnet_pinfolen);
397 for(j=0; j>bacnet_pinfolen; j++){
399 proto_tree_add_item(bacnet_tree, hf_bacnet_pinfo,
400 tvb, offset, 1, FALSE);
405 proto_item_set_len(ti, offset);
407 /* dissect BACnet APDU
409 next_tvb = tvb_new_subset(tvb,offset,-1,-1);
410 if (bacnet_control & BAC_CONTROL_NET) {
411 /* Unknown function - dissect the payload as data */
412 call_dissector(data_handle, next_tvb, pinfo, tree);
414 /* APDU - call the APDU dissector */
415 call_dissector(bacapp_handle, next_tvb, pinfo, tree);
420 proto_register_bacnet(void)
422 static hf_register_info hf[] = {
423 { &hf_bacnet_version,
424 { "Version", "bacnet.version",
425 FT_UINT8, BASE_DEC, NULL, 0,
426 "BACnet Version", HFILL }
428 { &hf_bacnet_control,
429 { "Control", "bacnet.control",
430 FT_UINT8, BASE_HEX, NULL, 0,
431 "BACnet Control", HFILL }
433 { &hf_bacnet_control_net,
435 "bacnet.control_net",
436 FT_BOOLEAN, 8, TFS(&control_net_set_high),
437 BAC_CONTROL_NET, "BACnet Control", HFILL }
439 { &hf_bacnet_control_res1,
441 "bacnet.control_res1",
442 FT_BOOLEAN, 8, TFS(&control_res_high),
443 BAC_CONTROL_RES1, "BACnet Control", HFILL }
445 { &hf_bacnet_control_dest,
446 { "Destination Specifier",
447 "bacnet.control_dest",
448 FT_BOOLEAN, 8, TFS(&control_dest_high),
449 BAC_CONTROL_DEST, "BACnet Control", HFILL }
451 { &hf_bacnet_control_res2,
453 "bacnet.control_res2",
454 FT_BOOLEAN, 8, TFS(&control_res_high),
455 BAC_CONTROL_RES2, "BACnet Control", HFILL }
457 { &hf_bacnet_control_src,
458 { "Source specifier",
459 "bacnet.control_src",
460 FT_BOOLEAN, 8, TFS(&control_src_high),
461 BAC_CONTROL_SRC, "BACnet Control", HFILL }
463 { &hf_bacnet_control_expect,
465 "bacnet.control_expect",
466 FT_BOOLEAN, 8, TFS(&control_expect_high),
467 BAC_CONTROL_EXPECT, "BACnet Control", HFILL }
469 { &hf_bacnet_control_prio_high,
471 "bacnet.control_prio_high",
472 FT_BOOLEAN, 8, TFS(&control_prio_high_high),
473 BAC_CONTROL_PRIO_HIGH, "BACnet Control", HFILL }
475 { &hf_bacnet_control_prio_low,
477 "bacnet.control_prio_low",
478 FT_BOOLEAN, 8, TFS(&control_prio_low_high),
479 BAC_CONTROL_PRIO_LOW, "BACnet Control", HFILL }
482 { "Destination Network Address", "bacnet.dnet",
483 FT_UINT16, BASE_HEX, NULL, 0,
484 "Destination Network Address", HFILL }
487 { "Destination MAC Layer Address Length", "bacnet.dlen",
488 FT_UINT8, BASE_DEC, NULL, 0,
489 "Destination MAC Layer Address Length", HFILL }
491 { &hf_bacnet_dadr_eth,
492 { "Destination ISO 8802-3 MAC Address", "bacnet.dadr_eth",
493 FT_ETHER, BASE_HEX, NULL, 0,
494 "Destination ISO 8802-3 MAC Address", HFILL }
496 { &hf_bacnet_dadr_tmp,
497 { "Unknown Destination MAC", "bacnet.dadr_tmp",
498 FT_BYTES, BASE_HEX, NULL, 0,
499 "Unknown Destination MAC", HFILL }
502 { "Source Network Address", "bacnet.snet",
503 FT_UINT16, BASE_HEX, NULL, 0,
504 "Source Network Address", HFILL }
507 { "Source MAC Layer Address Length", "bacnet.slen",
508 FT_UINT8, BASE_DEC, NULL, 0,
509 "Source MAC Layer Address Length", HFILL }
511 { &hf_bacnet_sadr_eth,
512 { "SADR", "bacnet.sadr_eth",
513 FT_ETHER, BASE_HEX, NULL, 0,
514 "Source ISO 8802-3 MAC Address", HFILL }
516 { &hf_bacnet_sadr_tmp,
517 { "Unknown Source MAC", "bacnet.sadr_tmp",
518 FT_BYTES, BASE_HEX, NULL, 0,
519 "Unknown Source MAC", HFILL }
522 { "Hop Count", "bacnet.hopc",
523 FT_UINT8, BASE_DEC, NULL, 0,
526 { &hf_bacnet_mesgtyp,
527 { "Message Type", "bacnet.mesgtyp",
528 FT_UINT8, BASE_HEX, NULL, 0,
529 "Message Type", HFILL }
532 { "Vendor ID", "bacnet.vendor",
533 FT_UINT16, BASE_HEX, NULL, 0,
537 { "Performance Index", "bacnet.perf",
538 FT_UINT8, BASE_DEC, NULL, 0,
539 "Performance Index", HFILL }
541 { &hf_bacnet_rejectreason,
542 { "Reject Reason", "bacnet.rejectreason",
543 FT_UINT8, BASE_DEC, NULL, 0,
544 "Reject Reason", HFILL }
546 { &hf_bacnet_rportnum,
547 { "Number of Port Mappings", "bacnet.rportnum",
548 FT_UINT8, BASE_DEC, NULL, 0,
549 "Number of Port Mappings", HFILL }
551 { &hf_bacnet_pinfolen,
552 { "Port Info Length", "bacnet.pinfolen",
553 FT_UINT8, BASE_DEC, NULL, 0,
554 "Port Info Length", HFILL }
557 { "Port ID", "bacnet.portid",
558 FT_UINT8, BASE_HEX, NULL, 0,
562 { "Port Info", "bacnet.pinfo",
563 FT_UINT8, BASE_HEX, NULL, 0,
568 static gint *ett[] = {
573 proto_bacnet = proto_register_protocol("Building Automation and Control Network NPDU",
576 proto_register_field_array(proto_bacnet, hf, array_length(hf));
577 proto_register_subtree_array(ett, array_length(ett));
579 register_dissector("bacnet", dissect_bacnet, proto_bacnet);
583 proto_reg_handoff_bacnet(void)
585 dissector_handle_t bacnet_handle;
587 bacnet_handle = find_dissector("bacnet");
588 dissector_add("bvlc.function", 0x04, bacnet_handle);
589 dissector_add("bvlc.function", 0x09, bacnet_handle);
590 dissector_add("bvlc.function", 0x0a, bacnet_handle);
591 dissector_add("bvlc.function", 0x0b, bacnet_handle);
592 dissector_add("llc.dsap", SAP_BACNET, bacnet_handle);
593 bacapp_handle = find_dissector("bacapp");
594 data_handle = find_dissector("data");