Do the standard "next entry offset" stuff in NT NOTIFY replies, so as to
[obnox/wireshark/wip.git] / 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: packet-bvlc.c,v 1.9 2002/01/21 07:36:32 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 #include "prefs.h"
36 #include <epan/strutil.h>
37
38 #ifdef HAVE_SYS_TYPES_H
39 # include <sys/types.h>
40 #endif
41
42 #ifdef HAVE_NETINET_IN_H
43 # include <netinet/in.h>
44 #endif
45
46 #include <glib.h>
47
48 #ifdef NEED_SNPRINTF_H
49 # include "snprintf.h"
50 #endif
51
52 #include <epan/packet.h>
53
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;
69
70 static dissector_handle_t data_handle;
71
72 static dissector_table_t bvlc_dissector_table;
73
74 static const char*
75 bvlc_function_name (guint8 bvlc_function){
76   static const char *type_names[] = {
77         "BVLC-Result",
78         "Write-Broadcast-Distribution-Table",
79         "Read-Broadcast-Distribution-Table",
80         "Read-Broadcast-Distribution-Table-Ack",
81         "Forwarded-NPDU",
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"
89   };
90   return (bvlc_function > 0xb)? "unknown" : type_names[bvlc_function];
91 }
92
93 static const char*
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"
103   };
104   return (bvlc_result > 0x0060)? "unknown" : result_names[bvlc_result];
105 }
106
107 static gint ett_bvlc = -1;
108 static gint ett_bdt = -1;
109 static gint ett_fdt = -1;
110
111 static void
112 dissect_bvlc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
113 {
114
115         proto_item *ti;
116         proto_item *ti_bdt;
117         proto_item *ti_fdt;
118         proto_tree *bvlc_tree;
119         proto_tree *bdt_tree; /* Broadcast Distribution Table */
120         proto_tree *fdt_tree; /* Foreign Device Table */
121         
122         gint offset;
123         guint8 bvlc_type;
124         guint8 bvlc_function;
125         guint16 bvlc_length;
126         guint16 packet_length;
127         guint8 npdu_length;
128         guint16 bvlc_result;
129         tvbuff_t *next_tvb;
130
131         if (check_col(pinfo->cinfo, COL_PROTOCOL))
132                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "BVLC");
133
134         if (check_col(pinfo->cinfo, COL_INFO))
135                 col_set_str(pinfo->cinfo, COL_INFO, "BACnet Virtual Link Control");
136
137         offset = 0;
138
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)
148                  */
149                 bvlc_length = 4;
150         } else if(bvlc_function == 0x04) {
151                 /* 4 Bytes + 6 Bytes for B/IP Address of Originating Device */
152                 bvlc_length = 10; 
153         } else {
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.
158                  */
159                 bvlc_length = packet_length;
160         }
161         
162         if (tree) {
163                 ti = proto_tree_add_item(tree, proto_bvlc, tvb, 0, 
164                         bvlc_length, FALSE);
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");
169                 offset ++;
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));
173                 offset ++;
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);
177                 offset += 2;
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.
183                          */
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));
189                         offset += 2;
190                         break;
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);
201                                 offset += 4;
202                                 proto_tree_add_item(bdt_tree, hf_bvlc_bdt_port,
203                                         tvb, offset, 2, FALSE);
204                                 offset += 2;
205                                 proto_tree_add_item(bdt_tree, 
206                                         hf_bvlc_bdt_mask, tvb, offset, 4,
207                                         FALSE);
208                                 offset += 4;
209                         } 
210                         /* We check this if we get a BDT-packet somewhere */
211                         break;
212                 case 0x02: /* Read-Broadcast-Distribution-Table */
213                         /* nothing to do here */
214                         break;
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);
219                         offset += 2;
220                         break;
221                 case 0x06: /* Read-Foreign-Device-Table */
222                         /* nothing to do here */
223                         break;
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.
233                          */
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);
241                                 offset += 4;
242                                 proto_tree_add_item(fdt_tree, hf_bvlc_fdt_port,
243                                         tvb, offset, 2, FALSE);
244                                 offset += 2;
245                                 proto_tree_add_item(fdt_tree, 
246                                         hf_bvlc_fdt_ttl, tvb, offset, 2,
247                                         FALSE);
248                                 offset += 2;
249                                 proto_tree_add_item(fdt_tree, 
250                                         hf_bvlc_fdt_timeout, tvb, offset, 2,
251                                         FALSE);
252                                 offset += 2;
253                         } 
254                         /* We check this if we get a FDT-packet somewhere */
255                         break;
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);
260                         offset += 4;
261                         proto_tree_add_item(bvlc_tree, hf_bvlc_fdt_port,
262                                 tvb, offset, 2, FALSE);
263                         offset += 2;
264                         break;
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!
270                                  */
271                         /* proto_tree_add_bytes_format(); */
272                         proto_tree_add_item(bvlc_tree, hf_bvlc_fwd_ip,
273                                 tvb, offset, 4, FALSE);
274                         offset += 4;
275                         proto_tree_add_item(bvlc_tree, hf_bvlc_fwd_port,
276                                 tvb, offset, 2, FALSE);
277                         offset += 2;
278                 default:/* Distribute-Broadcast-To-Network
279                          * Original-Unicast-NPDU
280                          * Original-Broadcast-NPDU
281                          * Going to the next dissector...
282                          */
283                         break;
284                 }
285
286         }
287 /* Ok, no routing information BVLC packet. Dissect as
288  * BACnet NPDU
289  */
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);
297         }
298 }
299
300 void
301 proto_register_bvlc(void)
302 {
303         static hf_register_info hf[] = {
304                 { &hf_bvlc_type,
305                         { "Type",           "bvlc.type",
306                         FT_UINT8, BASE_HEX, NULL, 0,
307                         "Type", HFILL }
308                 },
309                 { &hf_bvlc_function,
310                         { "Function",           "bvlc.function",
311                         FT_UINT8, BASE_HEX, NULL, 0,
312                         "BLVC Function", HFILL }
313                 },
314                 { &hf_bvlc_length,
315                         { "Length",           "bvlc.length",
316                         FT_UINT16, BASE_DEC, NULL, 0,
317                         "Length of BVLC", HFILL }
318                 },
319                 /* We should bitmask the result correctly when we have a
320                  * packet to dissect */
321                 { &hf_bvlc_result,
322                         { "Result",           "bvlc.result",
323                         FT_UINT16, BASE_HEX, NULL, 0xffff,
324                         "Result Code", HFILL }
325                 },
326                 { &hf_bvlc_bdt_ip,
327                         { "IP",           "bvlc.bdt_ip",
328                         FT_IPv4, BASE_NONE, NULL, 0,
329                         "BDT IP", HFILL }
330                 },
331                 { &hf_bvlc_bdt_port,
332                         { "Port",           "bvlc.bdt_port",
333                         FT_UINT16, BASE_DEC, NULL, 0,
334                         "BDT Port", HFILL }
335                 },
336                 { &hf_bvlc_bdt_mask,
337                         { "Mask",           "bvlc.bdt_mask",
338                         FT_BYTES, BASE_HEX, NULL, 0,
339                         "BDT Broadcast Distribution Mask", HFILL }
340                 },
341                 { &hf_bvlc_reg_ttl,
342                         { "TTL",           "bvlc.reg_ttl",
343                         FT_UINT16, BASE_DEC, NULL, 0,
344                         "Foreign Device Time To Live", HFILL }
345                 },
346                 { &hf_bvlc_fdt_ip,
347                         { "IP",           "bvlc.fdt_ip",
348                         FT_IPv4, BASE_NONE, NULL, 0,
349                         "FDT IP", HFILL }
350                 },
351                 { &hf_bvlc_fdt_port,
352                         { "Port",           "bvlc.fdt_port",
353                         FT_UINT16, BASE_DEC, NULL, 0,
354                         "FDT Port", HFILL }
355                 },
356                 { &hf_bvlc_fdt_ttl,
357                         { "TTL",           "bvlc.fdt_ttl",
358                         FT_UINT16, BASE_DEC, NULL, 0,
359                         "Foreign Device Time To Live", HFILL }
360                 },
361                 { &hf_bvlc_fdt_timeout,
362                         { "Timeout",           "bvlc.fdt_timeout",
363                         FT_UINT16, BASE_DEC, NULL, 0,
364                         "Foreign Device Timeout (seconds)", HFILL }
365                 },
366                 { &hf_bvlc_fwd_ip,
367                         { "IP",           "bvlc.fwd_ip",
368                         FT_IPv4, BASE_NONE, NULL, 0,
369                         "FWD IP", HFILL }
370                 },
371                 { &hf_bvlc_fwd_port,
372                         { "Port",           "bvlc.fwd_port",
373                         FT_UINT16, BASE_DEC, NULL, 0,
374                         "FWD Port", HFILL }
375                 },
376         };
377
378         static gint *ett[] = {
379                 &ett_bvlc,
380                 &ett_bdt,
381                 &ett_fdt,
382         };
383
384         proto_bvlc = proto_register_protocol("BACnet Virtual Link Control",
385             "BVLC", "bvlc");
386
387         proto_register_field_array(proto_bvlc, hf, array_length(hf));
388         proto_register_subtree_array(ett, array_length(ett));
389
390         register_dissector("bvlc", dissect_bvlc, proto_bvlc);
391
392         bvlc_dissector_table = register_dissector_table("bvlc.function",
393             "BVLC Function", FT_UINT8, BASE_HEX);
394 }
395
396 void
397 proto_reg_handoff_bvlc(void)
398 {
399         dissector_handle_t bvlc_handle;
400
401         bvlc_handle = find_dissector("bvlc");
402         dissector_add("udp.port", 0xBAC0, bvlc_handle);
403         data_handle = find_dissector("data");
404 }
405 /* Taken from add-135a (BACnet-IP-standard paper):
406  *
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.
413  *
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
416  * "Decode As".
417  */