6371058a4d536c5d5ad046269bf005262715465f
[metze/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         if (check_col(pinfo->cinfo, COL_PROTOCOL))
194                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "BACnet-NPDU");
195
196         if (check_col(pinfo->cinfo, COL_INFO))
197                 col_set_str(pinfo->cinfo, COL_INFO, "Building Automation and Control Network NPDU");
198
199         offset = 0;
200         bacnet_version = tvb_get_guint8(tvb, offset);
201         bacnet_control = tvb_get_guint8(tvb, offset+1);
202         bacnet_dlen = 0;
203         bacnet_slen = 0;
204         bacnet_mesgtyp = 0;
205         bacnet_rejectreason = 0;
206         bacnet_rportnum = 0;
207         bacnet_pinfolen =0;
208         i = 0;
209         j = 0;
210
211 /* I don't know the length of the NPDU by now. Setting the length after dissection */
212         ti = proto_tree_add_item(tree, proto_bacnet, tvb, 0, -1, FALSE);
213
214         bacnet_tree = proto_item_add_subtree(ti, ett_bacnet);
215
216         proto_tree_add_uint_format_value(bacnet_tree, hf_bacnet_version, tvb,
217                 offset, 1,
218                        bacnet_version,"0x%02x (%s)",bacnet_version,
219                        (bacnet_version == 0x01)?"ASHRAE 135-1995":"unknown");
220         offset ++;
221         ct = proto_tree_add_uint(bacnet_tree, hf_bacnet_control,
222                 tvb, offset, 1, bacnet_control);
223         control_tree = proto_item_add_subtree(ct, ett_bacnet_control);
224         proto_tree_add_boolean(control_tree, hf_bacnet_control_net,
225                 tvb, offset, 1, bacnet_control);
226         proto_tree_add_boolean(control_tree, hf_bacnet_control_res1, tvb,
227                 offset, 1, bacnet_control);
228         proto_tree_add_boolean(control_tree, hf_bacnet_control_dest, tvb,
229                 offset, 1, bacnet_control);
230         proto_tree_add_boolean(control_tree, hf_bacnet_control_res2, tvb,
231                 offset, 1, bacnet_control);
232         proto_tree_add_boolean(control_tree, hf_bacnet_control_src, tvb,
233                 offset, 1, bacnet_control);
234         proto_tree_add_boolean(control_tree, hf_bacnet_control_expect, tvb,
235                 offset, 1, bacnet_control);
236         proto_tree_add_boolean(control_tree, hf_bacnet_control_prio_high,
237                 tvb, offset, 1, bacnet_control);
238         proto_tree_add_boolean(control_tree, hf_bacnet_control_prio_low,
239                 tvb, offset, 1, bacnet_control);
240         offset ++;
241         if (bacnet_control & BAC_CONTROL_DEST) { /* DNET, DLEN, DADR */
242                 proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
243                         tvb, offset, 2, FALSE);
244                 offset += 2;
245                 bacnet_dlen = tvb_get_guint8(tvb, offset);
246                 /* DLEN = 0 is broadcast on dest.network */
247                 if( bacnet_dlen == 0) {
248                         /* append to hf_bacnet_dlen: broadcast */
249                         proto_tree_add_uint_format_value(bacnet_tree,
250                             hf_bacnet_dlen, tvb, offset, 1, bacnet_dlen,
251                             "%d indicates Broadcast on Destination Network",
252                             bacnet_dlen);
253                         offset ++;
254                         /* going to SNET */
255                 } else if (bacnet_dlen==6) {
256                         proto_tree_add_uint(bacnet_tree, hf_bacnet_dlen,
257                                 tvb, offset, 1, bacnet_dlen);
258                         offset ++;
259                         /* Ethernet MAC */
260                         proto_tree_add_item(bacnet_tree,
261                                 hf_bacnet_dadr_eth, tvb, offset,
262                                 bacnet_dlen, FALSE);
263                         offset += bacnet_dlen;
264                 } else if (bacnet_dlen==1) {
265                         proto_tree_add_uint(bacnet_tree, hf_bacnet_dlen,
266                                 tvb, offset, 1, bacnet_dlen);
267                         offset ++;
268                         /* MS/TP or ARCNET MAC */
269                         proto_tree_add_item(bacnet_tree,
270                                 hf_bacnet_dadr_mstp, tvb, offset,
271                                 bacnet_dlen, FALSE);
272                         offset += bacnet_dlen;
273                 } else if (bacnet_dlen<7) {
274                         proto_tree_add_uint(bacnet_tree, hf_bacnet_dlen,
275                                 tvb, offset, 1, bacnet_dlen);
276                         offset ++;
277                         /* Other MAC formats should be included here */
278                         proto_tree_add_item(bacnet_tree,
279                                 hf_bacnet_dadr_tmp, tvb, offset,
280                                 bacnet_dlen, FALSE);
281                         offset += bacnet_dlen;
282                 } else {
283                         proto_tree_add_uint_format_value(bacnet_tree,
284                             hf_bacnet_dlen, tvb, offset, 1, bacnet_dlen,
285                             "%d invalid!",
286                             bacnet_dlen);
287                 }
288         }
289         if (bacnet_control & BAC_CONTROL_SRC) { /* SNET, SLEN, SADR */
290                 /* SNET */
291                 proto_tree_add_uint(bacnet_tree, hf_bacnet_snet,
292                         tvb, offset, 2, tvb_get_ntohs(tvb, offset));
293                 offset += 2;
294                 bacnet_slen = tvb_get_guint8(tvb, offset);
295                 if( bacnet_slen == 0) { /* SLEN = 0 invalid */
296                         proto_tree_add_uint_format_value(bacnet_tree,
297                             hf_bacnet_slen, tvb, offset, 1, bacnet_slen,
298                             "%d invalid!",
299                             bacnet_slen);
300                         offset ++;
301                 } else if (bacnet_slen==6) {
302                         /* SLEN */
303                          proto_tree_add_uint(bacnet_tree, hf_bacnet_slen,
304                                 tvb, offset, 1, bacnet_slen);
305                         offset ++;
306                         /* Ethernet MAC */
307                         proto_tree_add_item(bacnet_tree,
308                                 hf_bacnet_sadr_eth, tvb, offset,
309                                 bacnet_slen, FALSE);
310                         offset += bacnet_slen;
311                 } else if (bacnet_slen==1) {
312                         /* SLEN */
313                          proto_tree_add_uint(bacnet_tree, hf_bacnet_slen,
314                                 tvb, offset, 1, bacnet_slen);
315                         offset ++;
316                         /* MS/TP or ARCNET MAC */
317                         proto_tree_add_item(bacnet_tree,
318                                 hf_bacnet_sadr_mstp, tvb, offset,
319                                 bacnet_slen, FALSE);
320                         offset += bacnet_slen;
321                 } else if (bacnet_slen<6) { /* LON MAC */
322                         /* SLEN */
323                          proto_tree_add_uint(bacnet_tree, hf_bacnet_slen,
324                                 tvb, offset, 1, bacnet_slen);
325                         offset ++;
326                         /* Other MAC formats should be included here */
327                         proto_tree_add_item(bacnet_tree,
328                                 hf_bacnet_sadr_tmp, tvb, offset,
329                                 bacnet_slen, FALSE);
330                         offset += bacnet_slen;
331                 } else {
332                         proto_tree_add_uint_format_value(bacnet_tree,
333                         hf_bacnet_slen, tvb, offset, 1, bacnet_slen,
334                             "%d invalid!",
335                             bacnet_slen);
336                         offset ++;
337                 }
338         }
339         if (bacnet_control & BAC_CONTROL_DEST) { /* Hopcount */
340                 proto_tree_add_item(bacnet_tree, hf_bacnet_hopc,
341                         tvb, offset, 1, FALSE);
342                 offset ++;
343         }
344         /* Network Layer Message Type */
345         if (bacnet_control & BAC_CONTROL_NET) {
346                 bacnet_mesgtyp =  tvb_get_guint8(tvb, offset);
347                 proto_tree_add_uint_format_value(bacnet_tree,
348                 hf_bacnet_mesgtyp, tvb, offset, 1, bacnet_mesgtyp,
349                         "%02x (%s)", bacnet_mesgtyp,
350                         bacnet_mesgtyp_name(bacnet_mesgtyp));
351                 /* Put the NPDU Type in the info column */
352                 if (check_col(pinfo->cinfo, COL_INFO))
353                 {
354                         col_clear(pinfo->cinfo, COL_INFO);
355                         col_add_str(pinfo->cinfo, COL_INFO,
356                                 bacnet_mesgtyp_name(bacnet_mesgtyp));
357                 }
358                 offset ++;
359                 /* Vendor ID
360                 * The standard says: "If Bit 7 of the control octet is 1 and
361                 * the Message Type field contains a value in the range
362                 * X'80' - X'FF', then a Vendor ID field shall be present (...)."
363                 * We should not go any further in dissecting the packet if it's
364                 * not present, but we don't know about that: No length field...
365                 */
366                 if (bacnet_mesgtyp > 0x7f) {
367                         proto_tree_add_item(bacnet_tree, hf_bacnet_vendor,
368                                 tvb, offset, 2, FALSE);
369                         offset += 2;
370                         /* attention: doesnt work here because of if(tree) */
371                         call_dissector(data_handle,
372                                 tvb_new_subset(tvb, offset, -1, -1), pinfo, tree);
373                 }
374                 /* Performance Index (in I-Could-Be-Router-To-Network) */
375                 if (bacnet_mesgtyp == BAC_NET_ICB_R) {
376                         proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
377                                 tvb, offset, 2, FALSE);
378                         offset += 2;
379                         proto_tree_add_item(bacnet_tree, hf_bacnet_perf,
380                                 tvb, offset, 1, FALSE);
381                         offset ++;
382                 }
383                 /* Reason, DNET (in Reject-Message-To-Network) */
384                 if (bacnet_mesgtyp == BAC_NET_REJ) {
385                         bacnet_rejectreason = tvb_get_guint8(tvb, offset);
386                         proto_tree_add_uint_format_value(bacnet_tree,
387                                 hf_bacnet_rejectreason,
388                                 tvb, offset, 1,
389                                 bacnet_rejectreason, "%d (%s)",
390                                 bacnet_rejectreason,
391                                 bacnet_rejectreason_name(bacnet_rejectreason));
392                         offset ++;
393                         proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
394                                 tvb, offset, 2, FALSE);
395                         offset += 2;
396                 }
397                 /* N*DNET (in Router-Busy-To-Network,Router-Available-To-Network) */
398                 if ((bacnet_mesgtyp == BAC_NET_R_BUSY) ||
399                         (bacnet_mesgtyp == BAC_NET_WHO_R) ||
400                         (bacnet_mesgtyp == BAC_NET_R_AVA) ||
401                         (bacnet_mesgtyp == BAC_NET_IAM_R) ) {
402                         while(tvb_reported_length_remaining(tvb, offset) > 1 ) {
403                                 proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
404                                         tvb, offset, 2, FALSE);
405                                 offset += 2;
406                         }
407                 }
408                 /* Initialize-Routing-Table */
409                 if ( (bacnet_mesgtyp == BAC_NET_INIT_RTAB) ||
410                         (bacnet_mesgtyp == BAC_NET_INIT_RTAB_ACK) ) {
411                         bacnet_rportnum = tvb_get_guint8(tvb, offset);
412                         /* number of ports */
413                         proto_tree_add_uint(bacnet_tree, hf_bacnet_rportnum,
414                                 tvb, offset, 1, bacnet_rportnum);
415                         offset ++;
416                         for(i=0; i<bacnet_rportnum; i++) {
417                                         /* Connected DNET */
418                                         proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
419                                         tvb, offset, 2, FALSE);
420                                         offset += 2;
421                                         /* Port ID */
422                                         proto_tree_add_item(bacnet_tree, hf_bacnet_portid,
423                                         tvb, offset, 1, FALSE);
424                                 offset ++;
425                                         /* Port Info Length */
426                                 bacnet_pinfolen = tvb_get_guint8(tvb, offset);
427                                         proto_tree_add_uint(bacnet_tree, hf_bacnet_pinfolen,
428                                         tvb, offset, 1, bacnet_pinfolen);
429                                 offset ++;
430                                 proto_tree_add_text(bacnet_tree, tvb, offset,
431                                         bacnet_pinfolen, "Port Info: %s",
432                                         tvb_bytes_to_str(tvb, offset, bacnet_pinfolen));
433                                 offset += bacnet_pinfolen;
434                         }
435                 }
436                 /* Establish-Connection-To-Network */
437                 if (bacnet_mesgtyp == BAC_NET_EST_CON) {
438                         proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
439                                 tvb, offset, 2, FALSE);
440                         offset += 2;
441                         proto_tree_add_item(bacnet_tree, hf_bacnet_term_time_value,
442                                 tvb, offset, 1, FALSE);
443                         offset ++;
444                 }
445                 /* Disconnect-Connection-To-Network */
446                 if (bacnet_mesgtyp == BAC_NET_DISC_CON) {
447                         proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
448                                 tvb, offset, 2, FALSE);
449                         offset += 2;
450                 }
451                 proto_item_set_len(ti, offset);
452         }
453         /* dissect BACnet APDU */
454         next_tvb = tvb_new_subset(tvb,offset,-1,-1);
455         if (bacnet_control & BAC_CONTROL_NET) {
456                 /* Unknown function - dissect the payload as data */
457                 call_dissector(data_handle, next_tvb, pinfo, tree);
458         } else {
459                 /* APDU - call the APDU dissector */
460                 call_dissector(bacapp_handle, next_tvb, pinfo, tree);
461         }
462 }
463
464 void
465 proto_register_bacnet(void)
466 {
467         static hf_register_info hf[] = {
468                 { &hf_bacnet_version,
469                         { "Version",           "bacnet.version",
470                         FT_UINT8, BASE_DEC, NULL, 0,
471                         "BACnet Version", HFILL }
472                 },
473                 { &hf_bacnet_control,
474                         { "Control",           "bacnet.control",
475                         FT_UINT8, BASE_HEX, NULL, 0,
476                         "BACnet Control", HFILL }
477                 },
478                 { &hf_bacnet_control_net,
479                         { "NSDU contains",
480                         "bacnet.control_net",
481                         FT_BOOLEAN, 8, TFS(&control_net_set_high),
482                         BAC_CONTROL_NET, "BACnet Control", HFILL }
483                 },
484                 { &hf_bacnet_control_res1,
485                         { "Reserved",
486                         "bacnet.control_res1",
487                         FT_BOOLEAN, 8, TFS(&control_res_high),
488                         BAC_CONTROL_RES1, "BACnet Control", HFILL }
489                 },
490                 { &hf_bacnet_control_dest,
491                         { "Destination Specifier",
492                         "bacnet.control_dest",
493                         FT_BOOLEAN, 8, TFS(&control_dest_high),
494                         BAC_CONTROL_DEST, "BACnet Control", HFILL }
495                 },
496                 { &hf_bacnet_control_res2,
497                         { "Reserved",
498                         "bacnet.control_res2",
499                         FT_BOOLEAN, 8, TFS(&control_res_high),
500                         BAC_CONTROL_RES2, "BACnet Control", HFILL }
501                 },
502                 { &hf_bacnet_control_src,
503                         { "Source specifier",
504                         "bacnet.control_src",
505                         FT_BOOLEAN, 8, TFS(&control_src_high),
506                         BAC_CONTROL_SRC, "BACnet Control", HFILL }
507                 },
508                 { &hf_bacnet_control_expect,
509                         { "Expecting Reply",
510                         "bacnet.control_expect",
511                         FT_BOOLEAN, 8, TFS(&control_expect_high),
512                         BAC_CONTROL_EXPECT, "BACnet Control", HFILL }
513                 },
514                 { &hf_bacnet_control_prio_high,
515                         { "Priority",
516                         "bacnet.control_prio_high",
517                         FT_BOOLEAN, 8, TFS(&control_prio_high_high),
518                         BAC_CONTROL_PRIO_HIGH, "BACnet Control", HFILL }
519                 },
520                 { &hf_bacnet_control_prio_low,
521                         { "Priority",
522                         "bacnet.control_prio_low",
523                         FT_BOOLEAN, 8, TFS(&control_prio_low_high),
524                         BAC_CONTROL_PRIO_LOW, "BACnet Control", HFILL }
525                 },
526                 { &hf_bacnet_dnet,
527                         { "Destination Network Address", "bacnet.dnet",
528                         FT_UINT16, BASE_DEC, NULL, 0,
529                         NULL, HFILL }
530                 },
531                 { &hf_bacnet_dlen,
532                         { "Destination MAC Layer Address Length", "bacnet.dlen",
533                         FT_UINT8, BASE_DEC, NULL, 0,
534                         NULL, HFILL }
535                 },
536                 { &hf_bacnet_dadr_eth,
537                         { "Destination ISO 8802-3 MAC Address", "bacnet.dadr_eth",
538                         FT_ETHER, BASE_NONE, NULL, 0,
539                         NULL, HFILL }
540                 },
541                 { &hf_bacnet_dadr_mstp,
542                         { "DADR", "bacnet.dadr_mstp",
543                         FT_UINT8, BASE_DEC, NULL, 0,
544                         "Destination MS/TP or ARCNET MAC Address", HFILL }
545                 },
546                 { &hf_bacnet_dadr_tmp,
547                         { "Unknown Destination MAC", "bacnet.dadr_tmp",
548                         FT_BYTES, BASE_NONE, NULL, 0,
549                         NULL, HFILL }
550                 },
551                 { &hf_bacnet_snet,
552                         { "Source Network Address", "bacnet.snet",
553                         FT_UINT16, BASE_DEC, NULL, 0,
554                         NULL, HFILL }
555                 },
556                 { &hf_bacnet_slen,
557                         { "Source MAC Layer Address Length", "bacnet.slen",
558                         FT_UINT8, BASE_DEC, NULL, 0,
559                         NULL, HFILL }
560                 },
561                 { &hf_bacnet_sadr_eth,
562                         { "SADR", "bacnet.sadr_eth",
563                         FT_ETHER, BASE_NONE, NULL, 0,
564                         "Source ISO 8802-3 MAC Address", HFILL }
565                 },
566                 { &hf_bacnet_sadr_mstp,
567                         { "SADR", "bacnet.sadr_mstp",
568                         FT_UINT8, BASE_DEC, NULL, 0,
569                         "Source MS/TP or ARCNET MAC Address", HFILL }
570                 },
571                 { &hf_bacnet_sadr_tmp,
572                         { "Unknown Source MAC", "bacnet.sadr_tmp",
573                         FT_BYTES, BASE_NONE, NULL, 0,
574                         NULL, HFILL }
575                 },
576                 { &hf_bacnet_hopc,
577                         { "Hop Count", "bacnet.hopc",
578                         FT_UINT8, BASE_DEC, NULL, 0,
579                         NULL, HFILL }
580                 },
581                 { &hf_bacnet_mesgtyp,
582                         { "Network Layer Message Type", "bacnet.mesgtyp",
583                         FT_UINT8, BASE_HEX, NULL, 0,
584                         NULL, HFILL }
585                 },
586                 { &hf_bacnet_vendor,
587                         { "Vendor ID", "bacnet.vendor",
588                         FT_UINT16, BASE_DEC, NULL, 0,
589                         NULL, HFILL }
590                 },
591                 { &hf_bacnet_perf,
592                         { "Performance Index", "bacnet.perf",
593                         FT_UINT8, BASE_DEC, NULL, 0,
594                         NULL, HFILL }
595                 },
596                 { &hf_bacnet_rejectreason,
597                         { "Reject Reason", "bacnet.rejectreason",
598                         FT_UINT8, BASE_DEC, NULL, 0,
599                         NULL, HFILL }
600                 },
601                 { &hf_bacnet_rportnum,
602                         { "Number of Port Mappings", "bacnet.rportnum",
603                         FT_UINT8, BASE_DEC, NULL, 0,
604                         NULL, HFILL }
605                 },
606                 { &hf_bacnet_pinfolen,
607                         { "Port Info Length", "bacnet.pinfolen",
608                         FT_UINT8, BASE_DEC, NULL, 0,
609                         NULL, HFILL }
610                 },
611                 { &hf_bacnet_portid,
612                         { "Port ID", "bacnet.portid",
613                         FT_UINT8, BASE_HEX, NULL, 0,
614                         NULL, HFILL }
615                 },
616                 { &hf_bacnet_term_time_value,
617                         { "Termination Time Value (seconds)", "bacnet.term_time_value",
618                         FT_UINT8, BASE_DEC, NULL, 0,
619                         "Termination Time Value", HFILL }
620                 }
621         };
622
623         static gint *ett[] = {
624                 &ett_bacnet,
625                 &ett_bacnet_control,
626         };
627
628         proto_bacnet = proto_register_protocol("Building Automation and Control Network NPDU",
629             "BACnet", "bacnet");
630
631         proto_register_field_array(proto_bacnet, hf, array_length(hf));
632         proto_register_subtree_array(ett, array_length(ett));
633
634         register_dissector("bacnet", dissect_bacnet, proto_bacnet);
635 }
636
637 void
638 proto_reg_handoff_bacnet(void)
639 {
640         dissector_handle_t bacnet_handle;
641
642         bacnet_handle = find_dissector("bacnet");
643         dissector_add("bvlc.function", 0x04, bacnet_handle);
644         dissector_add("bvlc.function", 0x09, bacnet_handle);
645         dissector_add("bvlc.function", 0x0a, bacnet_handle);
646         dissector_add("bvlc.function", 0x0b, bacnet_handle);
647         dissector_add("llc.dsap", SAP_BACNET, bacnet_handle);
648         bacapp_handle = find_dissector("bacapp");
649         data_handle = find_dissector("data");
650 }