2 * Reliable Multicast Transport (RMT)
3 * NORM Protocol Instantiation dissector
4 * Copyright 2005, Stefano Pettini <spettini@users.sourceforge.net>
6 * Extensive changes to decode more information Julian Onions
8 * Negative-acknowledgment (NACK)-Oriented Reliable Multicast (NORM):
9 * ------------------------------------------------------------------
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.
19 * RFC 3940, Negative-acknowledgment (NACK)-Oriented Reliable Multicast (NORM) Protocol
23 * Wireshark - Network traffic analyzer
24 * By Gerald Combs <gerald@wireshark.org>
25 * Copyright 1998 Gerald Combs
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.
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.
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.
52 #include <epan/packet.h>
53 #include <epan/prefs.h>
54 #include <epan/strutil.h>
56 #include "packet-rmt-norm.h"
60 static const value_string string_norm_type[] =
62 { NORM_INFO, "INFO" },
63 { NORM_DATA, "DATA" },
65 { NORM_NACK, "NACK" },
67 { NORM_REPORT, "REPORT" },
71 static const value_string string_norm_cmd_type[] =
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" },
83 static const value_string string_norm_ack_type[] =
85 { NORM_ACK_CC, "ACK CC" },
86 { NORM_ACK_FLUSH, "ACK FLUSH" },
90 static const value_string string_norm_nack_form[] =
92 { NORM_NACK_ITEMS, "Items" },
93 { NORM_NACK_RANGES, "Ranges" },
94 { NORM_NACK_ERASURES, "Erasures" },
98 #define hdrlen2bytes(x) ((x)*4U)
100 /* Initialize the protocol and registered fields */
101 /* ============================================= */
103 static int proto = -1;
104 static gboolean global_norm_heur = FALSE;
106 static struct _norm_hf hf;
107 static struct _norm_ett ett;
109 static gboolean preferences_initialized = FALSE;
110 static struct _norm_prefs preferences;
111 static struct _norm_prefs preferences_old;
116 /* Set/Reset preferences to default values */
117 static void norm_prefs_set_default(struct _norm_prefs *prefs)
119 fec_prefs_set_default(&prefs->fec);
122 /* Register preferences */
123 static void norm_prefs_register(struct _norm_prefs *prefs, module_t *module)
125 fec_prefs_register(&prefs->fec, module);
128 /* Save preferences to alc_prefs_old */
129 static void norm_prefs_save(struct _norm_prefs *p, struct _norm_prefs *p_old)
134 static const double RTT_MIN = 1.0e-06;
135 static const double RTT_MAX = 1000;
137 static double UnquantizeRtt(unsigned char qrtt)
139 return ((qrtt <= 31) ? (((double)(qrtt+1))*(double)RTT_MIN) :
140 (RTT_MAX/exp(((double)(255-qrtt))/(double)13.0)));
143 static double UnquantizeGSize(guint8 gsize)
145 guint mant = (gsize & 0x8) ? 5 : 1;
146 guint exponent = gsize & 0x7;
148 return mant * pow(10, exponent);
151 static double UnquantizeSendRate(guint16 send_rate)
153 return (send_rate >> 4) * 10.0 / 4096.0 * pow(10.0, (send_rate & 0x000f));
156 /* code to dissect fairly common sequence in NORM packets */
157 static guint dissect_grrtetc(proto_tree *tree, tvbuff_t *tvb, guint offset)
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);
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)
180 f->prefs = &preferences.fec;
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++;
187 proto_tree_add_item(tree, hf.reserved, tvb, offset, 1, FALSE); offset++;
189 proto_tree_add_item(tree, hf.object_transport_id, tvb, offset, 2, FALSE); offset+=2;
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);
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_)
205 /* Allocate an array of _ext elements */
207 guint offset_old = offset;
208 proto_tree *ext_tree;
210 ext = g_array_new(FALSE, TRUE, sizeof(struct _ext));
212 rmt_ext_parse(ext, tvb, &offset, hdrlen2bytes(norm->hlen));
216 struct _lct_prefs lctp;
217 memset(&lctp, 0, sizeof(lctp));
220 /* Add the extensions subtree */
221 ti = proto_tree_add_uint(tree, hf.extension,
223 offset - offset_old, ext->len);
224 ext_tree = proto_item_add_subtree(ti, ett.hdrext);
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);
232 lct_ext_decode(e, &lctp, tvb, ext_tree, ett.hdrext, *f);
233 /* fec_decode_ext_fti(e, tvb, ext_tree, ett.hdrext, *f); */
236 g_array_free(ext, TRUE);
240 static guint dissect_nack_data(struct _norm *norm, proto_tree *tree,
241 tvbuff_t *tvb, guint offset, packet_info *pinfo)
243 proto_item *ti, *tif;
244 proto_tree *nack_tree, *flag_tree;
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;
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);
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);
262 dissect_feccode(norm, &f, nack_tree, tvb, offset, pinfo, 1);
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)
275 proto_tree *flag_tree;
278 offset = dissect_grrtetc(tree, tvb, offset);
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);
293 offset = dissect_feccode(norm, &f, tree, tvb, offset, pinfo, 0);
295 if (offset < hdrlen2bytes(norm->hlen)) {
296 offset = dissect_norm_hdrext(norm, &f, tree, tvb, offset, pinfo);
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;
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));
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_)
317 proto_tree *flag_tree;
319 offset = dissect_grrtetc(tree, tvb, offset);
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);
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;
338 if (offset < hdrlen2bytes(norm->hlen)) {
340 memset(&f, 0, sizeof f);
344 f.prefs = &preferences.fec;
345 offset = dissect_norm_hdrext(norm, &f, tree, tvb, offset, pinfo);
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));
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)
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);
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)
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;
370 if (offset < hdrlen2bytes(norm->hlen)) {
372 memset(&f, 0, sizeof f);
376 f.prefs = &preferences.fec;
377 offset = dissect_norm_hdrext(norm, &f, tree, tvb, offset, pinfo);
379 while (tvb_reported_length_remaining(tvb, offset) > 0) {
380 offset = dissect_nack_data(norm, tree, tvb, offset, pinfo);
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_)
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;
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)) {
396 memset(&f, 0, sizeof f);
400 f.prefs = &preferences.fec;
401 offset = dissect_norm_hdrext(norm, &f, tree, tvb, offset, pinfo);
403 while (tvb_reported_length_remaining(tvb, offset) > 0) {
404 proto_item *ti, *tif;
405 proto_tree *cc_tree, *flag_tree;
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);
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;
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)
431 offset = dissect_feccode(norm, &f, tree, tvb, offset, pinfo, 0);
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;
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_)
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;
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)
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 ++;
463 offset = dissect_norm_cmd_cc(norm, tree, tvb, offset, pinfo);
466 offset = dissect_norm_cmd_flush(norm, tree, tvb, offset, pinfo);
468 case NORM_CMD_SQUELCH:
469 offset = dissect_norm_cmd_squelch(norm, tree, tvb, offset, pinfo);
471 case NORM_CMD_REPAIR_ADV:
472 offset = dissect_norm_cmd_repairadv(norm, tree, tvb, offset, pinfo);
474 case NORM_CMD_ACK_REQ:
475 offset = dissect_norm_cmd_ackreq(norm, tree, tvb, offset, pinfo);
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));
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)
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)) {
500 memset(&f, 0, sizeof f);
504 f.prefs = &preferences.fec;
505 offset = dissect_norm_hdrext(norm, &f, tree, tvb, offset, pinfo);
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));
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)
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)) {
526 memset(&f, 0, sizeof f);
530 f.prefs = &preferences.fec;
531 offset = dissect_norm_hdrext(norm, &f, tree, tvb, offset, pinfo);
534 while (tvb_reported_length_remaining(tvb, offset) > 0) {
535 offset = dissect_nack_data(norm, tree, tvb, offset, pinfo);
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));
541 /* Code to actually dissect the packets */
542 /* ==================================== */
544 static void dissect_norm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
546 /* Logical packet representation */
549 /* Offset for subpacket dissection */
552 /* Set up structures needed to add the protocol subtree and manage it */
554 proto_tree *norm_tree;
556 /* Structures and variables initialization */
558 memset(&norm, 0, sizeof(struct _norm));
560 /* Update packet info */
561 pinfo->current_proto = "NORM";
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);
569 /* NORM header dissection, part 1 */
570 /* ------------------------------ */
572 norm.version = hi_nibble(tvb_get_guint8(tvb, offset));
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);
580 /* Fill the NORM subtree */
581 proto_tree_add_uint(norm_tree, hf.version, tvb, offset, 1, norm.version);
586 /* This dissector supports only NORMv1 packets.
587 * If norm.version > 1 print only version field and quit.
589 if (norm.version == 1) {
591 /* NORM header dissection, part 2 */
592 /* ------------------------------ */
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);
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);
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)"));
619 dissect_norm_info(&norm, norm_tree, tvb, offset, pinfo);
622 dissect_norm_data(&norm, norm_tree, tvb, offset, pinfo);
625 dissect_norm_cmd(&norm, norm_tree, tvb, offset, pinfo);
628 dissect_norm_ack(&norm, norm_tree, tvb, offset, pinfo);
631 dissect_norm_nack(&norm, norm_tree, tvb, offset, pinfo);
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));
643 proto_tree_add_text(norm_tree, tvb, 0, -1, "Sorry, this dissector supports NORM version 1 only");
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);
652 dissect_norm_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
655 if (!global_norm_heur)
657 if (!tvb_bytes_exist(tvb, 0, 2))
658 return FALSE; /* not enough to check */
659 byte1 = tvb_get_guint8(tvb, 0);
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)
666 dissect_norm(tvb, pinfo, tree);
667 return TRUE; /* appears to be a NORM packet */
670 void proto_reg_handoff_norm(void)
672 static dissector_handle_t handle;
674 if (!preferences_initialized)
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);
682 norm_prefs_save(&preferences, &preferences_old);
685 void proto_register_norm(void)
687 /* Setup NORM header fields */
688 static hf_register_info hf_ptr[] = {
691 { "Version", "norm.version", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
693 { "Message Type", "norm.type", FT_UINT8, BASE_DEC, VALS(string_norm_type), 0x0, "", HFILL }},
695 { "Header length", "norm.hlen", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
697 { "Sequence", "norm.sequence", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
699 { "Source ID", "norm.source_id", FT_IPv4, BASE_NONE, NULL, 0x0, "", HFILL }},
701 { "Instance", "norm.instance_id", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL}},
703 { "grtt", "norm.grtt", FT_DOUBLE, BASE_DEC, NULL, 0x0, "", HFILL}},
705 { "Backoff", "norm.backoff", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL}},
707 { "Group Size", "norm.gsize", FT_DOUBLE, BASE_DEC, NULL, 0x0, "", HFILL}},
709 { "Flags", "norm.flags", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL}},
711 { "Repair Flag", "norm.flag.repair", FT_BOOLEAN, 8, NULL, NORM_FLAG_REPAIR, "", HFILL }},
713 { "Explicit Flag", "norm.flag.explicit", FT_BOOLEAN, 8, NULL, NORM_FLAG_EXPLICIT, "", HFILL }},
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 }},
719 { "File Flag", "norm.flag.file", FT_BOOLEAN, 8, NULL, NORM_FLAG_FILE, "", HFILL }},
721 { "Stream Flag", "norm.flag.stream", FT_BOOLEAN, 8, NULL, NORM_FLAG_STREAM, "", HFILL }},
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}},
727 { "Hdr Extension", "norm.hexext", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL}},
729 { "Reserved", "norm.reserved", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL}},
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}},
736 { "Flavor", "norm.flavor", FT_UINT8, BASE_DEC, VALS(string_norm_cmd_type), 0x0, "", HFILL}},
738 { "CC Sequence", "norm.ccsequence", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL}},
740 { "Send Time secs", "norm.cc_sts", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL}},
742 { "Send Time usecs", "norm.cc_stus", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL}},
744 { "CC Node ID", "norm.cc_node_id", FT_IPv4, BASE_NONE, NULL, 0x0, "", HFILL}},
746 { "CC Flags", "norm.cc_flags", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL}},
748 { "CLR", "norm.cc_flags.clr", FT_BOOLEAN, 8, NULL, NORM_FLAG_CC_CLR, "", HFILL}},
750 { "PLR", "norm.cc_flags.plr", FT_BOOLEAN, 8, NULL, NORM_FLAG_CC_PLR, "", HFILL}},
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}},
758 { "CC RTT", "norm.cc_rtt", FT_DOUBLE, BASE_DEC, NULL, 0x0, "", HFILL}},
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}},
765 { "Ack Source", "norm.ack.source", FT_IPv4, BASE_DEC, NULL, 0x0, "", HFILL}},
767 { "Ack Type", "norm.ack.type", FT_UINT8, BASE_DEC, VALS(string_norm_ack_type), 0x0, "", HFILL}},
769 { "Ack ID", "norm.ack.id", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL}},
771 { "Ack GRTT Sec", "norm.ack.grtt_sec", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL}},
773 { "Ack GRTT usec", "norm.ack.grtt_usec", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL}},
776 { "NAck Server", "norm.nack.server", FT_IPv4, BASE_DEC, NULL, 0x0, "", HFILL}},
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}},
782 { "NAck FORM", "norm.nack.form", FT_UINT8, BASE_DEC, VALS(string_norm_nack_form), 0x0, "", HFILL}},
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}},
794 { "NAck Length", "norm.nack.length", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL}},
797 FEC_FIELD_ARRAY(hf.fec, "NORM"),
800 { "Payload", "norm.payload", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }}
803 /* Setup protocol subtree array */
804 static gint *ett_ptr[] = {
809 &ett.congestioncontrol,
811 FEC_SUBTREE_ARRAY(ett.fec)
816 /* Clear hf and ett fields */
817 memset(&hf, 0xff, sizeof(struct _norm_hf));
818 memset(&ett, 0xff, sizeof(struct _norm_ett));
820 /* Register the protocol name and description */
821 proto = proto_register_protocol("Negative-acknowledgment Oriented Reliable Multicast", "NORM", "norm");
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));
827 /* Reset preferences */
828 norm_prefs_set_default(&preferences);
829 norm_prefs_save(&preferences, &preferences_old);
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",