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