c0f6c1a56ac2649bc9f6b3f687205325de48f06c
[obnox/wireshark/wip.git] / epan / dissectors / packet-bacnet.c
1 /* packet-bacnet.c
2  * Routines for BACnet (NPDU) dissection
3  * Copyright 2001, Hartmut Mueller <hartmut@abmlinux.org>, FH Dortmund
4  * Enhanced by Steve Karg, 2005, <skarg@users.sourceforge.net>
5  *
6  * $Id$
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * Copied from README.developer,v 1.23
13  *
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include <glib.h>
38
39 #include <epan/packet.h>
40
41 #include <epan/llcsaps.h>
42
43 static dissector_handle_t bacapp_handle;
44 static dissector_handle_t data_handle;
45
46 static const char*
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"
59         };
60   if(bacnet_mesgtyp < 0x0a) {
61         return type_names[bacnet_mesgtyp];
62   } else {
63         return (bacnet_mesgtyp < 0x80)? "Reserved for Use by ASHRAE" : "Vendor Proprietary Message";
64   }
65 }
66
67 static const char*
68 bacnet_rejectreason_name (guint8 bacnet_rejectreason) {
69   static const char *type_names[] = {
70         "Other error.",
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."
77         };
78    return (bacnet_rejectreason > 6)? "Invalid Rejection Reason.":  type_names[bacnet_rejectreason];
79 }
80
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
90
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
102
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."
106 };
107
108 static const true_false_string control_res_high = {
109         "Shall be zero, but is one.",
110         "Shall be zero and is zero."
111 };
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."
115 };
116
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"
120 };
121
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."
125 };
126
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."
130 };
131
132 static const true_false_string control_prio_low_high = {
133         "Urgent message",
134         "Normal message"
135 };
136
137
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_mstp = -1;
153 static int hf_bacnet_dadr_tmp = -1;
154 static int hf_bacnet_snet = -1;
155 static int hf_bacnet_slen = -1;
156 static int hf_bacnet_sadr_eth = -1;
157 static int hf_bacnet_sadr_mstp = -1;
158 static int hf_bacnet_sadr_tmp = -1;
159 static int hf_bacnet_hopc = -1;
160 static int hf_bacnet_mesgtyp = -1;
161 static int hf_bacnet_vendor = -1;
162 static int hf_bacnet_perf = -1;
163 static int hf_bacnet_rejectreason = -1;
164 static int hf_bacnet_rportnum = -1;
165 static int hf_bacnet_portid = -1;
166 static int hf_bacnet_pinfolen = -1;
167 static int hf_bacnet_term_time_value = -1;
168
169 static gint ett_bacnet = -1;
170 static gint ett_bacnet_control = -1;
171
172 static void
173 dissect_bacnet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
174 {
175         proto_item *ti;
176         proto_item *ct;
177         proto_tree *bacnet_tree;
178         proto_tree *control_tree;
179
180         gint offset;
181         guint8 bacnet_version;
182         guint8 bacnet_control;
183         guint8 bacnet_dlen;
184         guint8 bacnet_slen;
185         guint8 bacnet_mesgtyp;
186         guint8 bacnet_rejectreason;
187         guint8 bacnet_rportnum;
188         guint8 bacnet_pinfolen;
189         guint8 i;
190         guint8 j;
191         tvbuff_t *next_tvb;
192
193         col_set_str(pinfo->cinfo, COL_PROTOCOL, "BACnet-NPDU");
194
195         col_set_str(pinfo->cinfo, COL_INFO, "Building Automation and Control Network NPDU");
196
197         offset = 0;
198         bacnet_version = tvb_get_guint8(tvb, offset);
199         bacnet_control = tvb_get_guint8(tvb, offset+1);
200         bacnet_dlen = 0;
201         bacnet_slen = 0;
202         bacnet_mesgtyp = 0;
203         bacnet_rejectreason = 0;
204         bacnet_rportnum = 0;
205         bacnet_pinfolen =0;
206         i = 0;
207         j = 0;
208
209 /* I don't know the length of the NPDU by now. Setting the length after dissection */
210         ti = proto_tree_add_item(tree, proto_bacnet, tvb, 0, -1, FALSE);
211
212         bacnet_tree = proto_item_add_subtree(ti, ett_bacnet);
213
214         proto_tree_add_uint_format_value(bacnet_tree, hf_bacnet_version, tvb,
215                 offset, 1,
216                        bacnet_version,"0x%02x (%s)",bacnet_version,
217                        (bacnet_version == 0x01)?"ASHRAE 135-1995":"unknown");
218         offset ++;
219         ct = proto_tree_add_uint(bacnet_tree, hf_bacnet_control,
220                 tvb, offset, 1, bacnet_control);
221         control_tree = proto_item_add_subtree(ct, ett_bacnet_control);
222         proto_tree_add_boolean(control_tree, hf_bacnet_control_net,
223                 tvb, offset, 1, bacnet_control);
224         proto_tree_add_boolean(control_tree, hf_bacnet_control_res1, tvb,
225                 offset, 1, bacnet_control);
226         proto_tree_add_boolean(control_tree, hf_bacnet_control_dest, tvb,
227                 offset, 1, bacnet_control);
228         proto_tree_add_boolean(control_tree, hf_bacnet_control_res2, tvb,
229                 offset, 1, bacnet_control);
230         proto_tree_add_boolean(control_tree, hf_bacnet_control_src, tvb,
231                 offset, 1, bacnet_control);
232         proto_tree_add_boolean(control_tree, hf_bacnet_control_expect, tvb,
233                 offset, 1, bacnet_control);
234         proto_tree_add_boolean(control_tree, hf_bacnet_control_prio_high,
235                 tvb, offset, 1, bacnet_control);
236         proto_tree_add_boolean(control_tree, hf_bacnet_control_prio_low,
237                 tvb, offset, 1, bacnet_control);
238         offset ++;
239         if (bacnet_control & BAC_CONTROL_DEST) { /* DNET, DLEN, DADR */
240                 proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
241                         tvb, offset, 2, FALSE);
242                 offset += 2;
243                 bacnet_dlen = tvb_get_guint8(tvb, offset);
244                 /* DLEN = 0 is broadcast on dest.network */
245                 if( bacnet_dlen == 0) {
246                         /* append to hf_bacnet_dlen: broadcast */
247                         proto_tree_add_uint_format_value(bacnet_tree,
248                             hf_bacnet_dlen, tvb, offset, 1, bacnet_dlen,
249                             "%d indicates Broadcast on Destination Network",
250                             bacnet_dlen);
251                         offset ++;
252                         /* going to SNET */
253                 } else if (bacnet_dlen==6) {
254                         proto_tree_add_uint(bacnet_tree, hf_bacnet_dlen,
255                                 tvb, offset, 1, bacnet_dlen);
256                         offset ++;
257                         /* Ethernet MAC */
258                         proto_tree_add_item(bacnet_tree,
259                                 hf_bacnet_dadr_eth, tvb, offset,
260                                 bacnet_dlen, FALSE);
261                         offset += bacnet_dlen;
262                 } else if (bacnet_dlen==1) {
263                         proto_tree_add_uint(bacnet_tree, hf_bacnet_dlen,
264                                 tvb, offset, 1, bacnet_dlen);
265                         offset ++;
266                         /* MS/TP or ARCNET MAC */
267                         proto_tree_add_item(bacnet_tree,
268                                 hf_bacnet_dadr_mstp, tvb, offset,
269                                 bacnet_dlen, FALSE);
270                         offset += bacnet_dlen;
271                 } else if (bacnet_dlen<7) {
272                         proto_tree_add_uint(bacnet_tree, hf_bacnet_dlen,
273                                 tvb, offset, 1, bacnet_dlen);
274                         offset ++;
275                         /* Other MAC formats should be included here */
276                         proto_tree_add_item(bacnet_tree,
277                                 hf_bacnet_dadr_tmp, tvb, offset,
278                                 bacnet_dlen, FALSE);
279                         offset += bacnet_dlen;
280                 } else {
281                         proto_tree_add_uint_format_value(bacnet_tree,
282                             hf_bacnet_dlen, tvb, offset, 1, bacnet_dlen,
283                             "%d invalid!",
284                             bacnet_dlen);
285                 }
286         }
287         if (bacnet_control & BAC_CONTROL_SRC) { /* SNET, SLEN, SADR */
288                 /* SNET */
289                 proto_tree_add_uint(bacnet_tree, hf_bacnet_snet,
290                         tvb, offset, 2, tvb_get_ntohs(tvb, offset));
291                 offset += 2;
292                 bacnet_slen = tvb_get_guint8(tvb, offset);
293                 if( bacnet_slen == 0) { /* SLEN = 0 invalid */
294                         proto_tree_add_uint_format_value(bacnet_tree,
295                             hf_bacnet_slen, tvb, offset, 1, bacnet_slen,
296                             "%d invalid!",
297                             bacnet_slen);
298                         offset ++;
299                 } else if (bacnet_slen==6) {
300                         /* SLEN */
301                          proto_tree_add_uint(bacnet_tree, hf_bacnet_slen,
302                                 tvb, offset, 1, bacnet_slen);
303                         offset ++;
304                         /* Ethernet MAC */
305                         proto_tree_add_item(bacnet_tree,
306                                 hf_bacnet_sadr_eth, tvb, offset,
307                                 bacnet_slen, FALSE);
308                         offset += bacnet_slen;
309                 } else if (bacnet_slen==1) {
310                         /* SLEN */
311                          proto_tree_add_uint(bacnet_tree, hf_bacnet_slen,
312                                 tvb, offset, 1, bacnet_slen);
313                         offset ++;
314                         /* MS/TP or ARCNET MAC */
315                         proto_tree_add_item(bacnet_tree,
316                                 hf_bacnet_sadr_mstp, tvb, offset,
317                                 bacnet_slen, FALSE);
318                         offset += bacnet_slen;
319                 } else if (bacnet_slen<6) { /* LON MAC */
320                         /* SLEN */
321                          proto_tree_add_uint(bacnet_tree, hf_bacnet_slen,
322                                 tvb, offset, 1, bacnet_slen);
323                         offset ++;
324                         /* Other MAC formats should be included here */
325                         proto_tree_add_item(bacnet_tree,
326                                 hf_bacnet_sadr_tmp, tvb, offset,
327                                 bacnet_slen, FALSE);
328                         offset += bacnet_slen;
329                 } else {
330                         proto_tree_add_uint_format_value(bacnet_tree,
331                         hf_bacnet_slen, tvb, offset, 1, bacnet_slen,
332                             "%d invalid!",
333                             bacnet_slen);
334                         offset ++;
335                 }
336         }
337         if (bacnet_control & BAC_CONTROL_DEST) { /* Hopcount */
338                 proto_tree_add_item(bacnet_tree, hf_bacnet_hopc,
339                         tvb, offset, 1, FALSE);
340                 offset ++;
341         }
342         /* Network Layer Message Type */
343         if (bacnet_control & BAC_CONTROL_NET) {
344                 bacnet_mesgtyp =  tvb_get_guint8(tvb, offset);
345                 proto_tree_add_uint_format_value(bacnet_tree,
346                 hf_bacnet_mesgtyp, tvb, offset, 1, bacnet_mesgtyp,
347                         "%02x (%s)", bacnet_mesgtyp,
348                         bacnet_mesgtyp_name(bacnet_mesgtyp));
349                 /* Put the NPDU Type in the info column */
350                 if (check_col(pinfo->cinfo, COL_INFO))
351                 {
352                         col_clear(pinfo->cinfo, COL_INFO);
353                         col_add_str(pinfo->cinfo, COL_INFO,
354                                 bacnet_mesgtyp_name(bacnet_mesgtyp));
355                 }
356                 offset ++;
357                 /* Vendor ID
358                 * The standard says: "If Bit 7 of the control octet is 1 and
359                 * the Message Type field contains a value in the range
360                 * X'80' - X'FF', then a Vendor ID field shall be present (...)."
361                 * We should not go any further in dissecting the packet if it's
362                 * not present, but we don't know about that: No length field...
363                 */
364                 if (bacnet_mesgtyp > 0x7f) {
365                         proto_tree_add_item(bacnet_tree, hf_bacnet_vendor,
366                                 tvb, offset, 2, FALSE);
367                         offset += 2;
368                         /* attention: doesnt work here because of if(tree) */
369                         call_dissector(data_handle,
370                                 tvb_new_subset(tvb, offset, -1, -1), pinfo, tree);
371                 }
372                 /* Performance Index (in I-Could-Be-Router-To-Network) */
373                 if (bacnet_mesgtyp == BAC_NET_ICB_R) {
374                         proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
375                                 tvb, offset, 2, FALSE);
376                         offset += 2;
377                         proto_tree_add_item(bacnet_tree, hf_bacnet_perf,
378                                 tvb, offset, 1, FALSE);
379                         offset ++;
380                 }
381                 /* Reason, DNET (in Reject-Message-To-Network) */
382                 if (bacnet_mesgtyp == BAC_NET_REJ) {
383                         bacnet_rejectreason = tvb_get_guint8(tvb, offset);
384                         proto_tree_add_uint_format_value(bacnet_tree,
385                                 hf_bacnet_rejectreason,
386                                 tvb, offset, 1,
387                                 bacnet_rejectreason, "%d (%s)",
388                                 bacnet_rejectreason,
389                                 bacnet_rejectreason_name(bacnet_rejectreason));
390                         offset ++;
391                         proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
392                                 tvb, offset, 2, FALSE);
393                         offset += 2;
394                 }
395                 /* N*DNET (in Router-Busy-To-Network,Router-Available-To-Network) */
396                 if ((bacnet_mesgtyp == BAC_NET_R_BUSY) ||
397                         (bacnet_mesgtyp == BAC_NET_WHO_R) ||
398                         (bacnet_mesgtyp == BAC_NET_R_AVA) ||
399                         (bacnet_mesgtyp == BAC_NET_IAM_R) ) {
400                         while(tvb_reported_length_remaining(tvb, offset) > 1 ) {
401                                 proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
402                                         tvb, offset, 2, FALSE);
403                                 offset += 2;
404                         }
405                 }
406                 /* Initialize-Routing-Table */
407                 if ( (bacnet_mesgtyp == BAC_NET_INIT_RTAB) ||
408                         (bacnet_mesgtyp == BAC_NET_INIT_RTAB_ACK) ) {
409                         bacnet_rportnum = tvb_get_guint8(tvb, offset);
410                         /* number of ports */
411                         proto_tree_add_uint(bacnet_tree, hf_bacnet_rportnum,
412                                 tvb, offset, 1, bacnet_rportnum);
413                         offset ++;
414                         for(i=0; i<bacnet_rportnum; i++) {
415                                         /* Connected DNET */
416                                         proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
417                                         tvb, offset, 2, FALSE);
418                                         offset += 2;
419                                         /* Port ID */
420                                         proto_tree_add_item(bacnet_tree, hf_bacnet_portid,
421                                         tvb, offset, 1, FALSE);
422                                 offset ++;
423                                         /* Port Info Length */
424                                 bacnet_pinfolen = tvb_get_guint8(tvb, offset);
425                                         proto_tree_add_uint(bacnet_tree, hf_bacnet_pinfolen,
426                                         tvb, offset, 1, bacnet_pinfolen);
427                                 offset ++;
428                                 proto_tree_add_text(bacnet_tree, tvb, offset,
429                                         bacnet_pinfolen, "Port Info: %s",
430                                         tvb_bytes_to_str(tvb, offset, bacnet_pinfolen));
431                                 offset += bacnet_pinfolen;
432                         }
433                 }
434                 /* Establish-Connection-To-Network */
435                 if (bacnet_mesgtyp == BAC_NET_EST_CON) {
436                         proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
437                                 tvb, offset, 2, FALSE);
438                         offset += 2;
439                         proto_tree_add_item(bacnet_tree, hf_bacnet_term_time_value,
440                                 tvb, offset, 1, FALSE);
441                         offset ++;
442                 }
443                 /* Disconnect-Connection-To-Network */
444                 if (bacnet_mesgtyp == BAC_NET_DISC_CON) {
445                         proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
446                                 tvb, offset, 2, FALSE);
447                         offset += 2;
448                 }
449                 proto_item_set_len(ti, offset);
450         }
451         /* dissect BACnet APDU */
452         next_tvb = tvb_new_subset(tvb,offset,-1,-1);
453         if (bacnet_control & BAC_CONTROL_NET) {
454                 /* Unknown function - dissect the payload as data */
455                 call_dissector(data_handle, next_tvb, pinfo, tree);
456         } else {
457                 /* APDU - call the APDU dissector */
458                 call_dissector(bacapp_handle, next_tvb, pinfo, tree);
459         }
460 }
461
462 void
463 proto_register_bacnet(void)
464 {
465         static hf_register_info hf[] = {
466                 { &hf_bacnet_version,
467                         { "Version",           "bacnet.version",
468                         FT_UINT8, BASE_DEC, NULL, 0,
469                         "BACnet Version", HFILL }
470                 },
471                 { &hf_bacnet_control,
472                         { "Control",           "bacnet.control",
473                         FT_UINT8, BASE_HEX, NULL, 0,
474                         "BACnet Control", HFILL }
475                 },
476                 { &hf_bacnet_control_net,
477                         { "NSDU contains",
478                         "bacnet.control_net",
479                         FT_BOOLEAN, 8, TFS(&control_net_set_high),
480                         BAC_CONTROL_NET, "BACnet Control", HFILL }
481                 },
482                 { &hf_bacnet_control_res1,
483                         { "Reserved",
484                         "bacnet.control_res1",
485                         FT_BOOLEAN, 8, TFS(&control_res_high),
486                         BAC_CONTROL_RES1, "BACnet Control", HFILL }
487                 },
488                 { &hf_bacnet_control_dest,
489                         { "Destination Specifier",
490                         "bacnet.control_dest",
491                         FT_BOOLEAN, 8, TFS(&control_dest_high),
492                         BAC_CONTROL_DEST, "BACnet Control", HFILL }
493                 },
494                 { &hf_bacnet_control_res2,
495                         { "Reserved",
496                         "bacnet.control_res2",
497                         FT_BOOLEAN, 8, TFS(&control_res_high),
498                         BAC_CONTROL_RES2, "BACnet Control", HFILL }
499                 },
500                 { &hf_bacnet_control_src,
501                         { "Source specifier",
502                         "bacnet.control_src",
503                         FT_BOOLEAN, 8, TFS(&control_src_high),
504                         BAC_CONTROL_SRC, "BACnet Control", HFILL }
505                 },
506                 { &hf_bacnet_control_expect,
507                         { "Expecting Reply",
508                         "bacnet.control_expect",
509                         FT_BOOLEAN, 8, TFS(&control_expect_high),
510                         BAC_CONTROL_EXPECT, "BACnet Control", HFILL }
511                 },
512                 { &hf_bacnet_control_prio_high,
513                         { "Priority",
514                         "bacnet.control_prio_high",
515                         FT_BOOLEAN, 8, TFS(&control_prio_high_high),
516                         BAC_CONTROL_PRIO_HIGH, "BACnet Control", HFILL }
517                 },
518                 { &hf_bacnet_control_prio_low,
519                         { "Priority",
520                         "bacnet.control_prio_low",
521                         FT_BOOLEAN, 8, TFS(&control_prio_low_high),
522                         BAC_CONTROL_PRIO_LOW, "BACnet Control", HFILL }
523                 },
524                 { &hf_bacnet_dnet,
525                         { "Destination Network Address", "bacnet.dnet",
526                         FT_UINT16, BASE_DEC, NULL, 0,
527                         NULL, HFILL }
528                 },
529                 { &hf_bacnet_dlen,
530                         { "Destination MAC Layer Address Length", "bacnet.dlen",
531                         FT_UINT8, BASE_DEC, NULL, 0,
532                         NULL, HFILL }
533                 },
534                 { &hf_bacnet_dadr_eth,
535                         { "Destination ISO 8802-3 MAC Address", "bacnet.dadr_eth",
536                         FT_ETHER, BASE_NONE, NULL, 0,
537                         NULL, HFILL }
538                 },
539                 { &hf_bacnet_dadr_mstp,
540                         { "DADR", "bacnet.dadr_mstp",
541                         FT_UINT8, BASE_DEC, NULL, 0,
542                         "Destination MS/TP or ARCNET MAC Address", HFILL }
543                 },
544                 { &hf_bacnet_dadr_tmp,
545                         { "Unknown Destination MAC", "bacnet.dadr_tmp",
546                         FT_BYTES, BASE_NONE, NULL, 0,
547                         NULL, HFILL }
548                 },
549                 { &hf_bacnet_snet,
550                         { "Source Network Address", "bacnet.snet",
551                         FT_UINT16, BASE_DEC, NULL, 0,
552                         NULL, HFILL }
553                 },
554                 { &hf_bacnet_slen,
555                         { "Source MAC Layer Address Length", "bacnet.slen",
556                         FT_UINT8, BASE_DEC, NULL, 0,
557                         NULL, HFILL }
558                 },
559                 { &hf_bacnet_sadr_eth,
560                         { "SADR", "bacnet.sadr_eth",
561                         FT_ETHER, BASE_NONE, NULL, 0,
562                         "Source ISO 8802-3 MAC Address", HFILL }
563                 },
564                 { &hf_bacnet_sadr_mstp,
565                         { "SADR", "bacnet.sadr_mstp",
566                         FT_UINT8, BASE_DEC, NULL, 0,
567                         "Source MS/TP or ARCNET MAC Address", HFILL }
568                 },
569                 { &hf_bacnet_sadr_tmp,
570                         { "Unknown Source MAC", "bacnet.sadr_tmp",
571                         FT_BYTES, BASE_NONE, NULL, 0,
572                         NULL, HFILL }
573                 },
574                 { &hf_bacnet_hopc,
575                         { "Hop Count", "bacnet.hopc",
576                         FT_UINT8, BASE_DEC, NULL, 0,
577                         NULL, HFILL }
578                 },
579                 { &hf_bacnet_mesgtyp,
580                         { "Network Layer Message Type", "bacnet.mesgtyp",
581                         FT_UINT8, BASE_HEX, NULL, 0,
582                         NULL, HFILL }
583                 },
584                 { &hf_bacnet_vendor,
585                         { "Vendor ID", "bacnet.vendor",
586                         FT_UINT16, BASE_DEC, NULL, 0,
587                         NULL, HFILL }
588                 },
589                 { &hf_bacnet_perf,
590                         { "Performance Index", "bacnet.perf",
591                         FT_UINT8, BASE_DEC, NULL, 0,
592                         NULL, HFILL }
593                 },
594                 { &hf_bacnet_rejectreason,
595                         { "Reject Reason", "bacnet.rejectreason",
596                         FT_UINT8, BASE_DEC, NULL, 0,
597                         NULL, HFILL }
598                 },
599                 { &hf_bacnet_rportnum,
600                         { "Number of Port Mappings", "bacnet.rportnum",
601                         FT_UINT8, BASE_DEC, NULL, 0,
602                         NULL, HFILL }
603                 },
604                 { &hf_bacnet_pinfolen,
605                         { "Port Info Length", "bacnet.pinfolen",
606                         FT_UINT8, BASE_DEC, NULL, 0,
607                         NULL, HFILL }
608                 },
609                 { &hf_bacnet_portid,
610                         { "Port ID", "bacnet.portid",
611                         FT_UINT8, BASE_HEX, NULL, 0,
612                         NULL, HFILL }
613                 },
614                 { &hf_bacnet_term_time_value,
615                         { "Termination Time Value (seconds)", "bacnet.term_time_value",
616                         FT_UINT8, BASE_DEC, NULL, 0,
617                         "Termination Time Value", HFILL }
618                 }
619         };
620
621         static gint *ett[] = {
622                 &ett_bacnet,
623                 &ett_bacnet_control,
624         };
625
626         proto_bacnet = proto_register_protocol("Building Automation and Control Network NPDU",
627             "BACnet", "bacnet");
628
629         proto_register_field_array(proto_bacnet, hf, array_length(hf));
630         proto_register_subtree_array(ett, array_length(ett));
631
632         register_dissector("bacnet", dissect_bacnet, proto_bacnet);
633 }
634
635 void
636 proto_reg_handoff_bacnet(void)
637 {
638         dissector_handle_t bacnet_handle;
639
640         bacnet_handle = find_dissector("bacnet");
641         dissector_add("bvlc.function", 0x04, bacnet_handle);
642         dissector_add("bvlc.function", 0x09, bacnet_handle);
643         dissector_add("bvlc.function", 0x0a, bacnet_handle);
644         dissector_add("bvlc.function", 0x0b, bacnet_handle);
645         dissector_add("llc.dsap", SAP_BACNET, bacnet_handle);
646         bacapp_handle = find_dissector("bacapp");
647         data_handle = find_dissector("data");
648 }