Support BACNET over ARCNET, under the assumption that no fragmentation
[obnox/wireshark/wip.git] / packet-bacnet.c
1 /* packet-bacnet.c
2  * Routines for BACnet (NPDU) dissection
3  * Copyright 2001, Hartmut Mueller <hartmut@abmlinux.org>, FH Dortmund
4  *
5  * $Id: packet-bacnet.c,v 1.16 2003/01/23 09:54:54 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
9  * Copyright 1998 Gerald Combs
10  *
11  * Copied from README.developer,v 1.23
12  *
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.
17  *
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.
22  *
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.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include <glib.h>
37
38 #include <epan/packet.h>
39
40 #include "llcsaps.h"
41 #include "arcnet_pids.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_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;
166
167 static gint ett_bacnet = -1;
168 static gint ett_bacnet_control = -1;
169
170 static void
171 dissect_bacnet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
172 {
173         proto_item *ti;
174         proto_item *ct;
175         proto_tree *bacnet_tree;
176         proto_tree *control_tree;
177
178         gint offset;
179         guint8 bacnet_version;
180         guint8 bacnet_control;
181         guint8 bacnet_dlen;
182         guint8 bacnet_slen;
183         guint8 bacnet_mesgtyp;
184         guint8 bacnet_rejectreason;
185         guint8 bacnet_rportnum;
186         guint8 bacnet_pinfolen;
187         guint8 i;
188         guint8 j;
189         tvbuff_t *next_tvb;
190
191         if (check_col(pinfo->cinfo, COL_PROTOCOL))
192                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "BACnet-NPDU");
193
194         if (check_col(pinfo->cinfo, COL_INFO))
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         if (tree) {
210
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);
213
214                 bacnet_tree = proto_item_add_subtree(ti, ett_bacnet);
215
216                 proto_tree_add_uint_format(bacnet_tree, hf_bacnet_version, tvb,
217                         offset, 1,
218                         bacnet_version,"Version: 0x%02x (%s)",bacnet_version,
219                         (bacnet_version == 0x01)?"ASHRAE 135-1995":"unknown");
220                 offset ++;
221                 ct = proto_tree_add_uint_format(bacnet_tree, hf_bacnet_control,
222                         tvb, offset, 1,
223                         bacnet_control,"Control: 0x%02x",bacnet_control);
224                 control_tree = proto_item_add_subtree(ct,
225                         ett_bacnet_control);
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);
242                 offset ++;
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);
246                         offset += 2;
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",
254                                 bacnet_dlen);
255                                 offset ++;
256                                 /* going to SNET */
257                         } else if (bacnet_dlen==6) {
258                                 proto_tree_add_uint(bacnet_tree, hf_bacnet_dlen,
259                                         tvb, offset, 1, bacnet_dlen);
260                                 offset ++;
261                                 /* Ethernet MAC */
262                                 proto_tree_add_item(bacnet_tree,
263                                         hf_bacnet_dadr_eth, tvb, offset,
264                                         bacnet_dlen, FALSE);
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);
269                                 offset ++;
270                                 /* Other MAC formats should be included here */
271                                 proto_tree_add_item(bacnet_tree,
272                                         hf_bacnet_dadr_tmp, tvb, offset,
273                                         bacnet_dlen, FALSE);
274                                 offset += bacnet_dlen;
275                         } else {
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!",
279                                 bacnet_dlen);
280                         }
281                 }
282                 if (bacnet_control & BAC_CONTROL_SRC) { /* SNET, SLEN, SADR */
283                         /* SNET */
284                         proto_tree_add_uint(bacnet_tree, hf_bacnet_snet,
285                                 tvb, offset, 2, tvb_get_ntohs(tvb, offset));
286                         offset += 2;
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!",
292                                 bacnet_slen);
293                                 offset ++;
294                         } else if (bacnet_slen==6) {
295                                 /* SLEN */
296                                  proto_tree_add_uint(bacnet_tree, hf_bacnet_slen,
297                                         tvb, offset, 1, bacnet_slen);
298                                 offset ++;
299                                 /* Ethernet MAC */
300                                 proto_tree_add_item(bacnet_tree,
301                                         hf_bacnet_sadr_eth, tvb, offset,
302                                         bacnet_slen, FALSE);
303                                 offset += bacnet_slen;
304                         } else if (bacnet_slen<6) { /* LON,ARCNET,MS/TP MAC */
305                                 /* SLEN */
306                                  proto_tree_add_uint(bacnet_tree, hf_bacnet_slen,
307                                         tvb, offset, 1, bacnet_slen);
308                                 offset ++;
309                                 /* Other MAC formats should be included here */
310                                 proto_tree_add_item(bacnet_tree,
311                                         hf_bacnet_sadr_tmp, tvb, offset,
312                                         bacnet_slen, FALSE);
313                                 offset += bacnet_slen;
314                         } else {
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!",
318                                 bacnet_slen);
319                                 offset ++;
320                         }
321                 }
322                 if (bacnet_control & BAC_CONTROL_DEST) { /* Hopcount */
323                         proto_tree_add_item(bacnet_tree, hf_bacnet_hopc,
324                                 tvb, offset, 1, FALSE);
325                         offset ++;
326                 }
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));
334                         offset ++;
335                 }
336                 /* Vendor ID
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...
342                  */
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);
346                         offset += 2;
347                         /* attention: doesnt work here because of if(tree) */
348                         call_dissector(data_handle,
349                             tvb_new_subset(tvb, offset, -1, -1), pinfo, tree);
350                 }
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);
355                         offset ++;
356                 }
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,
362                                 tvb, offset, 1,
363                                 bacnet_rejectreason, "Rejection Reason: %d (%s)",
364                                 bacnet_rejectreason,
365                                 bacnet_rejectreason_name(bacnet_rejectreason));
366                         offset ++;
367                         proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
368                                 tvb, offset, 2, FALSE);
369                         offset += 2;
370                 }
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);
377                         offset += 2;
378                     }
379                 }
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);
387                     offset ++;
388                     for(i=0; i>bacnet_rportnum; i++) {
389                         /* Connected DNET */
390                         proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
391                                 tvb, offset, 2, FALSE);
392                         offset += 2;
393                         /* Port ID */
394                         proto_tree_add_item(bacnet_tree, hf_bacnet_portid,
395                                 tvb, offset, 1, FALSE);
396                         offset ++;
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);
401                         offset ++;
402                         for(j=0; j>bacnet_pinfolen; j++){
403                             /* Port Info */
404                             proto_tree_add_item(bacnet_tree, hf_bacnet_pinfo,
405                                 tvb, offset, 1, FALSE);
406                             offset ++;
407                         }
408                     }
409
410                 }
411                 proto_item_set_len(ti, offset);
412         }
413
414 /* dissect BACnet APDU
415  */
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);
420         } else {
421                 /* APDU - call the APDU dissector */
422                 call_dissector(bacapp_handle, next_tvb, pinfo, tree);
423         }
424 }
425
426 void
427 proto_register_bacnet(void)
428 {
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 }
434                 },
435                 { &hf_bacnet_control,
436                         { "Control",           "bacnet.control",
437                         FT_UINT8, BASE_HEX, NULL, 0xff,
438                         "BACnet Control", HFILL }
439                 },
440                 { &hf_bacnet_control_net,
441                         { "NSDU contains",
442                         "bacnet.control_net",
443                         FT_BOOLEAN, 8, TFS(&control_net_set_high),
444                         BAC_CONTROL_NET, "BACnet Control", HFILL }
445                 },
446                 { &hf_bacnet_control_res1,
447                         { "Reserved",
448                         "bacnet.control_res1",
449                         FT_BOOLEAN, 8, TFS(&control_res_high),
450                         BAC_CONTROL_RES1, "BACnet Control", HFILL }
451                 },
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 }
457                 },
458                 { &hf_bacnet_control_res2,
459                         { "Reserved",
460                         "bacnet.control_res2",
461                         FT_BOOLEAN, 8, TFS(&control_res_high),
462                         BAC_CONTROL_RES2, "BACnet Control", HFILL }
463                 },
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 }
469                 },
470                 { &hf_bacnet_control_expect,
471                         { "Expecting Reply",
472                         "bacnet.control_expect",
473                         FT_BOOLEAN, 8, TFS(&control_expect_high),
474                         BAC_CONTROL_EXPECT, "BACnet Control", HFILL }
475                 },
476                 { &hf_bacnet_control_prio_high,
477                         { "Priority",
478                         "bacnet.control_prio_high",
479                         FT_BOOLEAN, 8, TFS(&control_prio_high_high),
480                         BAC_CONTROL_PRIO_HIGH, "BACnet Control", HFILL }
481                 },
482                 { &hf_bacnet_control_prio_low,
483                         { "Priority",
484                         "bacnet.control_prio_low",
485                         FT_BOOLEAN, 8, TFS(&control_prio_low_high),
486                         BAC_CONTROL_PRIO_LOW, "BACnet Control", HFILL }
487                 },
488                 { &hf_bacnet_dnet,
489                         { "Destination Network Address", "bacnet.dnet",
490                         FT_UINT16, BASE_HEX, NULL, 0,
491                         "Destination Network Address", HFILL }
492                 },
493                 { &hf_bacnet_dlen,
494                         { "Destination MAC Layer Address Length", "bacnet.dlen",
495                         FT_UINT8, BASE_DEC, NULL, 0,
496                         "Destination MAC Layer Address Length", HFILL }
497                 },
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 }
502                 },
503                 { &hf_bacnet_dadr_tmp,
504                         { "Unknown Destination MAC", "bacnet.dadr_tmp",
505                         FT_BYTES, BASE_HEX, NULL, 0,
506                         "Unknown Destination MAC", HFILL }
507                 },
508                 { &hf_bacnet_snet,
509                         { "Source Network Address", "bacnet.snet",
510                         FT_UINT16, BASE_HEX, NULL, 0,
511                         "Source Network Address", HFILL }
512                 },
513                 { &hf_bacnet_slen,
514                         { "Source MAC Layer Address Length", "bacnet.slen",
515                         FT_UINT8, BASE_DEC, NULL, 0,
516                         "Source MAC Layer Address Length", HFILL }
517                 },
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 }
522                 },
523                 { &hf_bacnet_sadr_tmp,
524                         { "Unknown Source MAC", "bacnet.sadr_tmp",
525                         FT_BYTES, BASE_HEX, NULL, 0,
526                         "Unknown Source MAC", HFILL }
527                 },
528                 { &hf_bacnet_hopc,
529                         { "Hop Count", "bacnet.hopc",
530                         FT_UINT8, BASE_DEC, NULL, 0,
531                         "Hop Count", HFILL }
532                 },
533                 { &hf_bacnet_mesgtyp,
534                         { "Message Type", "bacnet.mesgtyp",
535                         FT_UINT8, BASE_HEX, NULL, 0,
536                         "Message Type", HFILL }
537                 },
538                 { &hf_bacnet_vendor,
539                         { "Vendor ID", "bacnet.vendor",
540                         FT_UINT16, BASE_HEX, NULL, 0,
541                         "Vendor ID", HFILL }
542                 },
543                 { &hf_bacnet_perf,
544                         { "Performance Index", "bacnet.perf",
545                         FT_UINT8, BASE_DEC, NULL, 0,
546                         "Performance Index", HFILL }
547                 },
548                 { &hf_bacnet_rejectreason,
549                         { "Reject Reason", "bacnet.rejectreason",
550                         FT_UINT8, BASE_DEC, NULL, 0,
551                         "Reject Reason", HFILL }
552                 },
553                 { &hf_bacnet_rportnum,
554                         { "Number of Port Mappings", "bacnet.rportnum",
555                         FT_UINT8, BASE_DEC, NULL, 0,
556                         "Number of Port Mappings", HFILL }
557                 },
558                 { &hf_bacnet_pinfolen,
559                         { "Port Info Length", "bacnet.pinfolen",
560                         FT_UINT8, BASE_DEC, NULL, 0,
561                         "Port Info Length", HFILL }
562                 },
563                 { &hf_bacnet_portid,
564                         { "Port ID", "bacnet.portid",
565                         FT_UINT8, BASE_HEX, NULL, 0,
566                         "Port ID", HFILL }
567                 },
568                 { &hf_bacnet_pinfo,
569                         { "Port Info", "bacnet.pinfo",
570                         FT_UINT8, BASE_HEX, NULL, 0,
571                         "Port Info", HFILL }
572                 },
573         };
574
575         static gint *ett[] = {
576                 &ett_bacnet,
577                 &ett_bacnet_control,
578         };
579
580         proto_bacnet = proto_register_protocol("Building Automation and Control Network NPDU",
581             "BACnet", "bacnet");
582
583         proto_register_field_array(proto_bacnet, hf, array_length(hf));
584         proto_register_subtree_array(ett, array_length(ett));
585
586         register_dissector("bacnet", dissect_bacnet, proto_bacnet);
587 }
588
589 void
590 proto_reg_handoff_bacnet(void)
591 {
592         dissector_handle_t bacnet_handle;
593
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");
603 }