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.9 2002/01/21 07:36:32 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.
36 #include <epan/strutil.h>
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"
52 #include <epan/packet.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;
70 static dissector_handle_t data_handle;
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 if (check_col(pinfo->cinfo, COL_PROTOCOL))
132 col_set_str(pinfo->cinfo, COL_PROTOCOL, "BVLC");
134 if (check_col(pinfo->cinfo, COL_INFO))
135 col_set_str(pinfo->cinfo, COL_INFO, "BACnet Virtual Link Control");
139 bvlc_type = tvb_get_guint8(tvb, offset);
140 bvlc_function = tvb_get_guint8(tvb, offset+1);
141 packet_length = tvb_get_ntohs(tvb, offset+2);
142 if (bvlc_function > 0x08) {
143 /* We have a constant header length of BVLC of 4 in every
144 * BVLC-packet forewarding an NPDU. Beware: Changes in the
145 * BACnet-IP-standard may break this.
146 * At the moment, no functions above 0x0b
147 * exist (Addendum 135a to ANSI/ASHRAE 135-1995 - BACnet)
150 } else if(bvlc_function == 0x04) {
151 /* 4 Bytes + 6 Bytes for B/IP Address of Originating Device */
154 /* BVLC-packets with function below 0x09 contain
155 * routing-level data (e.g. Broadcast Distribution)
156 * but no NPDU for BACnet, so bvlc_length goes up to the end
157 * of the captured frame.
159 bvlc_length = packet_length;
163 ti = proto_tree_add_item(tree, proto_bvlc, tvb, 0,
165 bvlc_tree = proto_item_add_subtree(ti, ett_bvlc);
166 proto_tree_add_uint_format(bvlc_tree, hf_bvlc_type, tvb, offset, 1,
167 bvlc_type,"Type: 0x%x (Version %s)",bvlc_type,
168 (bvlc_type == 0x81)?"BACnet/IP (Annex J)":"unknown");
170 proto_tree_add_uint_format(bvlc_tree, hf_bvlc_function, tvb,
171 offset, 1, bvlc_function,"Function: 0x%02x (%s)",
172 bvlc_function, bvlc_function_name(bvlc_function));
174 proto_tree_add_uint_format(bvlc_tree, hf_bvlc_length, tvb, offset,
175 2, bvlc_length, "BVLC-Length: %d of %d bytes BACnet packet length",
176 bvlc_length, packet_length);
178 switch (bvlc_function) {
179 case 0x00: /* BVLC-Result */
180 bvlc_result = tvb_get_ntohs(tvb, offset);
181 /* I dont know why the result code is encoded in 4 nibbles,
182 * but only using one: 0x00r0. Shifting left 4 bits.
184 /* We should bitmask the result correctly when we have a
185 * packet to dissect, see README.developer, 1.6.2, FID */
186 proto_tree_add_uint_format(bvlc_tree, hf_bvlc_result, tvb,
187 offset, 2, bvlc_result,"Result: 0x%04x (%s)",
188 bvlc_result, bvlc_result_name(bvlc_result << 4));
191 case 0x01: /* Write-Broadcast-Distribution-Table */
192 case 0x03: /* Read-Broadcast-Distribution-Table-Ack */
193 /* List of BDT Entries: N*10-octet */
194 ti_bdt = proto_tree_add_item(bvlc_tree, proto_bvlc, tvb,
195 offset, bvlc_length-4, FALSE);
196 bdt_tree = proto_item_add_subtree(ti_bdt, ett_bdt);
197 /* List of BDT Entries: N*10-octet */
198 while ((bvlc_length - offset) > 9) {
199 proto_tree_add_item(bdt_tree, hf_bvlc_bdt_ip,
200 tvb, offset, 4, FALSE);
202 proto_tree_add_item(bdt_tree, hf_bvlc_bdt_port,
203 tvb, offset, 2, FALSE);
205 proto_tree_add_item(bdt_tree,
206 hf_bvlc_bdt_mask, tvb, offset, 4,
210 /* We check this if we get a BDT-packet somewhere */
212 case 0x02: /* Read-Broadcast-Distribution-Table */
213 /* nothing to do here */
215 case 0x05: /* Register-Foreign-Device */
216 /* Time-to-Live 2-octets T, Time-to-Live T, in seconds */
217 proto_tree_add_item(bvlc_tree, hf_bvlc_reg_ttl,
218 tvb, offset, 2, FALSE);
221 case 0x06: /* Read-Foreign-Device-Table */
222 /* nothing to do here */
224 case 0x07: /* Read-Foreign-Device-Table-Ack */
225 /* List of FDT Entries: N*10-octet */
226 /* N indicates the number of entries in the FDT whose
227 * contents are being returned. Each returned entry
228 * consists of the 6-octet B/IP address of the registrant;
229 * the 2-octet Time-to-Live value supplied at the time of
230 * registration; and a 2-octet value representing the
231 * number of seconds remaining before the BBMD will purge
232 * the registrant's FDT entry if no re-registration occurs.
234 ti_fdt = proto_tree_add_item(bvlc_tree, proto_bvlc, tvb,
235 offset, bvlc_length -4, FALSE);
236 fdt_tree = proto_item_add_subtree(ti_fdt, ett_fdt);
237 /* List of FDT Entries: N*10-octet */
238 while ((bvlc_length - offset) > 9) {
239 proto_tree_add_item(fdt_tree, hf_bvlc_fdt_ip,
240 tvb, offset, 4, FALSE);
242 proto_tree_add_item(fdt_tree, hf_bvlc_fdt_port,
243 tvb, offset, 2, FALSE);
245 proto_tree_add_item(fdt_tree,
246 hf_bvlc_fdt_ttl, tvb, offset, 2,
249 proto_tree_add_item(fdt_tree,
250 hf_bvlc_fdt_timeout, tvb, offset, 2,
254 /* We check this if we get a FDT-packet somewhere */
256 case 0x08: /* Delete-Foreign-Device-Table-Entry */
257 /* FDT Entry: 6-octets */
258 proto_tree_add_item(bvlc_tree, hf_bvlc_fdt_ip,
259 tvb, offset, 4, FALSE);
261 proto_tree_add_item(bvlc_tree, hf_bvlc_fdt_port,
262 tvb, offset, 2, FALSE);
265 /* We check this if we get a FDT-packet somewhere */
266 case 0x04: /* Forwarded-NPDU
267 * Why is this 0x04? It would have been a better
268 * idea to append all forewarded NPDUs at the
269 * end of the function table in the B/IP-standard!
271 /* proto_tree_add_bytes_format(); */
272 proto_tree_add_item(bvlc_tree, hf_bvlc_fwd_ip,
273 tvb, offset, 4, FALSE);
275 proto_tree_add_item(bvlc_tree, hf_bvlc_fwd_port,
276 tvb, offset, 2, FALSE);
278 default:/* Distribute-Broadcast-To-Network
279 * Original-Unicast-NPDU
280 * Original-Broadcast-NPDU
281 * Going to the next dissector...
287 /* Ok, no routing information BVLC packet. Dissect as
290 npdu_length = packet_length - bvlc_length;
291 next_tvb = tvb_new_subset(tvb,bvlc_length,-1,npdu_length);
292 /* Code from Guy Harris */
293 if (!dissector_try_port(bvlc_dissector_table,
294 bvlc_function, next_tvb, pinfo, tree)) {
295 /* Unknown function - dissect the paylod as data */
296 call_dissector(data_handle,next_tvb, pinfo, tree);
301 proto_register_bvlc(void)
303 static hf_register_info hf[] = {
305 { "Type", "bvlc.type",
306 FT_UINT8, BASE_HEX, NULL, 0,
310 { "Function", "bvlc.function",
311 FT_UINT8, BASE_HEX, NULL, 0,
312 "BLVC Function", HFILL }
315 { "Length", "bvlc.length",
316 FT_UINT16, BASE_DEC, NULL, 0,
317 "Length of BVLC", HFILL }
319 /* We should bitmask the result correctly when we have a
320 * packet to dissect */
322 { "Result", "bvlc.result",
323 FT_UINT16, BASE_HEX, NULL, 0xffff,
324 "Result Code", HFILL }
327 { "IP", "bvlc.bdt_ip",
328 FT_IPv4, BASE_NONE, NULL, 0,
332 { "Port", "bvlc.bdt_port",
333 FT_UINT16, BASE_DEC, NULL, 0,
337 { "Mask", "bvlc.bdt_mask",
338 FT_BYTES, BASE_HEX, NULL, 0,
339 "BDT Broadcast Distribution Mask", HFILL }
342 { "TTL", "bvlc.reg_ttl",
343 FT_UINT16, BASE_DEC, NULL, 0,
344 "Foreign Device Time To Live", HFILL }
347 { "IP", "bvlc.fdt_ip",
348 FT_IPv4, BASE_NONE, NULL, 0,
352 { "Port", "bvlc.fdt_port",
353 FT_UINT16, BASE_DEC, NULL, 0,
357 { "TTL", "bvlc.fdt_ttl",
358 FT_UINT16, BASE_DEC, NULL, 0,
359 "Foreign Device Time To Live", HFILL }
361 { &hf_bvlc_fdt_timeout,
362 { "Timeout", "bvlc.fdt_timeout",
363 FT_UINT16, BASE_DEC, NULL, 0,
364 "Foreign Device Timeout (seconds)", HFILL }
367 { "IP", "bvlc.fwd_ip",
368 FT_IPv4, BASE_NONE, NULL, 0,
372 { "Port", "bvlc.fwd_port",
373 FT_UINT16, BASE_DEC, NULL, 0,
378 static gint *ett[] = {
384 proto_bvlc = proto_register_protocol("BACnet Virtual Link Control",
387 proto_register_field_array(proto_bvlc, hf, array_length(hf));
388 proto_register_subtree_array(ett, array_length(ett));
390 register_dissector("bvlc", dissect_bvlc, proto_bvlc);
392 bvlc_dissector_table = register_dissector_table("bvlc.function",
393 "BVLC Function", FT_UINT8, BASE_HEX);
397 proto_reg_handoff_bvlc(void)
399 dissector_handle_t bvlc_handle;
401 bvlc_handle = find_dissector("bvlc");
402 dissector_add("udp.port", 0xBAC0, bvlc_handle);
403 data_handle = find_dissector("data");
405 /* Taken from add-135a (BACnet-IP-standard paper):
407 * The default UDP port for both directed messages and broadcasts shall
408 * be X'BAC0' and all B/IP devices shall support it. In some cases,
409 * e.g., a situation where it is desirable for two groups of BACnet devices
410 * to coexist independently on the same IP subnet, the UDP port may be
411 * configured locally to a different value without it being considered
412 * a violation of this protocol.
414 * This dissector does not analyse UDP packets other than on port 0xBAC0.
415 * If you changed your BACnet port locally, use the ethereal feature