Add some additional sanity checking.
[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.17 2003/01/25 00:06:12 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
42 static dissector_handle_t bacapp_handle;
43 static dissector_handle_t data_handle;
44
45 static const char*
46 bacnet_mesgtyp_name (guint8 bacnet_mesgtyp){
47   static const char *type_names[] = {
48         "Who-Is-Router-To-Network",
49         "I-Am-Router-To-Network",
50         "I-Could-Be-Router-To-Network",
51         "Reject-Message-To-Network",
52         "Router-Busy-To-Network",
53         "Router-Available-To-Network",
54         "Initialize-Routing-Table",
55         "Initialize-Routing-Table-Ack",
56         "Establish-Connection-To-Network",
57         "Disconnect-Connection-To-Network"
58         };
59   if(bacnet_mesgtyp < 0x0a) {
60         return type_names[bacnet_mesgtyp];
61   } else {
62         return (bacnet_mesgtyp < 0x80)? "Reserved for Use by ASHRAE" : "Vendor Proprietary Message";
63   }
64 }
65
66 static const char*
67 bacnet_rejectreason_name (guint8 bacnet_rejectreason) {
68   static const char *type_names[] = {
69         "Other error.",
70         "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.",
71         "The router is busy and unable to accept messages for the specified DNET at the present time.",
72         "It is an unknown network layer message type.",
73         "The message is too long to be routed to this DNET.",
74         "The router is no longer directly connected to DNET but can reconnect if requested.",
75         "The router is no longer directly connected to DNET and cannot reconnect even if requested."
76         };
77    return (bacnet_rejectreason > 6)? "Invalid Rejection Reason.":  type_names[bacnet_rejectreason];
78 }
79
80 /* Network Layer Control Information */
81 #define BAC_CONTROL_NET          0x80
82 #define BAC_CONTROL_RES1         0x40
83 #define BAC_CONTROL_DEST         0x20
84 #define BAC_CONTROL_RES2         0x10
85 #define BAC_CONTROL_SRC          0x08
86 #define BAC_CONTROL_EXPECT       0x04
87 #define BAC_CONTROL_PRIO_HIGH    0x02
88 #define BAC_CONTROL_PRIO_LOW     0x01
89
90 /* Network Layer Message Types */
91 #define BAC_NET_WHO_R           0x00
92 #define BAC_NET_IAM_R           0x01
93 #define BAC_NET_ICB_R           0x02
94 #define BAC_NET_REJ             0x03
95 #define BAC_NET_R_BUSY          0x04
96 #define BAC_NET_R_AVA           0x05
97 #define BAC_NET_INIT_RTAB       0x06
98 #define BAC_NET_INIT_RTAB_ACK   0x07
99 #define BAC_NET_EST_CON         0x08
100 #define BAC_NET_DISC_CON        0x09
101
102 static const true_false_string control_net_set_high = {
103         "network layer message, message type field present.",
104         "BACnet APDU, message type field absent."
105 };
106
107 static const true_false_string control_res_high = {
108         "Shall be zero, but is one.",
109         "Shall be zero and is zero."
110 };
111 static const true_false_string control_dest_high = {
112         "DNET, DLEN and Hop Count present. If DLEN=0: broadcast, dest. address field absent.",
113         "DNET, DLEN, DADR and Hop Count absent."
114 };
115
116 static const true_false_string control_src_high = {
117         "SNET, SLEN and SADR present, SLEN=0 invalid, SLEN specifies length of SADR",
118         "SNET, SLEN and SADR absent"
119 };
120
121 static const true_false_string control_expect_high = {
122         "BACnet-Confirmed-Request-PDU, a segment of BACnet-ComplexACK-PDU or Network Message expecting a reply present.",
123         "Other than a BACnet-Confirmed-Request-PDU, segment of BACnet-ComplexACK-PDU or network layer message expecting a reply present."
124 };
125
126 static const true_false_string control_prio_high_high = {
127         "Life Safety or Critical Equipment message.",
128         "Not a Life Safety or Critical Equipment message."
129 };
130
131 static const true_false_string control_prio_low_high = {
132         "Urgent message",
133         "Normal message"
134 };
135
136
137 static int proto_bacnet = -1;
138 static int hf_bacnet_version = -1;
139 static int hf_bacnet_control = -1;
140 static int hf_bacnet_control_net = -1;
141 static int hf_bacnet_control_res1 = -1;
142 static int hf_bacnet_control_dest = -1;
143 static int hf_bacnet_control_res2 = -1;
144 static int hf_bacnet_control_src = -1;
145 static int hf_bacnet_control_expect = -1;
146 static int hf_bacnet_control_prio_high = -1;
147 static int hf_bacnet_control_prio_low = -1;
148 static int hf_bacnet_dnet = -1;
149 static int hf_bacnet_dlen = -1;
150 static int hf_bacnet_dadr_eth = -1;
151 static int hf_bacnet_dadr_tmp = -1;
152 static int hf_bacnet_snet = -1;
153 static int hf_bacnet_slen = -1;
154 static int hf_bacnet_sadr_eth = -1;
155 static int hf_bacnet_sadr_tmp = -1;
156 static int hf_bacnet_hopc = -1;
157 static int hf_bacnet_mesgtyp = -1;
158 static int hf_bacnet_vendor = -1;
159 static int hf_bacnet_perf = -1;
160 static int hf_bacnet_rejectreason = -1;
161 static int hf_bacnet_rportnum = -1;
162 static int hf_bacnet_portid = -1;
163 static int hf_bacnet_pinfolen = -1;
164 static int hf_bacnet_pinfo = -1;
165
166 static gint ett_bacnet = -1;
167 static gint ett_bacnet_control = -1;
168
169 static void
170 dissect_bacnet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
171 {
172         proto_item *ti;
173         proto_item *ct;
174         proto_tree *bacnet_tree;
175         proto_tree *control_tree;
176
177         gint offset;
178         guint8 bacnet_version;
179         guint8 bacnet_control;
180         guint8 bacnet_dlen;
181         guint8 bacnet_slen;
182         guint8 bacnet_mesgtyp;
183         guint8 bacnet_rejectreason;
184         guint8 bacnet_rportnum;
185         guint8 bacnet_pinfolen;
186         guint8 i;
187         guint8 j;
188         tvbuff_t *next_tvb;
189
190         if (check_col(pinfo->cinfo, COL_PROTOCOL))
191                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "BACnet-NPDU");
192
193         if (check_col(pinfo->cinfo, COL_INFO))
194                 col_set_str(pinfo->cinfo, COL_INFO, "Building Automation and Control Network NPDU");
195
196         offset = 0;
197         bacnet_version = tvb_get_guint8(tvb, offset);
198         bacnet_control = tvb_get_guint8(tvb, offset+1);
199         bacnet_dlen = 0;
200         bacnet_slen = 0;
201         bacnet_mesgtyp = 0;
202         bacnet_rejectreason = 0;
203         bacnet_rportnum = 0;
204         bacnet_pinfolen =0;
205         i = 0;
206         j = 0;
207
208         if (tree) {
209
210 /* I don't know the length of the NPDU by know. Setting the length after dissection */
211                 ti = proto_tree_add_item(tree, proto_bacnet, tvb, 0, -1, FALSE);
212
213                 bacnet_tree = proto_item_add_subtree(ti, ett_bacnet);
214
215                 proto_tree_add_uint_format(bacnet_tree, hf_bacnet_version, tvb,
216                         offset, 1,
217                         bacnet_version,"Version: 0x%02x (%s)",bacnet_version,
218                         (bacnet_version == 0x01)?"ASHRAE 135-1995":"unknown");
219                 offset ++;
220                 ct = proto_tree_add_uint_format(bacnet_tree, hf_bacnet_control,
221                         tvb, offset, 1,
222                         bacnet_control,"Control: 0x%02x",bacnet_control);
223                 control_tree = proto_item_add_subtree(ct,
224                         ett_bacnet_control);
225                 proto_tree_add_boolean(control_tree, hf_bacnet_control_net,
226                         tvb, offset, 1, bacnet_control);
227                 proto_tree_add_boolean(control_tree, hf_bacnet_control_res1, tvb,
228                         offset, 1, bacnet_control);
229                 proto_tree_add_boolean(control_tree, hf_bacnet_control_dest, tvb,
230                         offset, 1, bacnet_control);
231                 proto_tree_add_boolean(control_tree, hf_bacnet_control_res2, tvb,
232                         offset, 1, bacnet_control);
233                 proto_tree_add_boolean(control_tree, hf_bacnet_control_src, tvb,
234                         offset, 1, bacnet_control);
235                 proto_tree_add_boolean(control_tree, hf_bacnet_control_expect, tvb,
236                         offset, 1, bacnet_control);
237                 proto_tree_add_boolean(control_tree, hf_bacnet_control_prio_high,
238                         tvb, offset, 1, bacnet_control);
239                 proto_tree_add_boolean(control_tree, hf_bacnet_control_prio_low,
240                         tvb, offset, 1, bacnet_control);
241                 offset ++;
242                 if (bacnet_control & BAC_CONTROL_DEST) { /* DNET, DLEN, DADR */
243                         proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
244                                 tvb, offset, 2, FALSE);
245                         offset += 2;
246                         bacnet_dlen = tvb_get_guint8(tvb, offset);
247                         /* DLEN = 0 is broadcast on dest.network */
248                         if( bacnet_dlen == 0) {
249                                 /* append to hf_bacnet_dlen: broadcast */
250                                 proto_tree_add_uint_format(bacnet_tree,
251                                 hf_bacnet_dlen, tvb, offset, 1, bacnet_dlen,
252                                 "Destination MAC Layer Address Length: %d indicates Broadcast on Destination Network",
253                                 bacnet_dlen);
254                                 offset ++;
255                                 /* going to SNET */
256                         } else if (bacnet_dlen==6) {
257                                 proto_tree_add_uint(bacnet_tree, hf_bacnet_dlen,
258                                         tvb, offset, 1, bacnet_dlen);
259                                 offset ++;
260                                 /* Ethernet MAC */
261                                 proto_tree_add_item(bacnet_tree,
262                                         hf_bacnet_dadr_eth, tvb, offset,
263                                         bacnet_dlen, FALSE);
264                                 offset += bacnet_dlen;
265                         } else if (bacnet_dlen<7) {
266                                 proto_tree_add_uint(bacnet_tree, hf_bacnet_dlen,
267                                         tvb, offset, 1, bacnet_dlen);
268                                 offset ++;
269                                 /* Other MAC formats should be included here */
270                                 proto_tree_add_item(bacnet_tree,
271                                         hf_bacnet_dadr_tmp, tvb, offset,
272                                         bacnet_dlen, FALSE);
273                                 offset += bacnet_dlen;
274                         } else {
275                                 proto_tree_add_uint_format(bacnet_tree,
276                                 hf_bacnet_dlen, tvb, offset, 1, bacnet_dlen,
277                                 "Destination MAC Layer Address Length: %d invalid!",
278                                 bacnet_dlen);
279                         }
280                 }
281                 if (bacnet_control & BAC_CONTROL_SRC) { /* SNET, SLEN, SADR */
282                         /* SNET */
283                         proto_tree_add_uint(bacnet_tree, hf_bacnet_snet,
284                                 tvb, offset, 2, tvb_get_ntohs(tvb, offset));
285                         offset += 2;
286                         bacnet_slen = tvb_get_guint8(tvb, offset);
287                         if( bacnet_slen == 0) { /* SLEN = 0 invalid */
288                                 proto_tree_add_uint_format(bacnet_tree,
289                                 hf_bacnet_slen, tvb, offset, 1, bacnet_slen,
290                                 "Source MAC Layer Address Length: %d invalid!",
291                                 bacnet_slen);
292                                 offset ++;
293                         } else if (bacnet_slen==6) {
294                                 /* SLEN */
295                                  proto_tree_add_uint(bacnet_tree, hf_bacnet_slen,
296                                         tvb, offset, 1, bacnet_slen);
297                                 offset ++;
298                                 /* Ethernet MAC */
299                                 proto_tree_add_item(bacnet_tree,
300                                         hf_bacnet_sadr_eth, tvb, offset,
301                                         bacnet_slen, FALSE);
302                                 offset += bacnet_slen;
303                         } else if (bacnet_slen<6) { /* LON,ARCNET,MS/TP MAC */
304                                 /* SLEN */
305                                  proto_tree_add_uint(bacnet_tree, hf_bacnet_slen,
306                                         tvb, offset, 1, bacnet_slen);
307                                 offset ++;
308                                 /* Other MAC formats should be included here */
309                                 proto_tree_add_item(bacnet_tree,
310                                         hf_bacnet_sadr_tmp, tvb, offset,
311                                         bacnet_slen, FALSE);
312                                 offset += bacnet_slen;
313                         } else {
314                                 proto_tree_add_uint_format(bacnet_tree,
315                                 hf_bacnet_slen, tvb, offset, 1, bacnet_slen,
316                                 "Source MAC Layer Address Length: %d invalid!",
317                                 bacnet_slen);
318                                 offset ++;
319                         }
320                 }
321                 if (bacnet_control & BAC_CONTROL_DEST) { /* Hopcount */
322                         proto_tree_add_item(bacnet_tree, hf_bacnet_hopc,
323                                 tvb, offset, 1, FALSE);
324                         offset ++;
325                 }
326                 /* Network Layer Message Type */
327                 if (bacnet_control & BAC_CONTROL_NET) {
328                         bacnet_mesgtyp =  tvb_get_guint8(tvb, offset);
329                         proto_tree_add_uint_format(bacnet_tree,
330                         hf_bacnet_mesgtyp, tvb, offset, 1, bacnet_mesgtyp,
331                         "Network Layer Message Type: %02x (%s)", bacnet_mesgtyp,
332                         bacnet_mesgtyp_name(bacnet_mesgtyp));
333                         offset ++;
334                 }
335                 /* Vendor ID
336                  * The standard says: "If Bit 7 of the control octet is 1 and
337                  * the Message Type field contains a value in the range
338                  * X'80' - X'FF', then a Vendor ID field shall be present (...)."
339                  * We should not go any further in dissecting the packet if it's
340                  * not present, but we don't know about that: No length field...
341                  */
342                 if ((bacnet_mesgtyp > 0x7f) && (bacnet_control == BAC_CONTROL_NET)) {
343                         proto_tree_add_item(bacnet_tree, hf_bacnet_vendor,
344                                 tvb, offset, 2, FALSE);
345                         offset += 2;
346                         /* attention: doesnt work here because of if(tree) */
347                         call_dissector(data_handle,
348                             tvb_new_subset(tvb, offset, -1, -1), pinfo, tree);
349                 }
350                 /* Performance Index (in I-Could-Be-Router-To-Network) */
351                 if (bacnet_mesgtyp == BAC_NET_ICB_R) {
352                         proto_tree_add_item(bacnet_tree, hf_bacnet_perf,
353                                 tvb, offset, 1, FALSE);
354                         offset ++;
355                 }
356                 /* Reason, DNET (in Reject-Message-To-Network) */
357                 if (bacnet_mesgtyp == BAC_NET_REJ) {
358                         bacnet_rejectreason = tvb_get_guint8(tvb, offset);
359                         proto_tree_add_uint_format(bacnet_tree,
360                                 hf_bacnet_rejectreason,
361                                 tvb, offset, 1,
362                                 bacnet_rejectreason, "Rejection Reason: %d (%s)",
363                                 bacnet_rejectreason,
364                                 bacnet_rejectreason_name(bacnet_rejectreason));
365                         offset ++;
366                         proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
367                                 tvb, offset, 2, FALSE);
368                         offset += 2;
369                 }
370                 /* N*DNET (in Router-Busy-To-Network,Router-Available-To-Network) */
371                 if ((bacnet_mesgtyp == BAC_NET_R_BUSY) ||
372                 (bacnet_mesgtyp == BAC_NET_R_AVA) || (bacnet_mesgtyp == BAC_NET_IAM_R) ) {
373                     while(tvb_reported_length_remaining(tvb, offset) > 1 ) {
374                         proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
375                                 tvb, offset, 2, FALSE);
376                         offset += 2;
377                     }
378                 }
379                 /* Initialize-Routing-Table */
380                 if ( (bacnet_mesgtyp == BAC_NET_INIT_RTAB) ||
381                     (bacnet_mesgtyp == BAC_NET_INIT_RTAB_ACK) ) {
382                     bacnet_rportnum = tvb_get_guint8(tvb, offset);
383                     /* number of ports */
384                     proto_tree_add_uint(bacnet_tree, hf_bacnet_rportnum,
385                         tvb, offset, 1, bacnet_rportnum);
386                     offset ++;
387                     for(i=0; i>bacnet_rportnum; i++) {
388                         /* Connected DNET */
389                         proto_tree_add_item(bacnet_tree, hf_bacnet_dnet,
390                                 tvb, offset, 2, FALSE);
391                         offset += 2;
392                         /* Port ID */
393                         proto_tree_add_item(bacnet_tree, hf_bacnet_portid,
394                                 tvb, offset, 1, FALSE);
395                         offset ++;
396                         /* Port Info Length */
397                         bacnet_pinfolen = tvb_get_guint8(tvb, offset);
398                         proto_tree_add_uint(bacnet_tree, hf_bacnet_pinfolen,
399                                 tvb, offset, 1, bacnet_pinfolen);
400                         offset ++;
401                         for(j=0; j>bacnet_pinfolen; j++){
402                             /* Port Info */
403                             proto_tree_add_item(bacnet_tree, hf_bacnet_pinfo,
404                                 tvb, offset, 1, FALSE);
405                             offset ++;
406                         }
407                     }
408
409                 }
410                 proto_item_set_len(ti, offset);
411         }
412
413 /* dissect BACnet APDU
414  */
415         next_tvb = tvb_new_subset(tvb,offset,-1,-1);
416         if (bacnet_control & BAC_CONTROL_NET) {
417                 /* Unknown function - dissect the payload as data */
418                 call_dissector(data_handle, next_tvb, pinfo, tree);
419         } else {
420                 /* APDU - call the APDU dissector */
421                 call_dissector(bacapp_handle, next_tvb, pinfo, tree);
422         }
423 }
424
425 void
426 proto_register_bacnet(void)
427 {
428         static hf_register_info hf[] = {
429                 { &hf_bacnet_version,
430                         { "Version",           "bacnet.version",
431                         FT_UINT8, BASE_DEC, NULL, 0,
432                         "BACnet Version", HFILL }
433                 },
434                 { &hf_bacnet_control,
435                         { "Control",           "bacnet.control",
436                         FT_UINT8, BASE_HEX, NULL, 0xff,
437                         "BACnet Control", HFILL }
438                 },
439                 { &hf_bacnet_control_net,
440                         { "NSDU contains",
441                         "bacnet.control_net",
442                         FT_BOOLEAN, 8, TFS(&control_net_set_high),
443                         BAC_CONTROL_NET, "BACnet Control", HFILL }
444                 },
445                 { &hf_bacnet_control_res1,
446                         { "Reserved",
447                         "bacnet.control_res1",
448                         FT_BOOLEAN, 8, TFS(&control_res_high),
449                         BAC_CONTROL_RES1, "BACnet Control", HFILL }
450                 },
451                 { &hf_bacnet_control_dest,
452                         { "Destination Specifier",
453                         "bacnet.control_dest",
454                         FT_BOOLEAN, 8, TFS(&control_dest_high),
455                         BAC_CONTROL_DEST, "BACnet Control", HFILL }
456                 },
457                 { &hf_bacnet_control_res2,
458                         { "Reserved",
459                         "bacnet.control_res2",
460                         FT_BOOLEAN, 8, TFS(&control_res_high),
461                         BAC_CONTROL_RES2, "BACnet Control", HFILL }
462                 },
463                 { &hf_bacnet_control_src,
464                         { "Source specifier",
465                         "bacnet.control_src",
466                         FT_BOOLEAN, 8, TFS(&control_src_high),
467                         BAC_CONTROL_SRC, "BACnet Control", HFILL }
468                 },
469                 { &hf_bacnet_control_expect,
470                         { "Expecting Reply",
471                         "bacnet.control_expect",
472                         FT_BOOLEAN, 8, TFS(&control_expect_high),
473                         BAC_CONTROL_EXPECT, "BACnet Control", HFILL }
474                 },
475                 { &hf_bacnet_control_prio_high,
476                         { "Priority",
477                         "bacnet.control_prio_high",
478                         FT_BOOLEAN, 8, TFS(&control_prio_high_high),
479                         BAC_CONTROL_PRIO_HIGH, "BACnet Control", HFILL }
480                 },
481                 { &hf_bacnet_control_prio_low,
482                         { "Priority",
483                         "bacnet.control_prio_low",
484                         FT_BOOLEAN, 8, TFS(&control_prio_low_high),
485                         BAC_CONTROL_PRIO_LOW, "BACnet Control", HFILL }
486                 },
487                 { &hf_bacnet_dnet,
488                         { "Destination Network Address", "bacnet.dnet",
489                         FT_UINT16, BASE_HEX, NULL, 0,
490                         "Destination Network Address", HFILL }
491                 },
492                 { &hf_bacnet_dlen,
493                         { "Destination MAC Layer Address Length", "bacnet.dlen",
494                         FT_UINT8, BASE_DEC, NULL, 0,
495                         "Destination MAC Layer Address Length", HFILL }
496                 },
497                 { &hf_bacnet_dadr_eth,
498                         { "Destination ISO 8802-3 MAC Address", "bacnet.dadr_eth",
499                         FT_ETHER, BASE_HEX, NULL, 0,
500                         "Destination ISO 8802-3 MAC Address", HFILL }
501                 },
502                 { &hf_bacnet_dadr_tmp,
503                         { "Unknown Destination MAC", "bacnet.dadr_tmp",
504                         FT_BYTES, BASE_HEX, NULL, 0,
505                         "Unknown Destination MAC", HFILL }
506                 },
507                 { &hf_bacnet_snet,
508                         { "Source Network Address", "bacnet.snet",
509                         FT_UINT16, BASE_HEX, NULL, 0,
510                         "Source Network Address", HFILL }
511                 },
512                 { &hf_bacnet_slen,
513                         { "Source MAC Layer Address Length", "bacnet.slen",
514                         FT_UINT8, BASE_DEC, NULL, 0,
515                         "Source MAC Layer Address Length", HFILL }
516                 },
517                 { &hf_bacnet_sadr_eth,
518                         { "SADR", "bacnet.sadr_eth",
519                         FT_ETHER, BASE_HEX, NULL, 0,
520                         "Source ISO 8802-3 MAC Address", HFILL }
521                 },
522                 { &hf_bacnet_sadr_tmp,
523                         { "Unknown Source MAC", "bacnet.sadr_tmp",
524                         FT_BYTES, BASE_HEX, NULL, 0,
525                         "Unknown Source MAC", HFILL }
526                 },
527                 { &hf_bacnet_hopc,
528                         { "Hop Count", "bacnet.hopc",
529                         FT_UINT8, BASE_DEC, NULL, 0,
530                         "Hop Count", HFILL }
531                 },
532                 { &hf_bacnet_mesgtyp,
533                         { "Message Type", "bacnet.mesgtyp",
534                         FT_UINT8, BASE_HEX, NULL, 0,
535                         "Message Type", HFILL }
536                 },
537                 { &hf_bacnet_vendor,
538                         { "Vendor ID", "bacnet.vendor",
539                         FT_UINT16, BASE_HEX, NULL, 0,
540                         "Vendor ID", HFILL }
541                 },
542                 { &hf_bacnet_perf,
543                         { "Performance Index", "bacnet.perf",
544                         FT_UINT8, BASE_DEC, NULL, 0,
545                         "Performance Index", HFILL }
546                 },
547                 { &hf_bacnet_rejectreason,
548                         { "Reject Reason", "bacnet.rejectreason",
549                         FT_UINT8, BASE_DEC, NULL, 0,
550                         "Reject Reason", HFILL }
551                 },
552                 { &hf_bacnet_rportnum,
553                         { "Number of Port Mappings", "bacnet.rportnum",
554                         FT_UINT8, BASE_DEC, NULL, 0,
555                         "Number of Port Mappings", HFILL }
556                 },
557                 { &hf_bacnet_pinfolen,
558                         { "Port Info Length", "bacnet.pinfolen",
559                         FT_UINT8, BASE_DEC, NULL, 0,
560                         "Port Info Length", HFILL }
561                 },
562                 { &hf_bacnet_portid,
563                         { "Port ID", "bacnet.portid",
564                         FT_UINT8, BASE_HEX, NULL, 0,
565                         "Port ID", HFILL }
566                 },
567                 { &hf_bacnet_pinfo,
568                         { "Port Info", "bacnet.pinfo",
569                         FT_UINT8, BASE_HEX, NULL, 0,
570                         "Port Info", HFILL }
571                 },
572         };
573
574         static gint *ett[] = {
575                 &ett_bacnet,
576                 &ett_bacnet_control,
577         };
578
579         proto_bacnet = proto_register_protocol("Building Automation and Control Network NPDU",
580             "BACnet", "bacnet");
581
582         proto_register_field_array(proto_bacnet, hf, array_length(hf));
583         proto_register_subtree_array(ett, array_length(ett));
584
585         register_dissector("bacnet", dissect_bacnet, proto_bacnet);
586 }
587
588 void
589 proto_reg_handoff_bacnet(void)
590 {
591         dissector_handle_t bacnet_handle;
592
593         bacnet_handle = find_dissector("bacnet");
594         dissector_add("bvlc.function", 0x04, bacnet_handle);
595         dissector_add("bvlc.function", 0x09, bacnet_handle);
596         dissector_add("bvlc.function", 0x0a, bacnet_handle);
597         dissector_add("bvlc.function", 0x0b, bacnet_handle);
598         dissector_add("llc.dsap", SAP_BACNET, bacnet_handle);
599         bacapp_handle = find_dissector("bacapp");
600         data_handle = find_dissector("data");
601 }