2 * Routines for BACnet (NPDU) dissection
3 * Copyright 2001, Hartmut Mueller <hartmut@abmlinux.org>, FH Dortmund
5 * $Id: packet-bacnet.c,v 1.13 2002/08/02 23:35:47 jmayer 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>
40 static dissector_handle_t bacapp_handle;
41 static dissector_handle_t data_handle;
44 bacnet_mesgtyp_name (guint8 bacnet_mesgtyp){
45 static const char *type_names[] = {
46 "Who-Is-Router-To-Network",
47 "I-Am-Router-To-Network",
48 "I-Could-Be-Router-To-Network",
49 "Reject-Message-To-Network",
50 "Router-Busy-To-Network",
51 "Router-Available-To-Network",
52 "Initialize-Routing-Table",
53 "Initialize-Routing-Table-Ack",
54 "Establish-Connection-To-Network",
55 "Disconnect-Connection-To-Network"
57 if(bacnet_mesgtyp < 0x0a) {
58 return type_names[bacnet_mesgtyp];
60 return (bacnet_mesgtyp < 0x80)? "Reserved for Use by ASHRAE" : "Vendor Proprietary Message";
65 bacnet_rejectreason_name (guint8 bacnet_rejectreason) {
66 static const char *type_names[] = {
68 "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.",
69 "The router is busy and unable to accept messages for the specified DNET at the present time.",
70 "It is an unknown network layer message type.",
71 "The message is too long to be routed to this DNET.",
72 "The router is no longer directly connected to DNET but can reconnect if requested.",
73 "The router is no longer directly connected to DNET and cannot reconnect even if requested."
75 return (bacnet_rejectreason > 6)? "Invalid Rejection Reason.": type_names[bacnet_rejectreason];
78 /* Network Layer Control Information */
79 #define BAC_CONTROL_NET 0x80
80 #define BAC_CONTROL_RES1 0x40
81 #define BAC_CONTROL_DEST 0x20
82 #define BAC_CONTROL_RES2 0x10
83 #define BAC_CONTROL_SRC 0x08
84 #define BAC_CONTROL_EXPECT 0x04
85 #define BAC_CONTROL_PRIO_HIGH 0x02
86 #define BAC_CONTROL_PRIO_LOW 0x01
88 /* Network Layer Message Types */
89 #define BAC_NET_WHO_R 0x00
90 #define BAC_NET_IAM_R 0x01
91 #define BAC_NET_ICB_R 0x02
92 #define BAC_NET_REJ 0x03
93 #define BAC_NET_R_BUSY 0x04
94 #define BAC_NET_R_AVA 0x05
95 #define BAC_NET_INIT_RTAB 0x06
96 #define BAC_NET_INIT_RTAB_ACK 0x07
97 #define BAC_NET_EST_CON 0x08
98 #define BAC_NET_DISC_CON 0x09
100 static const true_false_string control_net_set_high = {
101 "network layer message, message type field present.",
102 "BACnet APDU, message type field absent."
105 static const true_false_string control_res_high = {
106 "Shall be zero, but is one.",
107 "Shall be zero and is zero."
109 static const true_false_string control_dest_high = {
110 "DNET, DLEN and Hop Count present. If DLEN=0: broadcast, dest. address field absent.",
111 "DNET, DLEN, DADR and Hop Count absent."
114 static const true_false_string control_src_high = {
115 "SNET, SLEN and SADR present, SLEN=0 invalid, SLEN specifies length of SADR",
116 "SNET, SLEN and SADR absent"
119 static const true_false_string control_expect_high = {
120 "BACnet-Confirmed-Request-PDU, a segment of BACnet-ComplexACK-PDU or Network Message expecting a reply present.",
121 "Other than a BACnet-Confirmed-Request-PDU, segment of BACnet-ComplexACK-PDU or network layer message expecting a reply present."
124 static const true_false_string control_prio_high_high = {
125 "Life Safety or Critical Equipment message.",
126 "Not a Life Safety or Critical Equipment message."
129 static const true_false_string control_prio_low_high = {
135 static int proto_bacnet = -1;
136 static int hf_bacnet_version = -1;
137 static int hf_bacnet_control = -1;
138 static int hf_bacnet_control_net = -1;
139 static int hf_bacnet_control_res1 = -1;
140 static int hf_bacnet_control_dest = -1;
141 static int hf_bacnet_control_res2 = -1;
142 static int hf_bacnet_control_src = -1;
143 static int hf_bacnet_control_expect = -1;
144 static int hf_bacnet_control_prio_high = -1;
145 static int hf_bacnet_control_prio_low = -1;
146 static int hf_bacnet_dnet = -1;
147 static int hf_bacnet_dlen = -1;
148 static int hf_bacnet_dadr_eth = -1;
149 static int hf_bacnet_dadr_tmp = -1;
150 static int hf_bacnet_snet = -1;
151 static int hf_bacnet_slen = -1;
152 static int hf_bacnet_sadr_eth = -1;
153 static int hf_bacnet_sadr_tmp = -1;
154 static int hf_bacnet_hopc = -1;
155 static int hf_bacnet_mesgtyp = -1;
156 static int hf_bacnet_vendor = -1;
157 static int hf_bacnet_perf = -1;
158 static int hf_bacnet_rejectreason = -1;
159 static int hf_bacnet_rportnum = -1;
160 static int hf_bacnet_portid = -1;
161 static int hf_bacnet_pinfolen = -1;
162 static int hf_bacnet_pinfo = -1;
164 static gint ett_bacnet = -1;
165 static gint ett_bacnet_control = -1;
168 dissect_bacnet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
172 proto_tree *bacnet_tree;
173 proto_tree *control_tree;
176 guint8 bacnet_version;
177 guint8 bacnet_control;
180 guint8 bacnet_mesgtyp;
181 guint8 bacnet_rejectreason;
182 guint8 bacnet_rportnum;
183 guint8 bacnet_pinfolen;
188 if (check_col(pinfo->cinfo, COL_PROTOCOL))
189 col_set_str(pinfo->cinfo, COL_PROTOCOL, "BACnet-NPDU");
191 if (check_col(pinfo->cinfo, COL_INFO))
192 col_set_str(pinfo->cinfo, COL_INFO, "Building Automation and Control Network NPDU");
195 bacnet_version = tvb_get_guint8(tvb, offset);
196 bacnet_control = tvb_get_guint8(tvb, offset+1);
200 bacnet_rejectreason = 0;
208 /* I don't know the length of the NPDU by know. 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_format(bacnet_tree, hf_bacnet_control,
220 bacnet_control,"Control: 0x%02x",bacnet_control);
221 control_tree = proto_item_add_subtree(ct,
223 proto_tree_add_boolean(control_tree, hf_bacnet_control_net,
224 tvb, offset, 1, bacnet_control);
225 proto_tree_add_boolean(control_tree, hf_bacnet_control_res1, tvb,
226 offset, 1, bacnet_control);
227 proto_tree_add_boolean(control_tree, hf_bacnet_control_dest, tvb,
228 offset, 1, bacnet_control);
229 proto_tree_add_boolean(control_tree, hf_bacnet_control_res2, tvb,
230 offset, 1, bacnet_control);
231 proto_tree_add_boolean(control_tree, hf_bacnet_control_src, tvb,
232 offset, 1, bacnet_control);
233 proto_tree_add_boolean(control_tree, hf_bacnet_control_expect, tvb,
234 offset, 1, bacnet_control);
235 proto_tree_add_boolean(control_tree, hf_bacnet_control_prio_high,
236 tvb, offset, 1, bacnet_control);
237 proto_tree_add_boolean(control_tree, hf_bacnet_control_prio_low,
238 tvb, offset, 1, bacnet_control);
240 if (bacnet_control & BAC_CONTROL_DEST) { /* DNET, DLEN, DADR */
241 proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
242 tvb, offset, 2, FALSE);
244 bacnet_dlen = tvb_get_guint8(tvb, offset);
245 /* DLEN = 0 is broadcast on dest.network */
246 if( bacnet_dlen == 0) {
247 /* append to hf_bacnet_dlen: broadcast */
248 proto_tree_add_uint_format(bacnet_tree,
249 hf_bacnet_dlen, tvb, offset, 1, bacnet_dlen,
250 "Destination MAC Layer Address Length: %d indicates Broadcast on Destination Network",
254 } else if (bacnet_dlen==6) {
255 proto_tree_add_uint(bacnet_tree, hf_bacnet_dlen,
256 tvb, offset, 1, bacnet_dlen);
259 proto_tree_add_item(bacnet_tree,
260 hf_bacnet_dadr_eth, tvb, offset,
262 offset += bacnet_dlen;
263 } else if (bacnet_dlen<7) {
264 proto_tree_add_uint(bacnet_tree, hf_bacnet_dlen,
265 tvb, offset, 1, bacnet_dlen);
267 /* Other MAC formats should be included here */
268 proto_tree_add_item(bacnet_tree,
269 hf_bacnet_dadr_tmp, tvb, offset,
271 offset += bacnet_dlen;
273 proto_tree_add_uint_format(bacnet_tree,
274 hf_bacnet_dlen, tvb, offset, 1, bacnet_dlen,
275 "Destination MAC Layer Address Length: %d invalid!",
279 if (bacnet_control & BAC_CONTROL_SRC) { /* SNET, SLEN, SADR */
281 proto_tree_add_uint(bacnet_tree, hf_bacnet_snet,
282 tvb, offset, 2, tvb_get_ntohs(tvb, offset));
284 bacnet_slen = tvb_get_guint8(tvb, offset);
285 if( bacnet_slen == 0) { /* SLEN = 0 invalid */
286 proto_tree_add_uint_format(bacnet_tree,
287 hf_bacnet_slen, tvb, offset, 1, bacnet_slen,
288 "Source MAC Layer Address Length: %d invalid!",
291 } else if (bacnet_slen==6) {
293 proto_tree_add_uint(bacnet_tree, hf_bacnet_slen,
294 tvb, offset, 1, bacnet_slen);
297 proto_tree_add_item(bacnet_tree,
298 hf_bacnet_sadr_eth, tvb, offset,
300 offset += bacnet_slen;
301 } else if (bacnet_slen<6) { /* LON,ARCNET,MS/TP MAC */
303 proto_tree_add_uint(bacnet_tree, hf_bacnet_slen,
304 tvb, offset, 1, bacnet_slen);
306 /* Other MAC formats should be included here */
307 proto_tree_add_item(bacnet_tree,
308 hf_bacnet_sadr_tmp, tvb, offset,
310 offset += bacnet_slen;
312 proto_tree_add_uint_format(bacnet_tree,
313 hf_bacnet_slen, tvb, offset, 1, bacnet_slen,
314 "Source MAC Layer Address Length: %d invalid!",
319 if (bacnet_control & BAC_CONTROL_DEST) { /* Hopcount */
320 proto_tree_add_item(bacnet_tree, hf_bacnet_hopc,
321 tvb, offset, 1, FALSE);
324 /* Network Layer Message Type */
325 if (bacnet_control & BAC_CONTROL_NET) {
326 bacnet_mesgtyp = tvb_get_guint8(tvb, offset);
327 proto_tree_add_uint_format(bacnet_tree,
328 hf_bacnet_mesgtyp, tvb, offset, 1, bacnet_mesgtyp,
329 "Network Layer Message Type: %02x (%s)", bacnet_mesgtyp,
330 bacnet_mesgtyp_name(bacnet_mesgtyp));
334 * The standard says: "If Bit 7 of the control octet is 1 and
335 * the Message Type field contains a value in the range
336 * X'80' - X'FF', then a Vendor ID field shall be present (...)."
337 * We should not go any further in dissecting the packet if it's
338 * not present, but we don't know about that: No length field...
340 if ((bacnet_mesgtyp > 0x7f) && (bacnet_control == BAC_CONTROL_NET)) {
341 proto_tree_add_item(bacnet_tree, hf_bacnet_vendor,
342 tvb, offset, 2, FALSE);
344 /* attention: doesnt work here because of if(tree) */
345 call_dissector(data_handle,
346 tvb_new_subset(tvb, offset, -1, -1), pinfo, tree);
348 /* Performance Index (in I-Could-Be-Router-To-Network) */
349 if (bacnet_mesgtyp == BAC_NET_ICB_R) {
350 proto_tree_add_item(bacnet_tree, hf_bacnet_perf,
351 tvb, offset, 1, FALSE);
354 /* Reason, DNET (in Reject-Message-To-Network) */
355 if (bacnet_mesgtyp == BAC_NET_REJ) {
356 bacnet_rejectreason = tvb_get_guint8(tvb, offset);
357 proto_tree_add_uint_format(bacnet_tree,
358 hf_bacnet_rejectreason,
360 bacnet_rejectreason, "Rejection Reason: %d (%s)",
362 bacnet_rejectreason_name(bacnet_rejectreason));
364 proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
365 tvb, offset, 2, FALSE);
368 /* N*DNET (in Router-Busy-To-Network,Router-Available-To-Network) */
369 if ((bacnet_mesgtyp == BAC_NET_R_BUSY) ||
370 (bacnet_mesgtyp == BAC_NET_R_AVA) || (bacnet_mesgtyp == BAC_NET_IAM_R) ) {
371 while(tvb_reported_length_remaining(tvb, offset) > 1 ) {
372 proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
373 tvb, offset, 2, FALSE);
377 /* Initialize-Routing-Table */
378 if ( (bacnet_mesgtyp == BAC_NET_INIT_RTAB) ||
379 (bacnet_mesgtyp == BAC_NET_INIT_RTAB_ACK) ) {
380 bacnet_rportnum = tvb_get_guint8(tvb, offset);
381 /* number of ports */
382 proto_tree_add_uint(bacnet_tree, hf_bacnet_rportnum,
383 tvb, offset, 1, bacnet_rportnum);
385 for(i=0; i>bacnet_rportnum; i++) {
387 proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
388 tvb, offset, 2, FALSE);
391 proto_tree_add_item(bacnet_tree, hf_bacnet_portid,
392 tvb, offset, 1, FALSE);
394 /* Port Info Length */
395 bacnet_pinfolen = tvb_get_guint8(tvb, offset);
396 proto_tree_add_uint(bacnet_tree, hf_bacnet_pinfolen,
397 tvb, offset, 1, bacnet_pinfolen);
399 for(j=0; j>bacnet_pinfolen; j++){
401 proto_tree_add_item(bacnet_tree, hf_bacnet_pinfo,
402 tvb, offset, 1, FALSE);
408 proto_item_set_len(ti, offset);
411 /* dissect BACnet APDU
413 next_tvb = tvb_new_subset(tvb,offset,-1,-1);
414 if (bacnet_control & BAC_CONTROL_NET) {
415 /* Unknown function - dissect the payload as data */
416 call_dissector(data_handle, next_tvb, pinfo, tree);
418 /* APDU - call the APDU dissector */
419 call_dissector(bacapp_handle, next_tvb, pinfo, tree);
424 proto_register_bacnet(void)
426 static hf_register_info hf[] = {
427 { &hf_bacnet_version,
428 { "Version", "bacnet.version",
429 FT_UINT8, BASE_DEC, NULL, 0,
430 "BACnet Version", HFILL }
432 { &hf_bacnet_control,
433 { "Control", "bacnet.control",
434 FT_UINT8, BASE_HEX, NULL, 0xff,
435 "BACnet Control", HFILL }
437 { &hf_bacnet_control_net,
439 "bacnet.control_net",
440 FT_BOOLEAN, 8, TFS(&control_net_set_high),
441 BAC_CONTROL_NET, "BACnet Control", HFILL }
443 { &hf_bacnet_control_res1,
445 "bacnet.control_res1",
446 FT_BOOLEAN, 8, TFS(&control_res_high),
447 BAC_CONTROL_RES1, "BACnet Control", HFILL }
449 { &hf_bacnet_control_dest,
450 { "Destination Specifier",
451 "bacnet.control_dest",
452 FT_BOOLEAN, 8, TFS(&control_dest_high),
453 BAC_CONTROL_DEST, "BACnet Control", HFILL }
455 { &hf_bacnet_control_res2,
457 "bacnet.control_res2",
458 FT_BOOLEAN, 8, TFS(&control_res_high),
459 BAC_CONTROL_RES2, "BACnet Control", HFILL }
461 { &hf_bacnet_control_src,
462 { "Source specifier",
463 "bacnet.control_src",
464 FT_BOOLEAN, 8, TFS(&control_src_high),
465 BAC_CONTROL_SRC, "BACnet Control", HFILL }
467 { &hf_bacnet_control_expect,
469 "bacnet.control_expect",
470 FT_BOOLEAN, 8, TFS(&control_expect_high),
471 BAC_CONTROL_EXPECT, "BACnet Control", HFILL }
473 { &hf_bacnet_control_prio_high,
475 "bacnet.control_prio_high",
476 FT_BOOLEAN, 8, TFS(&control_prio_high_high),
477 BAC_CONTROL_PRIO_HIGH, "BACnet Control", HFILL }
479 { &hf_bacnet_control_prio_low,
481 "bacnet.control_prio_low",
482 FT_BOOLEAN, 8, TFS(&control_prio_low_high),
483 BAC_CONTROL_PRIO_LOW, "BACnet Control", HFILL }
486 { "Destination Network Address", "bacnet.dnet",
487 FT_UINT16, BASE_HEX, NULL, 0,
488 "Destination Network Address", HFILL }
491 { "Destination MAC Layer Address Length", "bacnet.dlen",
492 FT_UINT8, BASE_DEC, NULL, 0,
493 "Destination MAC Layer Address Length", HFILL }
495 { &hf_bacnet_dadr_eth,
496 { "Destination ISO 8802-3 MAC Address", "bacnet.dadr_eth",
497 FT_ETHER, BASE_HEX, NULL, 0,
498 "Destination ISO 8802-3 MAC Address", HFILL }
500 { &hf_bacnet_dadr_tmp,
501 { "Unknown Destination MAC", "bacnet.dadr_tmp",
502 FT_BYTES, BASE_HEX, NULL, 0,
503 "Unknown Destination MAC", HFILL }
506 { "Source Network Address", "bacnet.snet",
507 FT_UINT16, BASE_HEX, NULL, 0,
508 "Source Network Address", HFILL }
511 { "Source MAC Layer Address Length", "bacnet.slen",
512 FT_UINT8, BASE_DEC, NULL, 0,
513 "Source MAC Layer Address Length", HFILL }
515 { &hf_bacnet_sadr_eth,
516 { "SADR", "bacnet.sadr_eth",
517 FT_ETHER, BASE_HEX, NULL, 0,
518 "Source ISO 8802-3 MAC Address", HFILL }
520 { &hf_bacnet_sadr_tmp,
521 { "Unknown Source MAC", "bacnet.sadr_tmp",
522 FT_BYTES, BASE_HEX, NULL, 0,
523 "Unknown Source MAC", HFILL }
526 { "Hop Count", "bacnet.hopc",
527 FT_UINT8, BASE_DEC, NULL, 0,
530 { &hf_bacnet_mesgtyp,
531 { "Message Type", "bacnet.mesgtyp",
532 FT_UINT8, BASE_HEX, NULL, 0,
533 "Message Type", HFILL }
536 { "Vendor ID", "bacnet.vendor",
537 FT_UINT16, BASE_HEX, NULL, 0,
541 { "Performance Index", "bacnet.perf",
542 FT_UINT8, BASE_DEC, NULL, 0,
543 "Performance Index", HFILL }
545 { &hf_bacnet_rejectreason,
546 { "Reject Reason", "bacnet.rejectreason",
547 FT_UINT8, BASE_DEC, NULL, 0,
548 "Reject Reason", HFILL }
550 { &hf_bacnet_rportnum,
551 { "Number of Port Mappings", "bacnet.rportnum",
552 FT_UINT8, BASE_DEC, NULL, 0,
553 "Number of Port Mappings", HFILL }
555 { &hf_bacnet_pinfolen,
556 { "Port Info Length", "bacnet.pinfolen",
557 FT_UINT8, BASE_DEC, NULL, 0,
558 "Port Info Length", HFILL }
561 { "Port ID", "bacnet.portid",
562 FT_UINT8, BASE_HEX, NULL, 0,
566 { "Port Info", "bacnet.pinfo",
567 FT_UINT8, BASE_HEX, NULL, 0,
572 static gint *ett[] = {
577 proto_bacnet = proto_register_protocol("Building Automation and Control Network NPDU",
580 proto_register_field_array(proto_bacnet, hf, array_length(hf));
581 proto_register_subtree_array(ett, array_length(ett));
583 register_dissector("bacnet", dissect_bacnet, proto_bacnet);
587 proto_reg_handoff_bacnet(void)
589 dissector_handle_t bacnet_handle;
591 bacnet_handle = find_dissector("bacnet");
592 dissector_add("bvlc.function", 0x04, bacnet_handle);
593 dissector_add("bvlc.function", 0x09, bacnet_handle);
594 dissector_add("bvlc.function", 0x0a, bacnet_handle);
595 dissector_add("bvlc.function", 0x0b, bacnet_handle);
596 bacapp_handle = find_dissector("bacapp");
597 data_handle = find_dissector("data");