2 * Routines for BACnet (NPDU) dissection
3 * Copyright 2001, Hartmut Mueller <hartmut@abmlinux.org>, FH Dortmund
5 * $Id: packet-bacnet.c,v 1.16 2003/01/23 09:54:54 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>
41 #include "arcnet_pids.h"
43 static dissector_handle_t bacapp_handle;
44 static dissector_handle_t data_handle;
47 bacnet_mesgtyp_name (guint8 bacnet_mesgtyp){
48 static const char *type_names[] = {
49 "Who-Is-Router-To-Network",
50 "I-Am-Router-To-Network",
51 "I-Could-Be-Router-To-Network",
52 "Reject-Message-To-Network",
53 "Router-Busy-To-Network",
54 "Router-Available-To-Network",
55 "Initialize-Routing-Table",
56 "Initialize-Routing-Table-Ack",
57 "Establish-Connection-To-Network",
58 "Disconnect-Connection-To-Network"
60 if(bacnet_mesgtyp < 0x0a) {
61 return type_names[bacnet_mesgtyp];
63 return (bacnet_mesgtyp < 0x80)? "Reserved for Use by ASHRAE" : "Vendor Proprietary Message";
68 bacnet_rejectreason_name (guint8 bacnet_rejectreason) {
69 static const char *type_names[] = {
71 "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.",
72 "The router is busy and unable to accept messages for the specified DNET at the present time.",
73 "It is an unknown network layer message type.",
74 "The message is too long to be routed to this DNET.",
75 "The router is no longer directly connected to DNET but can reconnect if requested.",
76 "The router is no longer directly connected to DNET and cannot reconnect even if requested."
78 return (bacnet_rejectreason > 6)? "Invalid Rejection Reason.": type_names[bacnet_rejectreason];
81 /* Network Layer Control Information */
82 #define BAC_CONTROL_NET 0x80
83 #define BAC_CONTROL_RES1 0x40
84 #define BAC_CONTROL_DEST 0x20
85 #define BAC_CONTROL_RES2 0x10
86 #define BAC_CONTROL_SRC 0x08
87 #define BAC_CONTROL_EXPECT 0x04
88 #define BAC_CONTROL_PRIO_HIGH 0x02
89 #define BAC_CONTROL_PRIO_LOW 0x01
91 /* Network Layer Message Types */
92 #define BAC_NET_WHO_R 0x00
93 #define BAC_NET_IAM_R 0x01
94 #define BAC_NET_ICB_R 0x02
95 #define BAC_NET_REJ 0x03
96 #define BAC_NET_R_BUSY 0x04
97 #define BAC_NET_R_AVA 0x05
98 #define BAC_NET_INIT_RTAB 0x06
99 #define BAC_NET_INIT_RTAB_ACK 0x07
100 #define BAC_NET_EST_CON 0x08
101 #define BAC_NET_DISC_CON 0x09
103 static const true_false_string control_net_set_high = {
104 "network layer message, message type field present.",
105 "BACnet APDU, message type field absent."
108 static const true_false_string control_res_high = {
109 "Shall be zero, but is one.",
110 "Shall be zero and is zero."
112 static const true_false_string control_dest_high = {
113 "DNET, DLEN and Hop Count present. If DLEN=0: broadcast, dest. address field absent.",
114 "DNET, DLEN, DADR and Hop Count absent."
117 static const true_false_string control_src_high = {
118 "SNET, SLEN and SADR present, SLEN=0 invalid, SLEN specifies length of SADR",
119 "SNET, SLEN and SADR absent"
122 static const true_false_string control_expect_high = {
123 "BACnet-Confirmed-Request-PDU, a segment of BACnet-ComplexACK-PDU or Network Message expecting a reply present.",
124 "Other than a BACnet-Confirmed-Request-PDU, segment of BACnet-ComplexACK-PDU or network layer message expecting a reply present."
127 static const true_false_string control_prio_high_high = {
128 "Life Safety or Critical Equipment message.",
129 "Not a Life Safety or Critical Equipment message."
132 static const true_false_string control_prio_low_high = {
138 static int proto_bacnet = -1;
139 static int hf_bacnet_version = -1;
140 static int hf_bacnet_control = -1;
141 static int hf_bacnet_control_net = -1;
142 static int hf_bacnet_control_res1 = -1;
143 static int hf_bacnet_control_dest = -1;
144 static int hf_bacnet_control_res2 = -1;
145 static int hf_bacnet_control_src = -1;
146 static int hf_bacnet_control_expect = -1;
147 static int hf_bacnet_control_prio_high = -1;
148 static int hf_bacnet_control_prio_low = -1;
149 static int hf_bacnet_dnet = -1;
150 static int hf_bacnet_dlen = -1;
151 static int hf_bacnet_dadr_eth = -1;
152 static int hf_bacnet_dadr_tmp = -1;
153 static int hf_bacnet_snet = -1;
154 static int hf_bacnet_slen = -1;
155 static int hf_bacnet_sadr_eth = -1;
156 static int hf_bacnet_sadr_tmp = -1;
157 static int hf_bacnet_hopc = -1;
158 static int hf_bacnet_mesgtyp = -1;
159 static int hf_bacnet_vendor = -1;
160 static int hf_bacnet_perf = -1;
161 static int hf_bacnet_rejectreason = -1;
162 static int hf_bacnet_rportnum = -1;
163 static int hf_bacnet_portid = -1;
164 static int hf_bacnet_pinfolen = -1;
165 static int hf_bacnet_pinfo = -1;
167 static gint ett_bacnet = -1;
168 static gint ett_bacnet_control = -1;
171 dissect_bacnet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
175 proto_tree *bacnet_tree;
176 proto_tree *control_tree;
179 guint8 bacnet_version;
180 guint8 bacnet_control;
183 guint8 bacnet_mesgtyp;
184 guint8 bacnet_rejectreason;
185 guint8 bacnet_rportnum;
186 guint8 bacnet_pinfolen;
191 if (check_col(pinfo->cinfo, COL_PROTOCOL))
192 col_set_str(pinfo->cinfo, COL_PROTOCOL, "BACnet-NPDU");
194 if (check_col(pinfo->cinfo, COL_INFO))
195 col_set_str(pinfo->cinfo, COL_INFO, "Building Automation and Control Network NPDU");
198 bacnet_version = tvb_get_guint8(tvb, offset);
199 bacnet_control = tvb_get_guint8(tvb, offset+1);
203 bacnet_rejectreason = 0;
211 /* I don't know the length of the NPDU by know. Setting the length after dissection */
212 ti = proto_tree_add_item(tree, proto_bacnet, tvb, 0, -1, FALSE);
214 bacnet_tree = proto_item_add_subtree(ti, ett_bacnet);
216 proto_tree_add_uint_format(bacnet_tree, hf_bacnet_version, tvb,
218 bacnet_version,"Version: 0x%02x (%s)",bacnet_version,
219 (bacnet_version == 0x01)?"ASHRAE 135-1995":"unknown");
221 ct = proto_tree_add_uint_format(bacnet_tree, hf_bacnet_control,
223 bacnet_control,"Control: 0x%02x",bacnet_control);
224 control_tree = proto_item_add_subtree(ct,
226 proto_tree_add_boolean(control_tree, hf_bacnet_control_net,
227 tvb, offset, 1, bacnet_control);
228 proto_tree_add_boolean(control_tree, hf_bacnet_control_res1, tvb,
229 offset, 1, bacnet_control);
230 proto_tree_add_boolean(control_tree, hf_bacnet_control_dest, tvb,
231 offset, 1, bacnet_control);
232 proto_tree_add_boolean(control_tree, hf_bacnet_control_res2, tvb,
233 offset, 1, bacnet_control);
234 proto_tree_add_boolean(control_tree, hf_bacnet_control_src, tvb,
235 offset, 1, bacnet_control);
236 proto_tree_add_boolean(control_tree, hf_bacnet_control_expect, tvb,
237 offset, 1, bacnet_control);
238 proto_tree_add_boolean(control_tree, hf_bacnet_control_prio_high,
239 tvb, offset, 1, bacnet_control);
240 proto_tree_add_boolean(control_tree, hf_bacnet_control_prio_low,
241 tvb, offset, 1, bacnet_control);
243 if (bacnet_control & BAC_CONTROL_DEST) { /* DNET, DLEN, DADR */
244 proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
245 tvb, offset, 2, FALSE);
247 bacnet_dlen = tvb_get_guint8(tvb, offset);
248 /* DLEN = 0 is broadcast on dest.network */
249 if( bacnet_dlen == 0) {
250 /* append to hf_bacnet_dlen: broadcast */
251 proto_tree_add_uint_format(bacnet_tree,
252 hf_bacnet_dlen, tvb, offset, 1, bacnet_dlen,
253 "Destination MAC Layer Address Length: %d indicates Broadcast on Destination Network",
257 } else if (bacnet_dlen==6) {
258 proto_tree_add_uint(bacnet_tree, hf_bacnet_dlen,
259 tvb, offset, 1, bacnet_dlen);
262 proto_tree_add_item(bacnet_tree,
263 hf_bacnet_dadr_eth, tvb, offset,
265 offset += bacnet_dlen;
266 } else if (bacnet_dlen<7) {
267 proto_tree_add_uint(bacnet_tree, hf_bacnet_dlen,
268 tvb, offset, 1, bacnet_dlen);
270 /* Other MAC formats should be included here */
271 proto_tree_add_item(bacnet_tree,
272 hf_bacnet_dadr_tmp, tvb, offset,
274 offset += bacnet_dlen;
276 proto_tree_add_uint_format(bacnet_tree,
277 hf_bacnet_dlen, tvb, offset, 1, bacnet_dlen,
278 "Destination MAC Layer Address Length: %d invalid!",
282 if (bacnet_control & BAC_CONTROL_SRC) { /* SNET, SLEN, SADR */
284 proto_tree_add_uint(bacnet_tree, hf_bacnet_snet,
285 tvb, offset, 2, tvb_get_ntohs(tvb, offset));
287 bacnet_slen = tvb_get_guint8(tvb, offset);
288 if( bacnet_slen == 0) { /* SLEN = 0 invalid */
289 proto_tree_add_uint_format(bacnet_tree,
290 hf_bacnet_slen, tvb, offset, 1, bacnet_slen,
291 "Source MAC Layer Address Length: %d invalid!",
294 } else if (bacnet_slen==6) {
296 proto_tree_add_uint(bacnet_tree, hf_bacnet_slen,
297 tvb, offset, 1, bacnet_slen);
300 proto_tree_add_item(bacnet_tree,
301 hf_bacnet_sadr_eth, tvb, offset,
303 offset += bacnet_slen;
304 } else if (bacnet_slen<6) { /* LON,ARCNET,MS/TP MAC */
306 proto_tree_add_uint(bacnet_tree, hf_bacnet_slen,
307 tvb, offset, 1, bacnet_slen);
309 /* Other MAC formats should be included here */
310 proto_tree_add_item(bacnet_tree,
311 hf_bacnet_sadr_tmp, tvb, offset,
313 offset += bacnet_slen;
315 proto_tree_add_uint_format(bacnet_tree,
316 hf_bacnet_slen, tvb, offset, 1, bacnet_slen,
317 "Source MAC Layer Address Length: %d invalid!",
322 if (bacnet_control & BAC_CONTROL_DEST) { /* Hopcount */
323 proto_tree_add_item(bacnet_tree, hf_bacnet_hopc,
324 tvb, offset, 1, FALSE);
327 /* Network Layer Message Type */
328 if (bacnet_control & BAC_CONTROL_NET) {
329 bacnet_mesgtyp = tvb_get_guint8(tvb, offset);
330 proto_tree_add_uint_format(bacnet_tree,
331 hf_bacnet_mesgtyp, tvb, offset, 1, bacnet_mesgtyp,
332 "Network Layer Message Type: %02x (%s)", bacnet_mesgtyp,
333 bacnet_mesgtyp_name(bacnet_mesgtyp));
337 * The standard says: "If Bit 7 of the control octet is 1 and
338 * the Message Type field contains a value in the range
339 * X'80' - X'FF', then a Vendor ID field shall be present (...)."
340 * We should not go any further in dissecting the packet if it's
341 * not present, but we don't know about that: No length field...
343 if ((bacnet_mesgtyp > 0x7f) && (bacnet_control == BAC_CONTROL_NET)) {
344 proto_tree_add_item(bacnet_tree, hf_bacnet_vendor,
345 tvb, offset, 2, FALSE);
347 /* attention: doesnt work here because of if(tree) */
348 call_dissector(data_handle,
349 tvb_new_subset(tvb, offset, -1, -1), pinfo, tree);
351 /* Performance Index (in I-Could-Be-Router-To-Network) */
352 if (bacnet_mesgtyp == BAC_NET_ICB_R) {
353 proto_tree_add_item(bacnet_tree, hf_bacnet_perf,
354 tvb, offset, 1, FALSE);
357 /* Reason, DNET (in Reject-Message-To-Network) */
358 if (bacnet_mesgtyp == BAC_NET_REJ) {
359 bacnet_rejectreason = tvb_get_guint8(tvb, offset);
360 proto_tree_add_uint_format(bacnet_tree,
361 hf_bacnet_rejectreason,
363 bacnet_rejectreason, "Rejection Reason: %d (%s)",
365 bacnet_rejectreason_name(bacnet_rejectreason));
367 proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
368 tvb, offset, 2, FALSE);
371 /* N*DNET (in Router-Busy-To-Network,Router-Available-To-Network) */
372 if ((bacnet_mesgtyp == BAC_NET_R_BUSY) ||
373 (bacnet_mesgtyp == BAC_NET_R_AVA) || (bacnet_mesgtyp == BAC_NET_IAM_R) ) {
374 while(tvb_reported_length_remaining(tvb, offset) > 1 ) {
375 proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
376 tvb, offset, 2, FALSE);
380 /* Initialize-Routing-Table */
381 if ( (bacnet_mesgtyp == BAC_NET_INIT_RTAB) ||
382 (bacnet_mesgtyp == BAC_NET_INIT_RTAB_ACK) ) {
383 bacnet_rportnum = tvb_get_guint8(tvb, offset);
384 /* number of ports */
385 proto_tree_add_uint(bacnet_tree, hf_bacnet_rportnum,
386 tvb, offset, 1, bacnet_rportnum);
388 for(i=0; i>bacnet_rportnum; i++) {
390 proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
391 tvb, offset, 2, FALSE);
394 proto_tree_add_item(bacnet_tree, hf_bacnet_portid,
395 tvb, offset, 1, FALSE);
397 /* Port Info Length */
398 bacnet_pinfolen = tvb_get_guint8(tvb, offset);
399 proto_tree_add_uint(bacnet_tree, hf_bacnet_pinfolen,
400 tvb, offset, 1, bacnet_pinfolen);
402 for(j=0; j>bacnet_pinfolen; j++){
404 proto_tree_add_item(bacnet_tree, hf_bacnet_pinfo,
405 tvb, offset, 1, FALSE);
411 proto_item_set_len(ti, offset);
414 /* dissect BACnet APDU
416 next_tvb = tvb_new_subset(tvb,offset,-1,-1);
417 if (bacnet_control & BAC_CONTROL_NET) {
418 /* Unknown function - dissect the payload as data */
419 call_dissector(data_handle, next_tvb, pinfo, tree);
421 /* APDU - call the APDU dissector */
422 call_dissector(bacapp_handle, next_tvb, pinfo, tree);
427 proto_register_bacnet(void)
429 static hf_register_info hf[] = {
430 { &hf_bacnet_version,
431 { "Version", "bacnet.version",
432 FT_UINT8, BASE_DEC, NULL, 0,
433 "BACnet Version", HFILL }
435 { &hf_bacnet_control,
436 { "Control", "bacnet.control",
437 FT_UINT8, BASE_HEX, NULL, 0xff,
438 "BACnet Control", HFILL }
440 { &hf_bacnet_control_net,
442 "bacnet.control_net",
443 FT_BOOLEAN, 8, TFS(&control_net_set_high),
444 BAC_CONTROL_NET, "BACnet Control", HFILL }
446 { &hf_bacnet_control_res1,
448 "bacnet.control_res1",
449 FT_BOOLEAN, 8, TFS(&control_res_high),
450 BAC_CONTROL_RES1, "BACnet Control", HFILL }
452 { &hf_bacnet_control_dest,
453 { "Destination Specifier",
454 "bacnet.control_dest",
455 FT_BOOLEAN, 8, TFS(&control_dest_high),
456 BAC_CONTROL_DEST, "BACnet Control", HFILL }
458 { &hf_bacnet_control_res2,
460 "bacnet.control_res2",
461 FT_BOOLEAN, 8, TFS(&control_res_high),
462 BAC_CONTROL_RES2, "BACnet Control", HFILL }
464 { &hf_bacnet_control_src,
465 { "Source specifier",
466 "bacnet.control_src",
467 FT_BOOLEAN, 8, TFS(&control_src_high),
468 BAC_CONTROL_SRC, "BACnet Control", HFILL }
470 { &hf_bacnet_control_expect,
472 "bacnet.control_expect",
473 FT_BOOLEAN, 8, TFS(&control_expect_high),
474 BAC_CONTROL_EXPECT, "BACnet Control", HFILL }
476 { &hf_bacnet_control_prio_high,
478 "bacnet.control_prio_high",
479 FT_BOOLEAN, 8, TFS(&control_prio_high_high),
480 BAC_CONTROL_PRIO_HIGH, "BACnet Control", HFILL }
482 { &hf_bacnet_control_prio_low,
484 "bacnet.control_prio_low",
485 FT_BOOLEAN, 8, TFS(&control_prio_low_high),
486 BAC_CONTROL_PRIO_LOW, "BACnet Control", HFILL }
489 { "Destination Network Address", "bacnet.dnet",
490 FT_UINT16, BASE_HEX, NULL, 0,
491 "Destination Network Address", HFILL }
494 { "Destination MAC Layer Address Length", "bacnet.dlen",
495 FT_UINT8, BASE_DEC, NULL, 0,
496 "Destination MAC Layer Address Length", HFILL }
498 { &hf_bacnet_dadr_eth,
499 { "Destination ISO 8802-3 MAC Address", "bacnet.dadr_eth",
500 FT_ETHER, BASE_HEX, NULL, 0,
501 "Destination ISO 8802-3 MAC Address", HFILL }
503 { &hf_bacnet_dadr_tmp,
504 { "Unknown Destination MAC", "bacnet.dadr_tmp",
505 FT_BYTES, BASE_HEX, NULL, 0,
506 "Unknown Destination MAC", HFILL }
509 { "Source Network Address", "bacnet.snet",
510 FT_UINT16, BASE_HEX, NULL, 0,
511 "Source Network Address", HFILL }
514 { "Source MAC Layer Address Length", "bacnet.slen",
515 FT_UINT8, BASE_DEC, NULL, 0,
516 "Source MAC Layer Address Length", HFILL }
518 { &hf_bacnet_sadr_eth,
519 { "SADR", "bacnet.sadr_eth",
520 FT_ETHER, BASE_HEX, NULL, 0,
521 "Source ISO 8802-3 MAC Address", HFILL }
523 { &hf_bacnet_sadr_tmp,
524 { "Unknown Source MAC", "bacnet.sadr_tmp",
525 FT_BYTES, BASE_HEX, NULL, 0,
526 "Unknown Source MAC", HFILL }
529 { "Hop Count", "bacnet.hopc",
530 FT_UINT8, BASE_DEC, NULL, 0,
533 { &hf_bacnet_mesgtyp,
534 { "Message Type", "bacnet.mesgtyp",
535 FT_UINT8, BASE_HEX, NULL, 0,
536 "Message Type", HFILL }
539 { "Vendor ID", "bacnet.vendor",
540 FT_UINT16, BASE_HEX, NULL, 0,
544 { "Performance Index", "bacnet.perf",
545 FT_UINT8, BASE_DEC, NULL, 0,
546 "Performance Index", HFILL }
548 { &hf_bacnet_rejectreason,
549 { "Reject Reason", "bacnet.rejectreason",
550 FT_UINT8, BASE_DEC, NULL, 0,
551 "Reject Reason", HFILL }
553 { &hf_bacnet_rportnum,
554 { "Number of Port Mappings", "bacnet.rportnum",
555 FT_UINT8, BASE_DEC, NULL, 0,
556 "Number of Port Mappings", HFILL }
558 { &hf_bacnet_pinfolen,
559 { "Port Info Length", "bacnet.pinfolen",
560 FT_UINT8, BASE_DEC, NULL, 0,
561 "Port Info Length", HFILL }
564 { "Port ID", "bacnet.portid",
565 FT_UINT8, BASE_HEX, NULL, 0,
569 { "Port Info", "bacnet.pinfo",
570 FT_UINT8, BASE_HEX, NULL, 0,
575 static gint *ett[] = {
580 proto_bacnet = proto_register_protocol("Building Automation and Control Network NPDU",
583 proto_register_field_array(proto_bacnet, hf, array_length(hf));
584 proto_register_subtree_array(ett, array_length(ett));
586 register_dissector("bacnet", dissect_bacnet, proto_bacnet);
590 proto_reg_handoff_bacnet(void)
592 dissector_handle_t bacnet_handle;
594 bacnet_handle = find_dissector("bacnet");
595 dissector_add("bvlc.function", 0x04, bacnet_handle);
596 dissector_add("bvlc.function", 0x09, bacnet_handle);
597 dissector_add("bvlc.function", 0x0a, bacnet_handle);
598 dissector_add("bvlc.function", 0x0b, bacnet_handle);
599 dissector_add("llc.dsap", SAP_BACNET, bacnet_handle);
600 dissector_add("arcnet.protocol_id", ARCNET_PROTO_BACNET, bacnet_handle);
601 bacapp_handle = find_dissector("bacapp");
602 data_handle = find_dissector("data");