some compilers dont like unnamed unions and structs
[obnox/wireshark/wip.git] / epan / dissectors / packet-rmt-norm.c
1 /* packet-rmt-norm.c
2  * Reliable Multicast Transport (RMT)
3  * NORM Protocol Instantiation dissector
4  * Copyright 2005, Stefano Pettini <spettini@users.sourceforge.net>
5  *
6  * Extensive changes to decode more information Julian Onions
7  *
8  * Negative-acknowledgment (NACK)-Oriented Reliable Multicast (NORM):
9  * ------------------------------------------------------------------
10  *
11  * This protocol is designed to provide end-to-end reliable transport of
12  * bulk data objects or streams over generic IP multicast routing and
13  * forwarding services.  NORM uses a selective, negative acknowledgment
14  * mechanism for transport reliability and offers additional protocol
15  * mechanisms to allow for operation with minimal "a priori"
16  * coordination among senders and receivers.
17  *
18  * References:
19  *     RFC 3940, Negative-acknowledgment (NACK)-Oriented Reliable Multicast (NORM) Protocol
20  *
21  * $Id$
22  *
23  * Wireshark - Network traffic analyzer
24  * By Gerald Combs <gerald@wireshark.org>
25  * Copyright 1998 Gerald Combs
26  *
27  * This program is free software; you can redistribute it and/or
28  * modify it under the terms of the GNU General Public License
29  * as published by the Free Software Foundation; either version 2
30  * of the License, or (at your option) any later version.
31  *
32  * This program is distributed in the hope that it will be useful,
33  * but WITHOUT ANY WARRANTY; without even the implied warranty of
34  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
35  * GNU General Public License for more details.
36  *
37  * You should have received a copy of the GNU General Public License
38  * along with this program; if not, write to the Free Software
39  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
40  */
41
42 #ifdef HAVE_CONFIG_H
43 # include "config.h"
44 #endif
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49
50 #include <glib.h>
51
52 #include <epan/packet.h>
53 #include <epan/prefs.h>
54 #include <epan/strutil.h>
55
56 #include "packet-rmt-norm.h"
57 #include <math.h>
58
59 /* String tables */
60 static const value_string string_norm_type[] =
61 {
62         { NORM_INFO, "INFO" },
63         { NORM_DATA, "DATA" },
64         { NORM_CMD, "CMD" },
65         { NORM_NACK, "NACK" },
66         { NORM_ACK, "ACK" },
67         { NORM_REPORT, "REPORT" },
68         { 0, NULL }
69 };
70
71 static const value_string string_norm_cmd_type[] =
72 {
73         { NORM_CMD_FLUSH, "FLUSH" },
74         { NORM_CMD_EOT, "EOT" },
75         { NORM_CMD_SQUELCH, "SQUELCH" },
76         { NORM_CMD_CC, "CC" },
77         { NORM_CMD_REPAIR_ADV, "REPAIR_ADV" },
78         { NORM_CMD_ACK_REQ, "ACK_REQ" },
79         { NORM_CMD_APPLICATION, "APPLICATION" },
80         { 0, NULL }
81 };
82
83 static const value_string string_norm_ack_type[] =
84 {
85         { NORM_ACK_CC, "ACK CC" },
86         { NORM_ACK_FLUSH, "ACK FLUSH" },
87         { 0, NULL }
88 };
89
90 static const value_string string_norm_nack_form[] =
91 {
92         { NORM_NACK_ITEMS, "Items" },
93         { NORM_NACK_RANGES, "Ranges" },
94         { NORM_NACK_ERASURES, "Erasures" },
95         { 0, NULL }
96 };
97
98 #define hdrlen2bytes(x) ((x)*4U)
99
100 /* Initialize the protocol and registered fields */
101 /* ============================================= */
102
103 static int proto = -1;
104 static gboolean global_norm_heur = FALSE;
105
106 static struct _norm_hf hf;
107 static struct _norm_ett ett;
108
109 static gboolean preferences_initialized = FALSE;
110 static struct _norm_prefs preferences;
111 static struct _norm_prefs preferences_old;
112
113 /* Preferences */
114 /* =========== */
115
116 /* Set/Reset preferences to default values */
117 static void norm_prefs_set_default(struct _norm_prefs *prefs)
118 {
119         fec_prefs_set_default(&prefs->fec);
120 }
121
122 /* Register preferences */
123 static void norm_prefs_register(struct _norm_prefs *prefs, module_t *module)
124 {
125         fec_prefs_register(&prefs->fec, module);
126 }
127
128 /* Save preferences to alc_prefs_old */
129 static void norm_prefs_save(struct _norm_prefs *p, struct _norm_prefs *p_old)
130 {
131         *p_old = *p;
132 }
133
134 static const double RTT_MIN = 1.0e-06;
135 static const double RTT_MAX = 1000;
136
137 static double UnquantizeRtt(unsigned char qrtt)
138 {
139          return ((qrtt <= 31) ? (((double)(qrtt+1))*(double)RTT_MIN) :
140                 (RTT_MAX/exp(((double)(255-qrtt))/(double)13.0)));
141 }
142
143 static double UnquantizeGSize(guint8 gsize)
144 {
145         guint mant = (gsize & 0x8) ? 5 : 1;
146         guint exponent = gsize & 0x7;
147         exponent ++;
148         return mant * pow(10, exponent);
149 }
150
151 static double UnquantizeSendRate(guint16 send_rate)
152 {
153         return (send_rate >> 4) * 10.0 / 4096.0 * pow(10.0, (send_rate & 0x000f));
154 }
155
156 /* code to dissect fairly common sequence in NORM packets */
157 static guint dissect_grrtetc(proto_tree *tree, tvbuff_t *tvb, guint offset)
158 {
159         guint8 backoff;
160         double gsize;
161         double grtt;
162         proto_tree_add_item(tree, hf.instance_id, tvb, offset, 2, FALSE); offset+=2;
163         grtt = UnquantizeRtt(tvb_get_guint8(tvb, offset));
164         proto_tree_add_double(tree, hf.grtt, tvb, offset, 1, grtt); offset++;
165         backoff = hi_nibble(tvb_get_guint8(tvb, offset));
166         gsize = UnquantizeGSize((guint8)lo_nibble(tvb_get_guint8(tvb, offset)));
167         proto_tree_add_uint(tree, hf.backoff, tvb, offset, 1, backoff);
168         proto_tree_add_double(tree, hf.gsize, tvb, offset, 1, gsize);
169         offset++;
170         return offset;
171 }
172
173 /* split out some common FEC handling */
174 static guint dissect_feccode(struct _norm *norm, struct _fec_ptr *f, proto_tree *tree,
175                                                          tvbuff_t *tvb, guint offset, packet_info *pinfo, gint reserved)
176 {
177         f->fec = &norm->fec;
178         f->hf = &hf.fec;
179         f->ett = &ett.fec;
180         f->prefs = &preferences.fec;
181
182
183         norm->fec.encoding_id = tvb_get_guint8(tvb, offset);
184         norm->fec.encoding_id_present = 1;
185         proto_tree_add_item(tree, hf.fec.encoding_id, tvb, offset, 1, FALSE); offset++;
186         if (reserved) {
187                 proto_tree_add_item(tree, hf.reserved, tvb, offset, 1, FALSE); offset++;
188         }
189         proto_tree_add_item(tree, hf.object_transport_id, tvb, offset, 2, FALSE); offset+=2;
190
191         if (norm->fec.encoding_id_present &&
192             tvb_reported_length_remaining(tvb, offset) > 0) {
193                 fec_dissector(*f, tvb, tree, &offset);
194                 if (check_col(pinfo->cinfo, COL_INFO))
195                         fec_info_column(f->fec, pinfo);
196         }
197         return offset;
198 }
199
200 static guint dissect_norm_hdrext(struct _norm *norm, struct _fec_ptr *f, proto_tree *tree,
201                                                          tvbuff_t *tvb, guint offset, packet_info *pinfo _U_)
202 {
203         guint i;
204         proto_item *ti;
205         /* Allocate an array of _ext elements */
206         GArray *ext;
207         guint offset_old = offset;
208         proto_tree *ext_tree;
209
210         ext = g_array_new(FALSE, TRUE, sizeof(struct _ext));
211
212         rmt_ext_parse(ext, tvb, &offset, hdrlen2bytes(norm->hlen));
213
214         if (ext->len > 0)
215         {
216                 struct _lct_prefs lctp;
217                 memset(&lctp, 0, sizeof(lctp));
218                 if (tree)
219                 {
220                         /* Add the extensions subtree */
221                         ti = proto_tree_add_uint(tree, hf.extension,
222                                 tvb, offset_old,
223                                 offset - offset_old, ext->len);
224                         ext_tree = proto_item_add_subtree(ti, ett.hdrext);
225                 } else
226                         ext_tree = NULL;
227
228                 /* Add the extensions to the subtree */
229                 for (i = 0; i < ext->len; i++) {
230                         struct _ext *e = &g_array_index(ext, struct _ext, i);
231
232                         lct_ext_decode(e, &lctp, tvb, ext_tree, ett.hdrext, *f);
233                         /* fec_decode_ext_fti(e, tvb, ext_tree, ett.hdrext, *f); */
234                 }
235         }
236         g_array_free(ext, TRUE);
237         return offset;
238 }
239
240 static guint dissect_nack_data(struct _norm *norm, proto_tree *tree,
241                                                          tvbuff_t *tvb, guint offset, packet_info *pinfo)
242 {
243         proto_item *ti, *tif;
244         proto_tree *nack_tree, *flag_tree;
245         guint16 len;
246         ti = proto_tree_add_text(tree, tvb, offset, -1, "NACK Data");
247         nack_tree = proto_item_add_subtree(ti, ett.nackdata);
248         proto_tree_add_item(nack_tree, hf.nack_form, tvb, offset, 1, FALSE); offset += 1;
249
250         tif = proto_tree_add_item(nack_tree, hf.nack_flags, tvb, offset, 1, FALSE);
251         flag_tree = proto_item_add_subtree(tif, ett.flags);
252         proto_tree_add_item(flag_tree, hf.nack_flags_segment, tvb, offset, 1, FALSE);
253         proto_tree_add_item(flag_tree, hf.nack_flags_block, tvb, offset, 1, FALSE);
254         proto_tree_add_item(flag_tree, hf.nack_flags_info, tvb, offset, 1, FALSE);
255         proto_tree_add_item(flag_tree, hf.nack_flags_object, tvb, offset, 1, FALSE);
256         offset += 1;
257         len = tvb_get_ntohs(tvb, offset);
258         proto_tree_add_item(nack_tree, hf.nack_length, tvb, offset, 2, FALSE); offset += 2;
259         proto_item_set_len(ti, 4+len);
260         if (len > 4) {
261                 struct _fec_ptr f;
262                 dissect_feccode(norm, &f, nack_tree, tvb, offset, pinfo, 1);
263         }
264         offset += len;
265         return offset;
266 }
267
268
269 /* code to dissect NORM data packets */
270 static void dissect_norm_data(struct _norm *norm, proto_tree *tree,
271         tvbuff_t *tvb, guint offset, packet_info *pinfo)
272 {
273         guint8 flags;
274         proto_item *ti;
275         proto_tree *flag_tree;
276         struct _fec_ptr f;
277
278         offset = dissect_grrtetc(tree, tvb, offset);
279
280
281         ti = proto_tree_add_item(tree, hf.flags, tvb, offset, 1, FALSE);
282         flags = tvb_get_guint8(tvb, offset);
283         flag_tree = proto_item_add_subtree(ti, ett.flags);
284         proto_tree_add_item(flag_tree, hf.flag.repair, tvb, offset, 1, FALSE);
285         proto_tree_add_item(flag_tree, hf.flag.explicit, tvb, offset, 1, FALSE);
286         proto_tree_add_item(flag_tree, hf.flag.info, tvb, offset, 1, FALSE);
287         proto_tree_add_item(flag_tree, hf.flag.unreliable, tvb, offset, 1, FALSE);
288         proto_tree_add_item(flag_tree, hf.flag.file, tvb, offset, 1, FALSE);
289         proto_tree_add_item(flag_tree, hf.flag.stream, tvb, offset, 1, FALSE);
290         proto_tree_add_item(flag_tree, hf.flag.msgstart, tvb, offset, 1, FALSE);
291         offset++;
292
293         offset = dissect_feccode(norm, &f, tree, tvb, offset, pinfo, 0);
294
295         if (offset < hdrlen2bytes(norm->hlen)) {
296                 offset = dissect_norm_hdrext(norm, &f, tree, tvb, offset, pinfo);
297         }
298         if (flags & NORM_FLAG_STREAM) {
299                 ti = proto_tree_add_text(tree, tvb, offset, 8, "Stream Data");
300                 flag_tree = proto_item_add_subtree(ti, ett.streampayload);
301                 proto_tree_add_item(flag_tree, hf.reserved, tvb, offset, 2, FALSE); offset+=2;
302                 proto_tree_add_item(flag_tree, hf.payload_len, tvb, offset, 2, FALSE); offset+=2;
303                 proto_tree_add_item(flag_tree, hf.payload_offset, tvb, offset, 4, FALSE); offset+=4;
304
305         }
306         if (tvb_reported_length_remaining(tvb, offset) > 0)
307                 proto_tree_add_none_format(tree, hf.payload, tvb, offset, -1, "Payload (%u bytes)", tvb_reported_length_remaining(tvb, offset));
308
309 }
310
311 /* code to dissect NORM info packets */
312 static void dissect_norm_info(struct _norm *norm, proto_tree *tree,
313         tvbuff_t *tvb, guint offset, packet_info *pinfo _U_)
314 {
315         guint8 flags;
316         proto_item *ti;
317         proto_tree *flag_tree;
318
319         offset = dissect_grrtetc(tree, tvb, offset);
320
321         ti = proto_tree_add_item(tree, hf.flags, tvb, offset, 1, FALSE);
322         flags = tvb_get_guint8(tvb, offset);
323         flag_tree = proto_item_add_subtree(ti, ett.flags);
324         proto_tree_add_item(flag_tree, hf.flag.repair, tvb, offset, 1, FALSE);
325         proto_tree_add_item(flag_tree, hf.flag.explicit, tvb, offset, 1, FALSE);
326         proto_tree_add_item(flag_tree, hf.flag.info, tvb, offset, 1, FALSE);
327         proto_tree_add_item(flag_tree, hf.flag.unreliable, tvb, offset, 1, FALSE);
328         proto_tree_add_item(flag_tree, hf.flag.file, tvb, offset, 1, FALSE);
329         proto_tree_add_item(flag_tree, hf.flag.stream, tvb, offset, 1, FALSE);
330         proto_tree_add_item(flag_tree, hf.flag.msgstart, tvb, offset, 1, FALSE);
331         offset++;
332
333         norm->fec.encoding_id = tvb_get_guint8(tvb, offset);
334         norm->fec.encoding_id_present = 1;
335         proto_tree_add_item(tree, hf.fec.encoding_id, tvb, offset, 1, FALSE); offset++;
336         proto_tree_add_item(tree, hf.object_transport_id, tvb, offset, 2, FALSE); offset+=2;
337
338         if (offset < hdrlen2bytes(norm->hlen)) {
339                 struct _fec_ptr f;
340                 memset(&f, 0, sizeof f);
341                 f.fec = &norm->fec;
342                 f.hf = &hf.fec;
343                 f.ett = &ett.fec;
344                 f.prefs = &preferences.fec;
345                 offset = dissect_norm_hdrext(norm, &f, tree, tvb, offset, pinfo);
346         }
347         if (tvb_reported_length_remaining(tvb, offset) > 0)
348                 proto_tree_add_none_format(tree, hf.payload, tvb, offset, -1, "Payload (%u bytes)", tvb_reported_length_remaining(tvb, offset));
349
350 }
351 /* code to dissect NORM cmd(flush) packets */
352 static guint dissect_norm_cmd_flush(struct _norm *norm, proto_tree *tree,
353         tvbuff_t *tvb, guint offset, packet_info *pinfo)
354 {
355         struct _fec_ptr f;
356         offset = dissect_feccode(norm, &f, tree, tvb, offset, pinfo, 0);
357         if (offset < hdrlen2bytes(norm->hlen)) {
358                 offset = dissect_norm_hdrext(norm, &f, tree, tvb, offset, pinfo);
359         }
360         return offset;
361 }
362
363 /* code to dissect NORM cmd(flush) packets */
364 static guint dissect_norm_cmd_repairadv(struct _norm *norm, proto_tree *tree,
365         tvbuff_t *tvb, guint offset, packet_info *pinfo)
366 {
367         proto_tree_add_item(tree, hf.flags, tvb, offset, 1, FALSE); offset ++;
368         proto_tree_add_item(tree, hf.reserved, tvb, offset, 2, FALSE); offset +=2;
369
370         if (offset < hdrlen2bytes(norm->hlen)) {
371                 struct _fec_ptr f;
372                 memset(&f, 0, sizeof f);
373                 f.fec = &norm->fec;
374                 f.hf = &hf.fec;
375                 f.ett = &ett.fec;
376                 f.prefs = &preferences.fec;
377                 offset = dissect_norm_hdrext(norm, &f, tree, tvb, offset, pinfo);
378         }
379         while (tvb_reported_length_remaining(tvb, offset) > 0) {
380                 offset = dissect_nack_data(norm, tree, tvb, offset, pinfo);
381         }
382         return offset;
383 }
384
385 /* code to dissect NORM cmd(cc) packets */
386 static guint dissect_norm_cmd_cc(struct _norm *norm, proto_tree *tree,
387         tvbuff_t *tvb, guint offset, packet_info *pinfo _U_)
388 {
389         proto_tree_add_item(tree, hf.reserved, tvb, offset, 1, FALSE); offset ++;
390         proto_tree_add_item(tree, hf.cc_sequence, tvb, offset, 2, FALSE); offset += 2;
391
392         proto_tree_add_item(tree, hf.cc_sts, tvb, offset, 4, FALSE); offset += 4;
393         proto_tree_add_item(tree, hf.cc_stus, tvb, offset, 4, FALSE); offset += 4;
394         if (offset < hdrlen2bytes(norm->hlen)) {
395                 struct _fec_ptr f;
396                 memset(&f, 0, sizeof f);
397                 f.fec = &norm->fec;
398                 f.hf = &hf.fec;
399                 f.ett = &ett.fec;
400                 f.prefs = &preferences.fec;
401                 offset = dissect_norm_hdrext(norm, &f, tree, tvb, offset, pinfo);
402         }
403         while (tvb_reported_length_remaining(tvb, offset) > 0) {
404                 proto_item *ti, *tif;
405                 proto_tree *cc_tree, *flag_tree;
406                 double grtt;
407                 ti = proto_tree_add_text(tree, tvb, offset, 8, "Congestion Control");
408                 cc_tree = proto_item_add_subtree(ti, ett.congestioncontrol);
409                 proto_tree_add_item(cc_tree, hf.cc_node_id, tvb, offset, 4, FALSE); offset += 4;
410                 tif = proto_tree_add_item(cc_tree, hf.cc_flags, tvb, offset, 1, FALSE);
411                 flag_tree = proto_item_add_subtree(tif, ett.flags);
412                 proto_tree_add_item(flag_tree, hf.cc_flags_clr, tvb, offset, 1, FALSE);
413                 proto_tree_add_item(flag_tree, hf.cc_flags_plr, tvb, offset, 1, FALSE);
414                 proto_tree_add_item(flag_tree, hf.cc_flags_rtt, tvb, offset, 1, FALSE);
415                 proto_tree_add_item(flag_tree, hf.cc_flags_start, tvb, offset, 1, FALSE);
416                 proto_tree_add_item(flag_tree, hf.cc_flags_leave, tvb, offset, 1, FALSE);
417                 offset += 1;
418                 grtt = UnquantizeRtt(tvb_get_guint8(tvb, offset));
419                 proto_tree_add_double(cc_tree, hf.cc_rtt, tvb, offset, 1, grtt); offset += 1;
420                 grtt = UnquantizeSendRate(tvb_get_ntohs(tvb, offset));
421                 proto_tree_add_double(cc_tree, hf.cc_rate, tvb, offset, 2, grtt); offset += 2;
422         }
423         return offset;
424 }
425
426 /* code to dissect NORM cmd(squelch) packets */
427 static guint dissect_norm_cmd_squelch(struct _norm *norm, proto_tree *tree,
428         tvbuff_t *tvb, guint offset, packet_info *pinfo)
429 {
430         struct _fec_ptr f;
431         offset = dissect_feccode(norm, &f, tree, tvb, offset, pinfo, 0);
432
433         while (tvb_reported_length_remaining(tvb, offset) > 0) {
434                 proto_tree_add_item(tree, hf.cc_transport_id, tvb, offset, 4, FALSE); offset += 2;
435         }
436         return offset;
437 }
438
439 /* code to dissect NORM cmd(squelch) packets */
440 static guint dissect_norm_cmd_ackreq(struct _norm *norm _U_, proto_tree *tree,
441         tvbuff_t *tvb, guint offset, packet_info *pinfo _U_)
442 {
443         proto_tree_add_item(tree, hf.reserved, tvb, offset, 1, FALSE); offset ++;
444         proto_tree_add_item(tree, hf.ack_type, tvb, offset, 1, FALSE); offset += 1;
445         proto_tree_add_item(tree, hf.ack_id, tvb, offset, 1, FALSE); offset += 1;
446         return offset;
447 }
448
449 /* code to dissect NORM cmd packets */
450 static void dissect_norm_cmd(struct _norm *norm, proto_tree *tree,
451         tvbuff_t *tvb, guint offset, packet_info *pinfo)
452 {
453         guint8 flavor;
454
455         offset = dissect_grrtetc(tree, tvb, offset);
456         flavor = tvb_get_guint8(tvb, offset);
457         if (check_col(pinfo->cinfo, COL_INFO))
458                 col_append_sep_str(pinfo->cinfo, COL_INFO, " ",
459                 val_to_str(flavor, string_norm_cmd_type, "Unknown Cmd Type (0x%04x)"));
460         proto_tree_add_item(tree, hf.cmd_flavor, tvb, offset, 1, FALSE); offset ++;
461         switch(flavor) {
462         case NORM_CMD_CC:
463                 offset = dissect_norm_cmd_cc(norm, tree, tvb, offset, pinfo);
464                 break;
465         case NORM_CMD_FLUSH:
466                 offset = dissect_norm_cmd_flush(norm, tree, tvb, offset, pinfo);
467                 break;
468         case NORM_CMD_SQUELCH:
469                 offset = dissect_norm_cmd_squelch(norm, tree, tvb, offset, pinfo);
470                 break;
471         case NORM_CMD_REPAIR_ADV:
472                 offset = dissect_norm_cmd_repairadv(norm, tree, tvb, offset, pinfo);
473                 break;
474         case NORM_CMD_ACK_REQ:
475                 offset = dissect_norm_cmd_ackreq(norm, tree, tvb, offset, pinfo);
476                 break;
477         }
478         if (tvb_reported_length_remaining(tvb, offset) > 0)
479                 proto_tree_add_none_format(tree, hf.payload, tvb, offset, -1, "Payload (%u bytes)", tvb_reported_length_remaining(tvb, offset));
480 }
481
482 /* code to dissect NORM ack packets */
483 static void dissect_norm_ack(struct _norm *norm, proto_tree *tree,
484         tvbuff_t *tvb, guint offset, packet_info *pinfo)
485 {
486         guint8 acktype;
487
488         proto_tree_add_item(tree, hf.ack_source, tvb, offset, 4, FALSE); offset += 4;
489         proto_tree_add_item(tree, hf.instance_id, tvb, offset, 2, FALSE); offset += 2;
490         acktype = tvb_get_guint8(tvb, offset);
491         if (check_col(pinfo->cinfo, COL_INFO))
492                 col_append_sep_str(pinfo->cinfo, COL_INFO, " ",
493                 val_to_str(acktype, string_norm_ack_type, "Unknown Ack Type (0x%04x)"));
494         proto_tree_add_item(tree, hf.ack_type, tvb, offset, 1, FALSE); offset += 1;
495         proto_tree_add_item(tree, hf.ack_id, tvb, offset, 1, FALSE); offset += 1;
496         proto_tree_add_item(tree, hf.ack_grtt_sec, tvb, offset, 4, FALSE); offset += 4;
497         proto_tree_add_item(tree, hf.ack_grtt_usec, tvb, offset, 4, FALSE); offset += 4;
498         if (offset < hdrlen2bytes(norm->hlen)) {
499                 struct _fec_ptr f;
500                 memset(&f, 0, sizeof f);
501                 f.fec = &norm->fec;
502                 f.hf = &hf.fec;
503                 f.ett = &ett.fec;
504                 f.prefs = &preferences.fec;
505                 offset = dissect_norm_hdrext(norm, &f, tree, tvb, offset, pinfo);
506         }
507
508         if (tvb_reported_length_remaining(tvb, offset) > 0)
509                 proto_tree_add_none_format(tree, hf.payload, tvb, offset, -1, "Payload (%u bytes)", tvb_reported_length_remaining(tvb, offset));
510
511 }
512
513
514
515 /* code to dissect NORM nack packets */
516 static void dissect_norm_nack(struct _norm *norm, proto_tree *tree,
517         tvbuff_t *tvb, guint offset, packet_info *pinfo)
518 {
519         proto_tree_add_item(tree, hf.nack_server, tvb, offset, 4, FALSE); offset += 4;
520         proto_tree_add_item(tree, hf.instance_id, tvb, offset, 2, FALSE); offset += 2;
521         proto_tree_add_item(tree, hf.reserved, tvb, offset, 2, FALSE); offset += 2;
522         proto_tree_add_item(tree, hf.nack_grtt_sec, tvb, offset, 4, FALSE); offset += 4;
523         proto_tree_add_item(tree, hf.nack_grtt_usec, tvb, offset, 4, FALSE); offset += 4;
524         if (offset < hdrlen2bytes(norm->hlen)) {
525                 struct _fec_ptr f;
526                 memset(&f, 0, sizeof f);
527                 f.fec = &norm->fec;
528                 f.hf = &hf.fec;
529                 f.ett = &ett.fec;
530                 f.prefs = &preferences.fec;
531                 offset = dissect_norm_hdrext(norm, &f, tree, tvb, offset, pinfo);
532         }
533
534         while (tvb_reported_length_remaining(tvb, offset) > 0) {
535                 offset = dissect_nack_data(norm, tree, tvb, offset, pinfo);
536         }
537         if (tvb_reported_length_remaining(tvb, offset) > 0)
538                 proto_tree_add_none_format(tree, hf.payload, tvb, offset, -1, "Payload (%u bytes)", tvb_reported_length_remaining(tvb, offset));
539
540 }
541 /* Code to actually dissect the packets */
542 /* ==================================== */
543
544 static void dissect_norm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
545 {
546         /* Logical packet representation */
547         struct _norm norm;
548
549         /* Offset for subpacket dissection */
550         guint offset;
551
552         /* Set up structures needed to add the protocol subtree and manage it */
553         proto_item *ti;
554         proto_tree *norm_tree;
555
556         /* Structures and variables initialization */
557         offset = 0;
558         memset(&norm, 0, sizeof(struct _norm));
559
560         /* Update packet info */
561         pinfo->current_proto = "NORM";
562
563         /* Make entries in Protocol column and Info column on summary display */
564         if (check_col(pinfo->cinfo, COL_PROTOCOL))
565                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "NORM");
566         if (check_col(pinfo->cinfo, COL_INFO))
567                 col_clear(pinfo->cinfo, COL_INFO);
568
569         /* NORM header dissection, part 1 */
570         /* ------------------------------ */
571
572         norm.version = hi_nibble(tvb_get_guint8(tvb, offset));
573
574         if (tree)
575         {
576                 /* Create subtree for the NORM protocol */
577                 ti = proto_tree_add_item(tree, proto, tvb, offset, -1, FALSE);
578                 norm_tree = proto_item_add_subtree(ti, ett.main);
579
580                 /* Fill the NORM subtree */
581                 proto_tree_add_uint(norm_tree, hf.version, tvb, offset, 1, norm.version);
582
583         } else
584                 norm_tree = NULL;
585
586         /* This dissector supports only NORMv1 packets.
587          * If norm.version > 1 print only version field and quit.
588          */
589         if (norm.version == 1) {
590
591                 /* NORM header dissection, part 2 */
592                 /* ------------------------------ */
593
594                 norm.type = lo_nibble(tvb_get_guint8(tvb, offset));
595                 norm.hlen = tvb_get_guint8(tvb, offset+1);
596                 norm.sequence = tvb_get_ntohs(tvb, offset+2);
597                 norm.source_id = tvb_get_ntohl(tvb, offset+4);
598
599                 if (tree)
600                 {
601                         proto_tree_add_uint(norm_tree, hf.type, tvb, offset, 1, norm.type);
602                         proto_tree_add_uint(norm_tree, hf.hlen, tvb, offset+1, 1, norm.hlen);
603                         proto_tree_add_uint(norm_tree, hf.sequence, tvb, offset+2, 2, norm.sequence);
604                         proto_tree_add_item(norm_tree, hf.source_id, tvb, offset+4, 4, FALSE);
605                 }
606
607                 offset += 8;
608
609
610                 /* Complete entry in Info column on summary display */
611                 /* ------------------------------------------------ */
612                 if (check_col(pinfo->cinfo, COL_INFO))
613                                 col_append_sep_str(pinfo->cinfo, COL_INFO, " ",
614                                 val_to_str(norm.type, string_norm_type, "Unknown Type (0x%04x)"));
615
616
617                 switch(norm.type) {
618                 case NORM_INFO:
619                         dissect_norm_info(&norm, norm_tree, tvb, offset, pinfo);
620                         break;
621                 case NORM_DATA:
622                         dissect_norm_data(&norm, norm_tree, tvb, offset, pinfo);
623                         break;
624                 case NORM_CMD:
625                         dissect_norm_cmd(&norm, norm_tree, tvb, offset, pinfo);
626                         break;
627                 case NORM_ACK:
628                         dissect_norm_ack(&norm, norm_tree, tvb, offset, pinfo);
629                         break;
630                 case NORM_NACK:
631                         dissect_norm_nack(&norm, norm_tree, tvb, offset, pinfo);
632                         break;
633                 default:
634                         /* Add the Payload item */
635                         if (tvb_reported_length_remaining(tvb, offset) > 0)
636                                 proto_tree_add_none_format(norm_tree, hf.payload, tvb, offset, -1, "Payload (%u bytes)", tvb_reported_length_remaining(tvb, offset));
637                         break;
638                 }
639
640         } else {
641
642                 if (tree)
643                         proto_tree_add_text(norm_tree, tvb, 0, -1, "Sorry, this dissector supports NORM version 1 only");
644
645                 /* Complete entry in Info column on summary display */
646                 if (check_col(pinfo->cinfo, COL_INFO))
647                         col_add_fstr(pinfo->cinfo, COL_INFO, "Version: %u (not supported)", norm.version);
648         }
649 }
650
651 static gboolean
652 dissect_norm_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
653 {
654         guint8 byte1;
655         if (!global_norm_heur)
656                 return FALSE;
657         if (!tvb_bytes_exist(tvb, 0, 2))
658                 return FALSE;   /* not enough to check */
659         byte1 = tvb_get_guint8(tvb, 0);
660
661         if (hi_nibble(byte1) != 1) return FALSE;
662         if (lo_nibble(byte1) < 1 || lo_nibble(byte1) > 6) return FALSE;
663         if (tvb_get_guint8(tvb, 1) > 20) return FALSE;
664         if (tvb_length_remaining(tvb, 0) < 12)
665                 return FALSE;
666         dissect_norm(tvb, pinfo, tree);
667         return TRUE; /* appears to be a NORM packet */
668 }
669
670 void proto_reg_handoff_norm(void)
671 {
672         static dissector_handle_t handle;
673
674         if (!preferences_initialized)
675         {
676                 preferences_initialized = TRUE;
677                 handle = create_dissector_handle(dissect_norm, proto);
678                 dissector_add_handle("udp.port", handle);
679                 heur_dissector_add("udp", dissect_norm_heur, proto);
680         }
681
682         norm_prefs_save(&preferences, &preferences_old);
683 }
684
685 void proto_register_norm(void)
686 {
687         /* Setup NORM header fields */
688         static hf_register_info hf_ptr[] = {
689
690                 { &hf.version,
691                         { "Version", "norm.version", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
692                 { &hf.type,
693                         { "Message Type", "norm.type", FT_UINT8, BASE_DEC, VALS(string_norm_type), 0x0, "", HFILL }},
694                 { &hf.hlen,
695                         { "Header length", "norm.hlen", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
696                 { &hf.sequence,
697                         { "Sequence", "norm.sequence", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
698                 { &hf.source_id,
699                         { "Source ID", "norm.source_id", FT_IPv4, BASE_NONE, NULL, 0x0, "", HFILL }},
700                 { &hf.instance_id,
701                         { "Instance", "norm.instance_id", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL}},
702                 { &hf.grtt,
703                         { "grtt", "norm.grtt", FT_DOUBLE, BASE_DEC, NULL, 0x0, "", HFILL}},
704                 { &hf.backoff,
705                         { "Backoff", "norm.backoff", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL}},
706                 { &hf.gsize,
707                         { "Group Size", "norm.gsize", FT_DOUBLE, BASE_DEC, NULL, 0x0, "", HFILL}},
708                 { &hf.flags,
709                         { "Flags", "norm.flags", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL}},
710             { &hf.flag.repair,
711                     { "Repair Flag", "norm.flag.repair", FT_BOOLEAN, 8, NULL, NORM_FLAG_REPAIR, "", HFILL }},
712             { &hf.flag.explicit,
713                     { "Explicit Flag", "norm.flag.explicit", FT_BOOLEAN, 8, NULL, NORM_FLAG_EXPLICIT, "", HFILL }},
714             { &hf.flag.info,
715                     { "Info Flag", "norm.flag.info", FT_BOOLEAN, 8, NULL, NORM_FLAG_INFO, "", HFILL }},
716             { &hf.flag.unreliable,
717                     { "Unreliable Flag", "norm.flag.unreliable", FT_BOOLEAN, 8, NULL, NORM_FLAG_UNRELIABLE, "", HFILL }},
718             { &hf.flag.file,
719                     { "File Flag", "norm.flag.file", FT_BOOLEAN, 8, NULL, NORM_FLAG_FILE, "", HFILL }},
720             { &hf.flag.stream,
721                     { "Stream Flag", "norm.flag.stream", FT_BOOLEAN, 8, NULL, NORM_FLAG_STREAM, "", HFILL }},
722             { &hf.flag.msgstart,
723                     { "Msg Start Flag", "norm.flag.msgstart", FT_BOOLEAN, 8, NULL, NORM_FLAG_MSG_START, "", HFILL }},
724                 { &hf.object_transport_id,
725                         { "Object Transport ID", "norm.object_transport_id", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL}},
726                 { &hf.extension,
727                         { "Hdr Extension", "norm.hexext", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL}},
728                 { &hf.reserved,
729                         { "Reserved", "norm.reserved", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL}},
730                 { &hf.payload_len,
731                         { "Payload Len", "norm.payload.len", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL}},
732                 { &hf.payload_offset,
733                         { "Payload Offset", "norm.payload.offset", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL}},
734
735                 { &hf.cmd_flavor,
736                         { "Flavor", "norm.flavor", FT_UINT8, BASE_DEC, VALS(string_norm_cmd_type), 0x0, "", HFILL}},
737                 { &hf.cc_sequence,
738                         { "CC Sequence", "norm.ccsequence", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL}},
739                 { &hf.cc_sts,
740                         { "Send Time secs", "norm.cc_sts", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL}},
741                 { &hf.cc_stus,
742                         { "Send Time usecs", "norm.cc_stus", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL}},
743                 { &hf.cc_node_id,
744                         { "CC Node ID", "norm.cc_node_id", FT_IPv4, BASE_NONE, NULL, 0x0, "", HFILL}},
745                 { &hf.cc_flags,
746                         { "CC Flags", "norm.cc_flags", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL}},
747                 { &hf.cc_flags_clr,
748                         { "CLR", "norm.cc_flags.clr", FT_BOOLEAN, 8, NULL, NORM_FLAG_CC_CLR, "", HFILL}},
749                 { &hf.cc_flags_plr,
750                         { "PLR", "norm.cc_flags.plr", FT_BOOLEAN, 8, NULL, NORM_FLAG_CC_PLR, "", HFILL}},
751                 { &hf.cc_flags_rtt,
752                         { "RTT", "norm.cc_flags.rtt", FT_BOOLEAN, 8, NULL, NORM_FLAG_CC_RTT, "", HFILL}},
753                 { &hf.cc_flags_start,
754                         { "Start", "norm.cc_flags.start", FT_BOOLEAN, 8, NULL, NORM_FLAG_CC_START, "", HFILL}},
755                 { &hf.cc_flags_leave,
756                         { "Leave", "norm.cc_flags.leave", FT_BOOLEAN, 8, NULL, NORM_FLAG_CC_LEAVE, "", HFILL}},
757                 { &hf.cc_rtt,
758                         { "CC RTT", "norm.cc_rtt", FT_DOUBLE, BASE_DEC, NULL, 0x0, "", HFILL}},
759                 { &hf.cc_rate,
760                         { "CC Rate", "norm.cc_rate", FT_DOUBLE, BASE_DEC, NULL, 0x0, "", HFILL}},
761                 { &hf.cc_transport_id,
762                         { "CC Transport ID", "norm.cc_transport_id", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL}},
763
764                 { &hf.ack_source,
765                         { "Ack Source", "norm.ack.source", FT_IPv4, BASE_DEC, NULL, 0x0, "", HFILL}},
766                 { &hf.ack_type,
767                         { "Ack Type", "norm.ack.type", FT_UINT8, BASE_DEC, VALS(string_norm_ack_type), 0x0, "", HFILL}},
768                 { &hf.ack_id,
769                         { "Ack ID", "norm.ack.id", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL}},
770                 { &hf.ack_grtt_sec,
771                         { "Ack GRTT Sec", "norm.ack.grtt_sec", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL}},
772                 { &hf.ack_grtt_usec,
773                         { "Ack GRTT usec", "norm.ack.grtt_usec", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL}},
774
775                 { &hf.nack_server,
776                         { "NAck Server", "norm.nack.server", FT_IPv4, BASE_DEC, NULL, 0x0, "", HFILL}},
777                 { &hf.nack_grtt_sec,
778                         { "NAck GRTT Sec", "norm.nack.grtt_sec", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL}},
779                 { &hf.nack_grtt_usec,
780                         { "NAck GRTT usec", "norm.nack.grtt_usec", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL}},
781                 { &hf.nack_form,
782                         { "NAck FORM", "norm.nack.form", FT_UINT8, BASE_DEC, VALS(string_norm_nack_form), 0x0, "", HFILL}},
783                 { &hf.nack_flags,
784                         { "NAck Flags", "norm.nack.flags", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL}},
785                 { &hf.nack_flags_segment,
786                         { "Segment", "norm.nack.flags.segment", FT_BOOLEAN, 8, NULL, NORM_NACK_SEGMENT, "", HFILL}},
787                 { &hf.nack_flags_block,
788                         { "Block", "norm.nack.flags.block", FT_BOOLEAN, 8, NULL, NORM_NACK_BLOCK, "", HFILL}},
789                 { &hf.nack_flags_info,
790                         { "Info", "norm.nack.flags.info", FT_BOOLEAN, 8, NULL, NORM_NACK_INFO, "", HFILL}},
791                 { &hf.nack_flags_object,
792                         { "Object", "norm.nack.flags.object", FT_BOOLEAN, 8, NULL, NORM_NACK_OBJECT, "", HFILL}},
793                 { &hf.nack_length,
794                         { "NAck Length", "norm.nack.length", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL}},
795
796
797                 FEC_FIELD_ARRAY(hf.fec, "NORM"),
798
799                 { &hf.payload,
800                         { "Payload", "norm.payload", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }}
801         };
802
803         /* Setup protocol subtree array */
804         static gint *ett_ptr[] = {
805                 &ett.main,
806                 &ett.hdrext,
807                 &ett.flags,
808                 &ett.streampayload,
809                 &ett.congestioncontrol,
810                 &ett.nackdata,
811                 FEC_SUBTREE_ARRAY(ett.fec)
812         };
813
814         module_t *module;
815
816         /* Clear hf and ett fields */
817         memset(&hf, 0xff, sizeof(struct _norm_hf));
818         memset(&ett, 0xff, sizeof(struct _norm_ett));
819
820         /* Register the protocol name and description */
821         proto = proto_register_protocol("Negative-acknowledgment Oriented Reliable Multicast", "NORM", "norm");
822
823         /* Register the header fields and subtrees used */
824         proto_register_field_array(proto, hf_ptr, array_length(hf_ptr));
825         proto_register_subtree_array(ett_ptr, array_length(ett_ptr));
826
827         /* Reset preferences */
828         norm_prefs_set_default(&preferences);
829         norm_prefs_save(&preferences, &preferences_old);
830
831         /* Register preferences */
832         module = prefs_register_protocol(proto, proto_reg_handoff_norm);
833         norm_prefs_register(&preferences, module);
834         prefs_register_bool_preference(module, "heuristic_norm",
835                         "Try to decode UDP packets as NORM packets",
836                         "Check this to decode NORM traffic between clients",
837                         &global_norm_heur);
838
839 }