2 * Routines for BACnet/IP (BVLL, BVLC) dissection
3 * Copyright 2001, Hartmut Mueller <hartmut@abmlinux.org>, FH Dortmund
5 * $Id: packet-bvlc.c,v 1.2 2001/04/20 20:34:28 guy Exp $
7 * Ethereal - Network traffic analyzer
8 * By Gerald Combs <gerald@ethereal.com>
9 * Copyright 1998 Gerald Combs
11 * Copied from README.developer,v 1.23
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
38 #ifdef HAVE_SYS_TYPES_H
39 # include <sys/types.h>
42 #ifdef HAVE_NETINET_IN_H
43 # include <netinet/in.h>
48 #ifdef NEED_SNPRINTF_H
49 # include "snprintf.h"
54 static int proto_bvlc = -1;
55 static int hf_bvlc_type = -1;
56 static int hf_bvlc_function = -1;
57 static int hf_bvlc_length = -1;
58 static int hf_bvlc_result = -1;
59 static int hf_bvlc_bdt_ip = -1;
60 static int hf_bvlc_bdt_mask = -1;
61 static int hf_bvlc_bdt_port = -1;
62 static int hf_bvlc_reg_ttl = -1;
63 static int hf_bvlc_fdt_ip = -1;
64 static int hf_bvlc_fdt_port = -1;
65 static int hf_bvlc_fdt_ttl = -1;
66 static int hf_bvlc_fdt_timeout = -1;
67 static int hf_bvlc_fwd_ip = -1;
68 static int hf_bvlc_fwd_port = -1;
72 static dissector_table_t bvlc_dissector_table;
75 bvlc_function_name (guint8 bvlc_function){
76 static const char *type_names[] = {
78 "Write-Broadcast-Distribution-Table",
79 "Read-Broadcast-Distribution-Table",
80 "Read-Broadcast-Distribution-Table-Ack",
82 "Register-Foreign-Device",
83 "Read-Foreign-Device-Table",
84 "Read-Foreign-Device-Table-Ack",
85 "Delete-Foreign-Device-Table-Entry",
86 "Distribute-Broadcast-To-Network",
87 "Original-Unicast-NPDU",
88 "Original-Broadcast-NPDU"
90 return (bvlc_function > 0xb)? "unknown" : type_names[bvlc_function];
94 bvlc_result_name (guint16 bvlc_result){
95 static const char *result_names[] = {
96 "Successful completion",
97 "Write-Broadcast-Distribution-Table NAK",
98 "Read-Broadcast-Distribution-Table NAK",
99 "Register-Foreign-Device NAK",
100 "Read-Foreign-Device-Table NAK",
101 "Delete-Foreign-Device-Table-Entry NAK",
102 "Distribute-Broadcast-To-Network NAK"
104 return (bvlc_result > 0x0060)? "unknown" : result_names[bvlc_result];
107 static gint ett_bvlc = -1;
108 static gint ett_bdt = -1;
109 static gint ett_fdt = -1;
112 dissect_bvlc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
118 proto_tree *bvlc_tree;
119 proto_tree *bdt_tree; /* Broadcast Distribution Table */
120 proto_tree *fdt_tree; /* Foreign Device Table */
124 guint8 bvlc_function;
126 guint16 packet_length;
131 pinfo->current_proto = "BACnet virtual link control";
133 if (check_col(pinfo->fd, COL_PROTOCOL))
134 col_set_str(pinfo->fd, COL_PROTOCOL, "BVLC");
136 if (check_col(pinfo->fd, COL_INFO))
137 col_set_str(pinfo->fd, COL_INFO, "BACnet Virtual Link Control");
141 bvlc_type = tvb_get_guint8(tvb, offset);
142 bvlc_function = tvb_get_guint8(tvb, offset+1);
143 packet_length = tvb_get_ntohs(tvb, offset+2);
144 if (bvlc_function > 0x08) {
145 /* We have a constant header length of BVLC of 4 in every
146 * BVLC-packet forewarding an NPDU. Beware: Changes in the
147 * BACnet-IP-standard may break this.
148 * At the moment, no functions above 0x0b
149 * exist (Addendum 135a to ANSI/ASHRAE 135-1995 - BACnet)
152 } else if(bvlc_function == 0x04) {
153 /* 4 Bytes + 6 Bytes for B/IP Address of Originating Device */
156 /* BVLC-packets with function below 0x09 contain
157 * routing-level data (e.g. Broadcast Distribution)
158 * but no NPDU for BACnet, so bvlc_length goes up to the end
159 * of the captured frame.
161 bvlc_length = packet_length;
165 ti = proto_tree_add_item(tree, proto_bvlc, tvb, 0,
167 bvlc_tree = proto_item_add_subtree(ti, ett_bvlc);
168 proto_tree_add_uint_format(bvlc_tree, hf_bvlc_type, tvb, offset, 1,
169 bvlc_type,"Type: 0x%x (Version %s)",bvlc_type,
170 (bvlc_type == 0x81)?"BACnet/IP (Annex J)":"unknown");
172 proto_tree_add_uint_format(bvlc_tree, hf_bvlc_function, tvb,
173 offset, 1, bvlc_function,"Function: 0x%02x (%s)",
174 bvlc_function, bvlc_function_name(bvlc_function));
176 proto_tree_add_uint_format(bvlc_tree, hf_bvlc_length, tvb, offset,
177 2, bvlc_length, "BVLC-Length: %d of %d bytes BACnet packet length",
178 bvlc_length, packet_length);
180 switch (bvlc_function) {
181 case 0x00: /* BVLC-Result */
182 bvlc_result = tvb_get_ntohs(tvb, offset);
183 /* I dont know why the result code is encoded in 4 nibbles,
184 * but only using one: 0x00r0. Shifting left 4 bits.
186 /* We should bitmask the result correctly when we have a
187 * packet to dissect, see README.developer, 1.6.2, FID */
188 proto_tree_add_uint_format(bvlc_tree, hf_bvlc_result, tvb,
189 offset, 2, bvlc_result,"Result: 0x%04x (%s)",
190 bvlc_result, bvlc_result_name(bvlc_result << 4));
193 case 0x01: /* Write-Broadcast-Distribution-Table */
194 case 0x03: /* Read-Broadcast-Distribution-Table-Ack */
195 /* List of BDT Entries: N*10-octet */
196 ti_bdt = proto_tree_add_item(bvlc_tree, proto_bvlc, tvb,
197 offset, bvlc_length-4, FALSE);
198 bdt_tree = proto_item_add_subtree(ti_bdt, ett_bdt);
199 /* List of BDT Entries: N*10-octet */
200 while ((bvlc_length - offset) > 9) {
201 proto_tree_add_item(bdt_tree, hf_bvlc_bdt_ip,
202 tvb, offset, 4, FALSE);
204 proto_tree_add_item(bdt_tree, hf_bvlc_bdt_port,
205 tvb, offset, 2, FALSE);
207 proto_tree_add_item(bdt_tree,
208 hf_bvlc_bdt_mask, tvb, offset, 4,
212 /* We check this if we get a BDT-packet somewhere */
214 case 0x02: /* Read-Broadcast-Distribution-Table */
215 /* nothing to do here */
217 case 0x05: /* Register-Foreign-Device */
218 /* Time-to-Live 2-octets T, Time-to-Live T, in seconds */
219 proto_tree_add_item(bvlc_tree, hf_bvlc_reg_ttl,
220 tvb, offset, 2, FALSE);
223 case 0x06: /* Read-Foreign-Device-Table */
224 /* nothing to do here */
226 case 0x07: /* Read-Foreign-Device-Table-Ack */
227 /* List of FDT Entries: N*10-octet */
228 /* N indicates the number of entries in the FDT whose
229 * contents are being returned. Each returned entry
230 * consists of the 6-octet B/IP address of the registrant;
231 * the 2-octet Time-to-Live value supplied at the time of
232 * registration; and a 2-octet value representing the
233 * number of seconds remaining before the BBMD will purge
234 * the registrant's FDT entry if no re-registration occurs.
236 ti_fdt = proto_tree_add_item(bvlc_tree, proto_bvlc, tvb,
237 offset, bvlc_length -4, FALSE);
238 fdt_tree = proto_item_add_subtree(ti_fdt, ett_fdt);
239 /* List of FDT Entries: N*10-octet */
240 while ((bvlc_length - offset) > 9) {
241 proto_tree_add_item(fdt_tree, hf_bvlc_fdt_ip,
242 tvb, offset, 4, FALSE);
244 proto_tree_add_item(fdt_tree, hf_bvlc_fdt_port,
245 tvb, offset, 2, FALSE);
247 proto_tree_add_item(fdt_tree,
248 hf_bvlc_fdt_ttl, tvb, offset, 2,
251 proto_tree_add_item(fdt_tree,
252 hf_bvlc_fdt_timeout, tvb, offset, 2,
256 /* We check this if we get a FDT-packet somewhere */
258 case 0x08: /* Delete-Foreign-Device-Table-Entry */
259 /* FDT Entry: 6-octets */
260 proto_tree_add_item(bvlc_tree, hf_bvlc_fdt_ip,
261 tvb, offset, 4, FALSE);
263 proto_tree_add_item(bvlc_tree, hf_bvlc_fdt_port,
264 tvb, offset, 2, FALSE);
267 /* We check this if we get a FDT-packet somewhere */
268 case 0x04: /* Forwarded-NPDU
269 * Why is this 0x04? It would have been a better
270 * idea to append all forewarded NPDUs at the
271 * end of the function table in the B/IP-standard!
273 /* proto_tree_add_bytes_format(); */
274 proto_tree_add_item(bvlc_tree, hf_bvlc_fwd_ip,
275 tvb, offset, 4, FALSE);
277 proto_tree_add_item(bvlc_tree, hf_bvlc_fwd_port,
278 tvb, offset, 2, FALSE);
280 default:/* Distribute-Broadcast-To-Network
281 * Original-Unicast-NPDU
282 * Original-Broadcast-NPDU
283 * Going to the next dissector...
289 /* Ok, no routing information BVLC packet. Dissect as
292 npdu_length = packet_length - bvlc_length;
293 next_tvb = tvb_new_subset(tvb,bvlc_length,-1,npdu_length);
294 /* Code from Guy Harris */
295 if (!dissector_try_port(bvlc_dissector_table,
296 bvlc_function, next_tvb, pinfo, tree)) {
297 /* Unknown function - dissect the paylod as data */
298 dissect_data(next_tvb, 0, pinfo, tree);
303 proto_register_bvlc(void)
305 static hf_register_info hf[] = {
307 { "Type", "bvlc.type",
308 FT_UINT8, BASE_HEX, NULL, 0,
312 { "Function", "bvlc.function",
313 FT_UINT8, BASE_HEX, NULL, 0,
317 { "Length", "bvlc.length",
318 FT_UINT16, BASE_DEC, NULL, 0,
321 /* We should bitmask the result correctly when we have a
322 * packet to dissect */
324 { "Result", "bvlc.result",
325 FT_UINT16, BASE_HEX, NULL, 0xffff,
329 { "IP", "bvlc.bdt_ip",
330 FT_IPv4, BASE_NONE, NULL, 0,
334 { "Port", "bvlc.bdt_port",
335 FT_UINT16, BASE_DEC, NULL, 0,
339 { "Mask", "bvlc.bdt_mask",
340 FT_BYTES, BASE_HEX, NULL, 0,
341 "BDT Broadcast Distribution Mask" }
344 { "TTL", "bvlc.reg_ttl",
345 FT_UINT16, BASE_DEC, NULL, 0,
346 "Foreign Device Time To Live" }
349 { "IP", "bvlc.fdt_ip",
350 FT_IPv4, BASE_NONE, NULL, 0,
354 { "Port", "bvlc.fdt_port",
355 FT_UINT16, BASE_DEC, NULL, 0,
359 { "TTL", "bvlc.fdt_ttl",
360 FT_UINT16, BASE_DEC, NULL, 0,
361 "Foreign Device Time To Live" }
363 { &hf_bvlc_fdt_timeout,
364 { "Timeout", "bvlc.fdt_timeout",
365 FT_UINT16, BASE_DEC, NULL, 0,
366 "Foreign Device Timeout (seconds)" }
369 { "IP", "bvlc.fwd_ip",
370 FT_IPv4, BASE_NONE, NULL, 0,
374 { "Port", "bvlc.fwd_port",
375 FT_UINT16, BASE_DEC, NULL, 0,
380 static gint *ett[] = {
386 proto_bvlc = proto_register_protocol("BACnet Virtual Link Control",
389 proto_register_field_array(proto_bvlc, hf, array_length(hf));
390 proto_register_subtree_array(ett, array_length(ett));
392 register_dissector("bvlc", dissect_bvlc, proto_bvlc);
394 bvlc_dissector_table = register_dissector_table("bvlc.function");
398 proto_reg_handoff_bvlc(void)
400 dissector_add("udp.port", 0xBAC0, dissect_bvlc, proto_bvlc); /* added proto_bvlc */
402 /* Taken from add-135a (BACnet-IP-standard paper):
404 * The default UDP port for both directed messages and broadcasts shall
405 * be X'BAC0' and all B/IP devices shall support it. In some cases,
406 * e.g., a situation where it is desirable for two groups of BACnet devices
407 * to coexist independently on the same IP subnet, the UDP port may be
408 * configured locally to a different value without it being considered
409 * a violation of this protocol.
411 * This dissector does not analyse UDP packets other than on port 0xBAC0.
412 * If you changed your BACnet port locally, use the ethereal feature