#include <string.h> and/or #include <stdio.h> not needed.
[obnox/wireshark/wip.git] / epan / dissectors / packet-bvlc.c
1 /* packet-bvlc.c
2  * Routines for BACnet/IP (BVLL, BVLC) dissection
3  * Copyright 2001, Hartmut Mueller <hartmut@abmlinux.org>, FH Dortmund
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
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 <epan/prefs.h>
33 #include <epan/strutil.h>
34
35 #include <glib.h>
36
37 #include <epan/packet.h>
38
39 /* Taken from add-135a (BACnet-IP-standard paper):
40  *
41  * The default UDP port for both directed messages and broadcasts shall
42  * be X'BAC0' and all B/IP devices shall support it. In some cases,
43  * e.g., a situation where it is desirable for two groups of BACnet devices
44  * to coexist independently on the same IP subnet, the UDP port may be
45  * configured locally to a different value without it being considered
46  * a violation of this protocol.
47  */
48 static guint global_additional_bvlc_udp_port = 0;
49
50 static int proto_bvlc = -1;
51 static int hf_bvlc_type = -1;
52 static int hf_bvlc_function = -1;
53 static int hf_bvlc_length = -1;
54 static int hf_bvlc_result = -1;
55 static int hf_bvlc_bdt_ip = -1;
56 static int hf_bvlc_bdt_mask = -1;
57 static int hf_bvlc_bdt_port = -1;
58 static int hf_bvlc_reg_ttl = -1;
59 static int hf_bvlc_fdt_ip = -1;
60 static int hf_bvlc_fdt_port = -1;
61 static int hf_bvlc_fdt_ttl = -1;
62 static int hf_bvlc_fdt_timeout = -1;
63 static int hf_bvlc_fwd_ip = -1;
64 static int hf_bvlc_fwd_port = -1;
65
66 static dissector_handle_t data_handle;
67
68 static dissector_table_t bvlc_dissector_table;
69
70 static const value_string bvlc_function_names[] = {
71         { 0x00, "BVLC-Result", },
72         { 0x01, "Write-Broadcast-Distribution-Table", },
73         { 0x02, "Read-Broadcast-Distribution-Table", },
74         { 0x03, "Read-Broadcast-Distribution-Table-Ack", },
75         { 0x04, "Forwarded-NPDU", },
76         { 0x05, "Register-Foreign-Device", },
77         { 0x06, "Read-Foreign-Device-Table", },
78         { 0x07, "Read-Foreign-Device-Table-Ack", },
79         { 0x08, "Delete-Foreign-Device-Table-Entry", },
80         { 0x09, "Distribute-Broadcast-To-Network", },
81         { 0x0a, "Original-Unicast-NPDU", },
82         { 0x0b, "Original-Broadcast-NPDU" },
83         { 0,    NULL }
84 };
85
86 static const value_string bvlc_result_names[] = {
87         { 0x00, "Successful completion" },
88         { 0x10, "Write-Broadcast-Distribution-Table NAK" },
89         { 0x20, "Read-Broadcast-Distribution-Table NAK" },
90         { 0x30, "Register-Foreign-Device NAK" },
91         { 0x40, "Read-Foreign-Device-Table NAK" },
92         { 0x50, "Delete-Foreign-Device-Table-Entry NAK" },
93         { 0x60, "Distribute-Broadcast-To-Network NAK" },
94         { 0,    NULL }
95 };
96
97 static gint ett_bvlc = -1;
98 static gint ett_bdt = -1;
99 static gint ett_fdt = -1;
100
101 #define BACNET_IP_ANNEX_J       0x81
102
103 static const value_string bvlc_types[] = {
104         { BACNET_IP_ANNEX_J,    "BACnet/IP (Annex J)" },
105         { 0,                    NULL }
106 };
107
108 static int
109 dissect_bvlc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
110 {
111
112         proto_item *ti;
113         proto_item *ti_bdt;
114         proto_item *ti_fdt;
115         proto_tree *bvlc_tree;
116         proto_tree *bdt_tree; /* Broadcast Distribution Table */
117         proto_tree *fdt_tree; /* Foreign Device Table */
118
119         gint offset;
120         guint8 bvlc_type;
121         guint8 bvlc_function;
122         guint16 bvlc_length;
123         guint16 packet_length;
124         guint npdu_length;
125         guint length_remaining;
126         guint16 bvlc_result;
127         tvbuff_t *next_tvb;
128
129         offset = 0;
130
131         bvlc_type =  tvb_get_guint8(tvb, offset);
132
133         /*
134          * Simple sanity check - make sure the type is one we know about.
135          */
136         if (match_strval(bvlc_type, bvlc_types) == NULL)
137                 return 0;
138
139         col_set_str(pinfo->cinfo, COL_PROTOCOL, "BVLC");
140
141         col_set_str(pinfo->cinfo, COL_INFO, "BACnet Virtual Link Control");
142
143         bvlc_function = tvb_get_guint8(tvb, offset+1);
144         packet_length = tvb_get_ntohs(tvb, offset+2);
145         length_remaining = tvb_reported_length_remaining(tvb, offset);
146         if (bvlc_function > 0x08) {
147                 /*  We have a constant header length of BVLC of 4 in every
148                  *  BVLC-packet forewarding an NPDU. Beware: Changes in the
149                  *  BACnet-IP-standard may break this.
150                  *  At the moment, no functions above 0x0b
151                  *  exist (Addendum 135a to ANSI/ASHRAE 135-1995 - BACnet)
152                  */
153                 bvlc_length = 4;
154         } else if(bvlc_function == 0x04) {
155                 /* 4 Bytes + 6 Bytes for B/IP Address of Originating Device */
156                 bvlc_length = 10;
157         } else {
158                 /*  BVLC-packets with function below 0x09 contain
159                  *  routing-level data (e.g. Broadcast Distribution)
160                  *  but no NPDU for BACnet, so bvlc_length goes up to the end
161                  *  of the captured frame.
162                  */
163                 bvlc_length = packet_length;
164         }
165
166         if (tree) {
167                 if (bvlc_length < 4) {
168                         proto_tree_add_text(tree, tvb, 2, 2,
169                                 "Bogus length: %d", bvlc_length);
170                         return tvb_reported_length(tvb);        /* XXX - reject? */
171                 }
172                 ti = proto_tree_add_item(tree, proto_bvlc, tvb, 0,
173                         bvlc_length, FALSE);
174                 bvlc_tree = proto_item_add_subtree(ti, ett_bvlc);
175                 proto_tree_add_uint(bvlc_tree, hf_bvlc_type, tvb, offset, 1,
176                         bvlc_type);
177                 offset ++;
178                 proto_tree_add_uint(bvlc_tree, hf_bvlc_function, tvb,
179                         offset, 1, bvlc_function);
180                 offset ++;
181                 if (length_remaining != packet_length)
182                         proto_tree_add_uint_format_value(bvlc_tree, hf_bvlc_length, tvb, offset,
183                                 2, bvlc_length, 
184                                 "%d of %d bytes (invalid length - expected %d bytes)",
185                                 bvlc_length, packet_length, length_remaining);
186                 else            
187                         proto_tree_add_uint_format_value(bvlc_tree, hf_bvlc_length, tvb, offset,
188                                 2, bvlc_length, "%d of %d bytes BACnet packet length",
189                                 bvlc_length, packet_length);
190                 offset += 2;
191                 switch (bvlc_function) {
192                 case 0x00: /* BVLC-Result */
193                         bvlc_result = tvb_get_ntohs(tvb, offset);
194                         /* I dont know why the result code is encoded in 4 nibbles,
195                          * but only using one: 0x00r0. Shifting left 4 bits.
196                          */
197                         /* We should bitmask the result correctly when we have a
198                         * packet to dissect, see README.developer, 1.6.2, FID */
199                         proto_tree_add_uint_format_value(bvlc_tree, hf_bvlc_result, tvb,
200                                 offset, 2, bvlc_result,"0x%04x (%s)",
201                                 bvlc_result, val_to_str(bvlc_result,
202                                         bvlc_result_names, "Unknown"));
203                         offset += 2;
204                         break;
205                 case 0x01: /* Write-Broadcast-Distribution-Table */
206                 case 0x03: /* Read-Broadcast-Distribution-Table-Ack */
207                         /* List of BDT Entries: N*10-octet */
208                         ti_bdt = proto_tree_add_item(bvlc_tree, proto_bvlc, tvb,
209                                 offset, bvlc_length-4, FALSE);
210                         bdt_tree = proto_item_add_subtree(ti_bdt, ett_bdt);
211                         /* List of BDT Entries: N*10-octet */
212                         while ((bvlc_length - offset) > 9) {
213                                 proto_tree_add_item(bdt_tree, hf_bvlc_bdt_ip,
214                                         tvb, offset, 4, FALSE);
215                                 offset += 4;
216                                 proto_tree_add_item(bdt_tree, hf_bvlc_bdt_port,
217                                         tvb, offset, 2, FALSE);
218                                 offset += 2;
219                                 proto_tree_add_item(bdt_tree,
220                                         hf_bvlc_bdt_mask, tvb, offset, 4,
221                                         FALSE);
222                                 offset += 4;
223                         }
224                         /* We check this if we get a BDT-packet somewhere */
225                         break;
226                 case 0x02: /* Read-Broadcast-Distribution-Table */
227                         /* nothing to do here */
228                         break;
229                 case 0x05: /* Register-Foreign-Device */
230                         /* Time-to-Live 2-octets T, Time-to-Live T, in seconds */
231                         proto_tree_add_item(bvlc_tree, hf_bvlc_reg_ttl,
232                                 tvb, offset, 2, FALSE);
233                         offset += 2;
234                         break;
235                 case 0x06: /* Read-Foreign-Device-Table */
236                         /* nothing to do here */
237                         break;
238                 case 0x07: /* Read-Foreign-Device-Table-Ack */
239                         /* List of FDT Entries: N*10-octet */
240                         /* N indicates the number of entries in the FDT whose
241                          * contents are being returned. Each returned entry
242                          * consists of the 6-octet B/IP address of the registrant;
243                          * the 2-octet Time-to-Live value supplied at the time of
244                          * registration; and a 2-octet value representing the
245                          * number of seconds remaining before the BBMD will purge
246                          * the registrant's FDT entry if no re-registration occurs.
247                          */
248                         ti_fdt = proto_tree_add_item(bvlc_tree, proto_bvlc, tvb,
249                                 offset, bvlc_length -4, FALSE);
250                         fdt_tree = proto_item_add_subtree(ti_fdt, ett_fdt);
251                         /* List of FDT Entries: N*10-octet */
252                         while ((bvlc_length - offset) > 9) {
253                                 proto_tree_add_item(fdt_tree, hf_bvlc_fdt_ip,
254                                         tvb, offset, 4, FALSE);
255                                 offset += 4;
256                                 proto_tree_add_item(fdt_tree, hf_bvlc_fdt_port,
257                                         tvb, offset, 2, FALSE);
258                                 offset += 2;
259                                 proto_tree_add_item(fdt_tree,
260                                         hf_bvlc_fdt_ttl, tvb, offset, 2,
261                                         FALSE);
262                                 offset += 2;
263                                 proto_tree_add_item(fdt_tree,
264                                         hf_bvlc_fdt_timeout, tvb, offset, 2,
265                                         FALSE);
266                                 offset += 2;
267                         }
268                         /* We check this if we get a FDT-packet somewhere */
269                         break;
270                 case 0x08: /* Delete-Foreign-Device-Table-Entry */
271                         /* FDT Entry:   6-octets */
272                         proto_tree_add_item(bvlc_tree, hf_bvlc_fdt_ip,
273                                 tvb, offset, 4, FALSE);
274                         offset += 4;
275                         proto_tree_add_item(bvlc_tree, hf_bvlc_fdt_port,
276                                 tvb, offset, 2, FALSE);
277                         offset += 2;
278                         break;
279                         /* We check this if we get a FDT-packet somewhere */
280                 case 0x04:      /* Forwarded-NPDU
281                                  * Why is this 0x04? It would have been a better
282                                  * idea to append all forewarded NPDUs at the
283                                  * end of the function table in the B/IP-standard!
284                                  */
285                         /* proto_tree_add_bytes_format(); */
286                         proto_tree_add_item(bvlc_tree, hf_bvlc_fwd_ip,
287                                 tvb, offset, 4, FALSE);
288                         offset += 4;
289                         proto_tree_add_item(bvlc_tree, hf_bvlc_fwd_port,
290                                 tvb, offset, 2, FALSE);
291                         offset += 2;
292                 default:/* Distribute-Broadcast-To-Network
293                          * Original-Unicast-NPDU
294                          * Original-Broadcast-NPDU
295                          * Going to the next dissector...
296                          */
297                         break;
298                 }
299
300         }
301 /* Ok, no routing information BVLC packet. Dissect as
302  * BACnet NPDU
303  */
304         npdu_length = packet_length - bvlc_length;
305         next_tvb = tvb_new_subset(tvb,bvlc_length,-1,npdu_length);
306         /* Code from Guy Harris */
307         if (!dissector_try_port(bvlc_dissector_table,
308             bvlc_function, next_tvb, pinfo, tree)) {
309                 /* Unknown function - dissect the paylod as data */
310                 call_dissector(data_handle,next_tvb, pinfo, tree);
311         }
312         return tvb_reported_length(tvb);
313 }
314
315 void proto_reg_handoff_bvlc(void);
316
317 void
318 proto_register_bvlc(void)
319 {
320         static hf_register_info hf[] = {
321                 { &hf_bvlc_type,
322                         { "Type",           "bvlc.type",
323                         FT_UINT8, BASE_HEX, VALS(bvlc_types), 0,
324                         NULL, HFILL }
325                 },
326                 { &hf_bvlc_function,
327                         { "Function",           "bvlc.function",
328                         FT_UINT8, BASE_HEX, VALS(bvlc_function_names), 0,
329                         "BVLC Function", HFILL }
330                 },
331                 { &hf_bvlc_length,
332                         { "BVLC-Length",        "bvlc.length",
333                         FT_UINT16, BASE_DEC, NULL, 0,
334                         "Length of BVLC", HFILL }
335                 },
336                 /* We should bitmask the result correctly when we have a
337                  * packet to dissect */
338                 { &hf_bvlc_result,
339                         { "Result",           "bvlc.result",
340                         FT_UINT16, BASE_HEX, NULL, 0,
341                         "Result Code", HFILL }
342                 },
343                 { &hf_bvlc_bdt_ip,
344                         { "IP",           "bvlc.bdt_ip",
345                         FT_IPv4, BASE_NONE, NULL, 0,
346                         "BDT IP", HFILL }
347                 },
348                 { &hf_bvlc_bdt_port,
349                         { "Port",           "bvlc.bdt_port",
350                         FT_UINT16, BASE_DEC, NULL, 0,
351                         "BDT Port", HFILL }
352                 },
353                 { &hf_bvlc_bdt_mask,
354                         { "Mask",           "bvlc.bdt_mask",
355                         FT_BYTES, BASE_NONE, NULL, 0,
356                         "BDT Broadcast Distribution Mask", HFILL }
357                 },
358                 { &hf_bvlc_reg_ttl,
359                         { "TTL",           "bvlc.reg_ttl",
360                         FT_UINT16, BASE_DEC, NULL, 0,
361                         "Foreign Device Time To Live", HFILL }
362                 },
363                 { &hf_bvlc_fdt_ip,
364                         { "IP",           "bvlc.fdt_ip",
365                         FT_IPv4, BASE_NONE, NULL, 0,
366                         "FDT IP", HFILL }
367                 },
368                 { &hf_bvlc_fdt_port,
369                         { "Port",           "bvlc.fdt_port",
370                         FT_UINT16, BASE_DEC, NULL, 0,
371                         "FDT Port", HFILL }
372                 },
373                 { &hf_bvlc_fdt_ttl,
374                         { "TTL",           "bvlc.fdt_ttl",
375                         FT_UINT16, BASE_DEC, NULL, 0,
376                         "Foreign Device Time To Live", HFILL }
377                 },
378                 { &hf_bvlc_fdt_timeout,
379                         { "Timeout",           "bvlc.fdt_timeout",
380                         FT_UINT16, BASE_DEC, NULL, 0,
381                         "Foreign Device Timeout (seconds)", HFILL }
382                 },
383                 { &hf_bvlc_fwd_ip,
384                         { "IP",           "bvlc.fwd_ip",
385                         FT_IPv4, BASE_NONE, NULL, 0,
386                         "FWD IP", HFILL }
387                 },
388                 { &hf_bvlc_fwd_port,
389                         { "Port",           "bvlc.fwd_port",
390                         FT_UINT16, BASE_DEC, NULL, 0,
391                         "FWD Port", HFILL }
392                 },
393         };
394
395         static gint *ett[] = {
396                 &ett_bvlc,
397                 &ett_bdt,
398                 &ett_fdt,
399         };
400
401         module_t *bvlc_module;
402
403         proto_bvlc = proto_register_protocol("BACnet Virtual Link Control",
404             "BVLC", "bvlc");
405
406         proto_register_field_array(proto_bvlc, hf, array_length(hf));
407         proto_register_subtree_array(ett, array_length(ett));
408
409         bvlc_module = prefs_register_protocol(proto_bvlc, proto_reg_handoff_bvlc);
410         prefs_register_uint_preference(bvlc_module, "additional_udp_port",
411                                         "Additional UDP port", "Set an additional UDP port, "
412                                         "besides the standard X'BAC0' (47808) port.",
413                                         10, &global_additional_bvlc_udp_port);
414
415         new_register_dissector("bvlc", dissect_bvlc, proto_bvlc);
416
417         bvlc_dissector_table = register_dissector_table("bvlc.function",
418             "BVLC Function", FT_UINT8, BASE_HEX);
419 }
420
421 void
422 proto_reg_handoff_bvlc(void)
423 {
424         static gboolean bvlc_initialized = FALSE;
425         static dissector_handle_t bvlc_handle;
426         static guint additional_bvlc_udp_port;
427         
428         if (!bvlc_initialized)
429         {
430                 bvlc_handle = find_dissector("bvlc");
431                 dissector_add("udp.port", 0xBAC0, bvlc_handle);
432                 data_handle = find_dissector("data");
433                 bvlc_initialized = TRUE;
434         }
435         else
436         {
437                 if (additional_bvlc_udp_port != 0) {
438                         dissector_delete("udp.port", additional_bvlc_udp_port, bvlc_handle);
439                 }
440         }
441
442         if (global_additional_bvlc_udp_port != 0) {
443                 dissector_add("udp.port", global_additional_bvlc_udp_port, bvlc_handle);
444         }
445         additional_bvlc_udp_port = global_additional_bvlc_udp_port;
446 }