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
21 * Wireshark - Network traffic analyzer
22 * By Gerald Combs <gerald@wireshark.org>
23 * Copyright 1998 Gerald Combs
25 * SPDX-License-Identifier: GPL-2.0-or-later
32 #include <epan/packet.h>
33 #include <epan/prefs.h>
34 #include <epan/expert.h>
35 #include <epan/proto_data.h>
36 #include "packet-rmt-common.h"
38 void proto_register_norm(void);
39 void proto_reg_handoff_norm(void);
50 static const value_string string_norm_type[] =
52 { NORM_INFO, "INFO" },
53 { NORM_DATA, "DATA" },
55 { NORM_NACK, "NACK" },
57 { NORM_REPORT, "REPORT" },
61 #define NORM_CMD_FLUSH 1
62 #define NORM_CMD_EOT 2
63 #define NORM_CMD_SQUELCH 3
65 #define NORM_CMD_REPAIR_ADV 5
66 #define NORM_CMD_ACK_REQ 6
67 #define NORM_CMD_APPLICATION 7
69 static const value_string string_norm_cmd_type[] =
71 { NORM_CMD_FLUSH, "FLUSH" },
72 { NORM_CMD_EOT, "EOT" },
73 { NORM_CMD_SQUELCH, "SQUELCH" },
74 { NORM_CMD_CC, "CC" },
75 { NORM_CMD_REPAIR_ADV, "REPAIR_ADV" },
76 { NORM_CMD_ACK_REQ, "ACK_REQ" },
77 { NORM_CMD_APPLICATION, "APPLICATION" },
82 #define NORM_ACK_FLUSH 2
84 static const value_string string_norm_ack_type[] =
86 { NORM_ACK_CC, "ACK CC" },
87 { NORM_ACK_FLUSH, "ACK FLUSH" },
91 #define NORM_NACK_ITEMS 1
92 #define NORM_NACK_RANGES 2
93 #define NORM_NACK_ERASURES 3
95 static const value_string string_norm_nack_form[] =
97 { NORM_NACK_ITEMS, "Items" },
98 { NORM_NACK_RANGES, "Ranges" },
99 { NORM_NACK_ERASURES, "Erasures" },
103 #define NORM_FLAG_REPAIR 0x01
104 #define NORM_FLAG_EXPLICIT 0x02
105 #define NORM_FLAG_INFO 0x04
106 #define NORM_FLAG_UNRELIABLE 0x08
107 #define NORM_FLAG_FILE 0x10
108 #define NORM_FLAG_STREAM 0x20
109 #define NORM_FLAG_MSG_START 0x40
111 #define NORM_NACK_SEGMENT 0x01
112 #define NORM_NACK_BLOCK 0x02
113 #define NORM_NACK_INFO 0x04
114 #define NORM_NACK_OBJECT 0x08
116 #define NORM_FLAG_CC_CLR 0x01
117 #define NORM_FLAG_CC_PLR 0x02
118 #define NORM_FLAG_CC_RTT 0x04
119 #define NORM_FLAG_CC_START 0x08
120 #define NORM_FLAG_CC_LEAVE 0x10
122 #define hdrlen2bytes(x) ((x)*4U)
124 typedef struct norm_packet_data
127 } norm_packet_data_t;
129 /* Initialize the protocol and registered fields */
130 /* ============================================= */
131 static dissector_handle_t rmt_fec_handle;
133 static int proto_rmt_norm = -1;
135 static int hf_version = -1;
136 static int hf_type = -1;
137 static int hf_hlen = -1;
138 static int hf_sequence = -1;
139 static int hf_source_id = -1;
140 static int hf_instance_id = -1;
141 static int hf_grtt = -1;
142 static int hf_backoff = -1;
143 static int hf_gsize = -1;
144 static int hf_flags = -1;
145 static int hf_flag_repair = -1;
146 static int hf_flag_norm_explicit = -1;
147 static int hf_flag_info = -1;
148 static int hf_flag_unreliable = -1;
149 static int hf_flag_file = -1;
150 static int hf_flag_stream = -1;
151 static int hf_flag_msgstart = -1;
152 static int hf_object_transport_id = -1;
153 static int hf_extension = -1;
154 static int hf_reserved = -1;
155 static int hf_payload_len = -1;
156 static int hf_payload_offset = -1;
157 static int hf_cmd_flavor = -1;
158 static int hf_cc_sequence = -1;
159 static int hf_cc_sts = -1;
160 static int hf_cc_stus = -1;
161 static int hf_cc_node_id = -1;
162 static int hf_cc_flags = -1;
163 static int hf_cc_flags_clr = -1;
164 static int hf_cc_flags_plr = -1;
165 static int hf_cc_flags_rtt = -1;
166 static int hf_cc_flags_start = -1;
167 static int hf_cc_flags_leave = -1;
168 static int hf_cc_rtt = -1;
169 static int hf_cc_rate = -1;
170 static int hf_cc_transport_id = -1;
171 static int hf_ack_source = -1;
172 static int hf_ack_type = -1;
173 static int hf_ack_id = -1;
174 static int hf_ack_grtt_sec = -1;
175 static int hf_ack_grtt_usec = -1;
176 static int hf_nack_server = -1;
177 static int hf_nack_grtt_sec = -1;
178 static int hf_nack_grtt_usec = -1;
179 static int hf_nack_form = -1;
180 static int hf_nack_flags = -1;
181 static int hf_nack_flags_segment = -1;
182 static int hf_nack_flags_block = -1;
183 static int hf_nack_flags_info = -1;
184 static int hf_nack_flags_object = -1;
185 static int hf_nack_length = -1;
186 static int hf_payload = -1;
187 static int hf_fec_encoding_id = -1;
189 static int ett_main = -1;
190 static int ett_hdrext = -1;
191 static int ett_flags = -1;
192 static int ett_streampayload = -1;
193 static int ett_congestioncontrol = -1;
194 static int ett_nackdata = -1;
196 static expert_field ei_version1_only = EI_INIT;
198 static const double RTT_MIN = 1.0e-06;
199 static const double RTT_MAX = 1000;
201 static double UnquantizeRtt(unsigned char qrtt)
203 return ((qrtt <= 31) ? (((double)(qrtt+1))*(double)RTT_MIN) :
204 (RTT_MAX/exp(((double)(255-qrtt))/(double)13.0)));
207 static double UnquantizeGSize(guint8 gsizex)
209 guint mant = (gsizex & 0x8) ? 5 : 1;
210 guint exponent = gsizex & 0x7;
213 return mant * pow(10, exponent);
216 /* code to dissect fairly common sequence in NORM packets */
217 static guint dissect_grrtetc(proto_tree *tree, tvbuff_t *tvb, guint offset)
223 proto_tree_add_item(tree, hf_instance_id, tvb, offset, 2, ENC_BIG_ENDIAN); offset+=2;
224 grtt = UnquantizeRtt(tvb_get_guint8(tvb, offset));
225 proto_tree_add_double(tree, hf_grtt, tvb, offset, 1, grtt); offset += 1;
226 backoff = hi_nibble(tvb_get_guint8(tvb, offset));
227 gsizex = UnquantizeGSize((guint8)lo_nibble(tvb_get_guint8(tvb, offset)));
228 proto_tree_add_uint(tree, hf_backoff, tvb, offset, 1, backoff);
229 proto_tree_add_double(tree, hf_gsize, tvb, offset, 1, gsizex);
234 /* split out some common FEC handling */
235 static guint dissect_feccode(proto_tree *tree, tvbuff_t *tvb, guint offset,
236 packet_info *pinfo, gint reserved)
238 norm_packet_data_t *norm_data;
239 guint8 encoding_id = tvb_get_guint8(tvb, offset);
241 /* Save encoding ID */
242 norm_data = wmem_new0(wmem_file_scope(), norm_packet_data_t);
243 norm_data->encoding_id = encoding_id;
245 p_add_proto_data(wmem_file_scope(), pinfo, proto_rmt_norm, 0, norm_data);
247 proto_tree_add_item(tree, hf_fec_encoding_id, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1;
249 proto_tree_add_item(tree, hf_reserved, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1;
251 proto_tree_add_item(tree, hf_object_transport_id, tvb, offset, 2, ENC_BIG_ENDIAN); offset+=2;
253 if (tvb_reported_length_remaining(tvb, offset) > 0) {
254 fec_data_exchange_t fec;
258 new_tvb = tvb_new_subset_remaining(tvb, offset);
260 fec.encoding_id = encoding_id;
261 len = call_dissector_with_data(rmt_fec_handle, new_tvb, pinfo, tree, &fec);
269 static guint dissect_norm_hdrext(proto_tree *tree, packet_info *pinfo,
270 tvbuff_t *tvb, guint offset, guint8 hlen)
272 lct_data_exchange_t data_exchange;
273 norm_packet_data_t *packet_data = (norm_packet_data_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_rmt_norm, 0);
275 memset(&data_exchange, 0, sizeof(data_exchange));
277 if (packet_data != NULL)
278 data_exchange.codepoint = packet_data->encoding_id;
280 offset += lct_ext_decode(tree, tvb, pinfo, offset, hdrlen2bytes(hlen), &data_exchange,
281 hf_extension, ett_hdrext);
286 static guint dissect_nack_data(proto_tree *tree, tvbuff_t *tvb, guint offset,
289 proto_item *ti, *tif;
290 proto_tree *nack_tree, *flag_tree;
293 nack_tree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_nackdata, &ti, "NACK Data");
294 proto_tree_add_item(nack_tree, hf_nack_form, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1;
296 tif = proto_tree_add_item(nack_tree, hf_nack_flags, tvb, offset, 1, ENC_BIG_ENDIAN);
297 flag_tree = proto_item_add_subtree(tif, ett_flags);
298 proto_tree_add_item(flag_tree, hf_nack_flags_segment, tvb, offset, 1, ENC_BIG_ENDIAN);
299 proto_tree_add_item(flag_tree, hf_nack_flags_block, tvb, offset, 1, ENC_BIG_ENDIAN);
300 proto_tree_add_item(flag_tree, hf_nack_flags_info, tvb, offset, 1, ENC_BIG_ENDIAN);
301 proto_tree_add_item(flag_tree, hf_nack_flags_object, tvb, offset, 1, ENC_BIG_ENDIAN);
303 len = tvb_get_ntohs(tvb, offset);
304 proto_tree_add_item(nack_tree, hf_nack_length, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2;
305 proto_item_set_len(ti, 4+len);
307 dissect_feccode(nack_tree, tvb, offset, pinfo, 1);
313 /* code to dissect NORM data packets */
314 static void dissect_norm_data(proto_tree *tree, packet_info *pinfo,
315 tvbuff_t *tvb, guint offset, guint8 hlen)
319 proto_tree *flag_tree;
321 offset = dissect_grrtetc(tree, tvb, offset);
323 ti = proto_tree_add_item(tree, hf_flags, tvb, offset, 1, ENC_BIG_ENDIAN);
324 flags = tvb_get_guint8(tvb, offset);
325 flag_tree = proto_item_add_subtree(ti, ett_flags);
326 proto_tree_add_item(flag_tree, hf_flag_repair, tvb, offset, 1, ENC_BIG_ENDIAN);
327 proto_tree_add_item(flag_tree, hf_flag_norm_explicit, tvb, offset, 1, ENC_BIG_ENDIAN);
328 proto_tree_add_item(flag_tree, hf_flag_info, tvb, offset, 1, ENC_BIG_ENDIAN);
329 proto_tree_add_item(flag_tree, hf_flag_unreliable, tvb, offset, 1, ENC_BIG_ENDIAN);
330 proto_tree_add_item(flag_tree, hf_flag_file, tvb, offset, 1, ENC_BIG_ENDIAN);
331 proto_tree_add_item(flag_tree, hf_flag_stream, tvb, offset, 1, ENC_BIG_ENDIAN);
332 proto_tree_add_item(flag_tree, hf_flag_msgstart, tvb, offset, 1, ENC_BIG_ENDIAN);
335 offset = dissect_feccode(tree, tvb, offset, pinfo, 0);
337 if (offset < hdrlen2bytes(hlen)) {
338 offset = dissect_norm_hdrext(tree, pinfo, tvb, offset, hlen);
340 if (flags & NORM_FLAG_STREAM) {
341 flag_tree = proto_tree_add_subtree(tree, tvb, offset, 8, ett_streampayload, NULL, "Stream Data");
342 proto_tree_add_item(flag_tree, hf_reserved, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2;
343 proto_tree_add_item(flag_tree, hf_payload_len, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2;
344 proto_tree_add_item(flag_tree, hf_payload_offset, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4;
347 if (tvb_reported_length_remaining(tvb, offset) > 0)
348 proto_tree_add_item(tree, hf_payload, tvb, offset, -1, ENC_NA);
351 /* code to dissect NORM info packets */
352 static void dissect_norm_info(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, guint offset, guint8 hlen)
355 proto_tree *flag_tree;
356 norm_packet_data_t *norm_data;
358 offset = dissect_grrtetc(tree, tvb, offset);
360 ti = proto_tree_add_item(tree, hf_flags, tvb, offset, 1, ENC_BIG_ENDIAN);
361 flag_tree = proto_item_add_subtree(ti, ett_flags);
362 proto_tree_add_item(flag_tree, hf_flag_repair, tvb, offset, 1, ENC_BIG_ENDIAN);
363 proto_tree_add_item(flag_tree, hf_flag_norm_explicit, tvb, offset, 1, ENC_BIG_ENDIAN);
364 proto_tree_add_item(flag_tree, hf_flag_info, tvb, offset, 1, ENC_BIG_ENDIAN);
365 proto_tree_add_item(flag_tree, hf_flag_unreliable, tvb, offset, 1, ENC_BIG_ENDIAN);
366 proto_tree_add_item(flag_tree, hf_flag_file, tvb, offset, 1, ENC_BIG_ENDIAN);
367 proto_tree_add_item(flag_tree, hf_flag_stream, tvb, offset, 1, ENC_BIG_ENDIAN);
368 proto_tree_add_item(flag_tree, hf_flag_msgstart, tvb, offset, 1, ENC_BIG_ENDIAN);
371 /* Save encoding ID */
372 norm_data = wmem_new0(wmem_file_scope(), norm_packet_data_t);
373 norm_data->encoding_id = tvb_get_guint8(tvb, offset);
375 p_add_proto_data(wmem_file_scope(), pinfo, proto_rmt_norm, 0, norm_data);
377 proto_tree_add_item(tree, hf_fec_encoding_id, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1;
378 proto_tree_add_item(tree, hf_object_transport_id, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2;
380 if (offset < hdrlen2bytes(hlen)) {
381 offset = dissect_norm_hdrext(tree, pinfo, tvb, offset, hlen);
383 if (tvb_reported_length_remaining(tvb, offset) > 0)
384 proto_tree_add_item(tree, hf_payload, tvb, offset, -1, ENC_NA);
387 /* code to dissect NORM cmd(flush) packets */
388 static guint dissect_norm_cmd_flush(proto_tree *tree, packet_info *pinfo,
389 tvbuff_t *tvb, guint offset, guint8 hlen)
391 offset = dissect_feccode(tree, tvb, offset, pinfo, 0);
392 if (offset < hdrlen2bytes(hlen)) {
393 offset = dissect_norm_hdrext(tree, pinfo, tvb, offset, hlen);
398 /* code to dissect NORM cmd(flush) packets */
399 static guint dissect_norm_cmd_repairadv(proto_tree *tree, packet_info *pinfo,
400 tvbuff_t *tvb, guint offset, guint8 hlen)
402 proto_tree_add_item(tree, hf_flags, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1;
403 proto_tree_add_item(tree, hf_reserved, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2;
405 if (offset < hdrlen2bytes(hlen)) {
406 offset = dissect_norm_hdrext(tree, pinfo, tvb, offset, hlen);
408 while (tvb_reported_length_remaining(tvb, offset) > 0) {
409 offset = dissect_nack_data(tree, tvb, offset, pinfo);
414 /* code to dissect NORM cmd(cc) packets */
415 static guint dissect_norm_cmd_cc(proto_tree *tree, packet_info *pinfo,
416 tvbuff_t *tvb, guint offset, guint8 hlen)
418 proto_tree_add_item(tree, hf_reserved, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1;
419 proto_tree_add_item(tree, hf_cc_sequence, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2;
421 proto_tree_add_item(tree, hf_cc_sts, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4;
422 proto_tree_add_item(tree, hf_cc_stus, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4;
423 if (offset < hdrlen2bytes(hlen)) {
424 offset = dissect_norm_hdrext(tree, pinfo, tvb, offset, hlen);
426 while (offset < hdrlen2bytes(hlen)) {
428 proto_tree *cc_tree, *flag_tree;
430 cc_tree = proto_tree_add_subtree(tree, tvb, offset, 8, ett_congestioncontrol, NULL, "Congestion Control");
431 proto_tree_add_item(cc_tree, hf_cc_node_id, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4;
432 tif = proto_tree_add_item(cc_tree, hf_cc_flags, tvb, offset, 1, ENC_BIG_ENDIAN);
433 flag_tree = proto_item_add_subtree(tif, ett_flags);
434 proto_tree_add_item(flag_tree, hf_cc_flags_clr, tvb, offset, 1, ENC_BIG_ENDIAN);
435 proto_tree_add_item(flag_tree, hf_cc_flags_plr, tvb, offset, 1, ENC_BIG_ENDIAN);
436 proto_tree_add_item(flag_tree, hf_cc_flags_rtt, tvb, offset, 1, ENC_BIG_ENDIAN);
437 proto_tree_add_item(flag_tree, hf_cc_flags_start, tvb, offset, 1, ENC_BIG_ENDIAN);
438 proto_tree_add_item(flag_tree, hf_cc_flags_leave, tvb, offset, 1, ENC_BIG_ENDIAN);
440 grtt = UnquantizeRtt(tvb_get_guint8(tvb, offset));
441 proto_tree_add_double(cc_tree, hf_cc_rtt, tvb, offset, 1, grtt); offset += 1;
442 grtt = rmt_decode_send_rate(tvb_get_ntohs(tvb, offset));
443 proto_tree_add_double(cc_tree, hf_cc_rate, tvb, offset, 2, grtt); offset += 2;
448 /* code to dissect NORM cmd(squelch) packets */
449 static guint dissect_norm_cmd_squelch(proto_tree *tree, packet_info *pinfo,
450 tvbuff_t *tvb, guint offset)
452 offset = dissect_feccode(tree, tvb, offset, pinfo, 0);
454 while (tvb_reported_length_remaining(tvb, offset) > 0) {
455 proto_tree_add_item(tree, hf_cc_transport_id, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 2;
460 /* code to dissect NORM cmd(squelch) packets */
461 static guint dissect_norm_cmd_ackreq(proto_tree *tree, packet_info *pinfo _U_,
462 tvbuff_t *tvb, guint offset)
464 proto_tree_add_item(tree, hf_reserved, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1;
465 proto_tree_add_item(tree, hf_ack_type, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1;
466 proto_tree_add_item(tree, hf_ack_id, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1;
470 /* code to dissect NORM cmd packets */
471 static void dissect_norm_cmd(proto_tree *tree, packet_info *pinfo,
472 tvbuff_t *tvb, guint offset, guint8 hlen)
476 offset = dissect_grrtetc(tree, tvb, offset);
477 flavor = tvb_get_guint8(tvb, offset);
479 col_append_sep_str(pinfo->cinfo, COL_INFO, " ",
480 val_to_str(flavor, string_norm_cmd_type, "Unknown Cmd Type (0x%04x)"));
481 proto_tree_add_item(tree, hf_cmd_flavor, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1;
484 offset = dissect_norm_cmd_cc(tree, pinfo, tvb, offset, hlen);
487 offset = dissect_norm_cmd_flush(tree, pinfo, tvb, offset, hlen);
489 case NORM_CMD_SQUELCH:
490 offset = dissect_norm_cmd_squelch(tree, pinfo, tvb, offset);
492 case NORM_CMD_REPAIR_ADV:
493 offset = dissect_norm_cmd_repairadv(tree, pinfo, tvb, offset, hlen);
495 case NORM_CMD_ACK_REQ:
496 offset = dissect_norm_cmd_ackreq(tree, pinfo, tvb, offset);
499 if (tvb_reported_length_remaining(tvb, offset) > 0)
500 proto_tree_add_item(tree, hf_payload, tvb, offset, -1, ENC_NA);
503 /* code to dissect NORM ack packets */
504 static void dissect_norm_ack(proto_tree *tree, packet_info *pinfo,
505 tvbuff_t *tvb, guint offset, guint8 hlen)
509 proto_tree_add_item(tree, hf_ack_source, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4;
510 proto_tree_add_item(tree, hf_instance_id, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2;
511 acktype = tvb_get_guint8(tvb, offset);
513 col_append_sep_str(pinfo->cinfo, COL_INFO, " ",
514 val_to_str(acktype, string_norm_ack_type, "Unknown Ack Type (0x%04x)"));
515 proto_tree_add_item(tree, hf_ack_type, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1;
516 proto_tree_add_item(tree, hf_ack_id, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1;
517 proto_tree_add_item(tree, hf_ack_grtt_sec, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4;
518 proto_tree_add_item(tree, hf_ack_grtt_usec, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4;
519 if (offset < hdrlen2bytes(hlen)) {
520 offset = dissect_norm_hdrext(tree, pinfo, tvb, offset, hlen);
523 if (tvb_reported_length_remaining(tvb, offset) > 0)
524 proto_tree_add_item(tree, hf_payload, tvb, offset, -1, ENC_NA);
527 /* code to dissect NORM nack packets */
528 static void dissect_norm_nack(proto_tree *tree, packet_info *pinfo,
529 tvbuff_t *tvb, guint offset, guint8 hlen)
531 proto_tree_add_item(tree, hf_nack_server, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4;
532 proto_tree_add_item(tree, hf_instance_id, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2;
533 proto_tree_add_item(tree, hf_reserved, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2;
534 proto_tree_add_item(tree, hf_nack_grtt_sec, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4;
535 proto_tree_add_item(tree, hf_nack_grtt_usec, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4;
536 if (offset < hdrlen2bytes(hlen)) {
537 offset = dissect_norm_hdrext(tree, pinfo, tvb, offset, hlen);
540 while (tvb_reported_length_remaining(tvb, offset) > 0) {
541 offset = dissect_nack_data(tree, tvb, offset, pinfo);
543 if (tvb_reported_length_remaining(tvb, offset) > 0)
544 proto_tree_add_item(tree, hf_payload, tvb, offset, -1, ENC_NA);
547 /* Code to actually dissect the packets */
548 /* ==================================== */
550 dissect_norm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
552 /* Logical packet representation */
557 /* Offset for subpacket dissection */
560 /* Set up structures needed to add the protocol subtree and manage it */
562 proto_tree *norm_tree;
564 /* Make entries in Protocol column and Info column on summary display */
565 col_set_str(pinfo->cinfo, COL_PROTOCOL, "NORM");
566 col_clear(pinfo->cinfo, COL_INFO);
568 /* NORM header dissection, part 1 */
569 /* ------------------------------ */
571 version = hi_nibble(tvb_get_guint8(tvb, offset));
573 /* Create subtree for the NORM protocol */
574 ti = proto_tree_add_item(tree, proto_rmt_norm, tvb, offset, -1, ENC_NA);
575 norm_tree = proto_item_add_subtree(ti, ett_main);
577 /* Fill the NORM subtree */
578 proto_tree_add_uint(norm_tree, hf_version, tvb, offset, 1, version);
580 /* This dissector supports only NORMv1 packets.
581 * If version > 1 print only version field and quit.
584 expert_add_info(pinfo, ti, &ei_version1_only);
586 /* Complete entry in Info column on summary display */
587 col_add_fstr(pinfo->cinfo, COL_INFO, "Version: %u (not supported)", version);
591 /* NORM header dissection, part 2 */
592 /* ------------------------------ */
594 type = lo_nibble(tvb_get_guint8(tvb, offset));
595 hlen = tvb_get_guint8(tvb, offset+1);
598 proto_tree_add_uint(norm_tree, hf_type, tvb, offset, 1, type);
599 proto_tree_add_item(norm_tree, hf_hlen, tvb, offset+1, 1, ENC_BIG_ENDIAN);
600 proto_tree_add_item(norm_tree, hf_sequence, tvb, offset+2, 2, ENC_BIG_ENDIAN);
601 proto_tree_add_item(norm_tree, hf_source_id, tvb, offset+4, 4, ENC_BIG_ENDIAN);
607 /* Complete entry in Info column on summary display */
608 /* ------------------------------------------------ */
609 col_append_sep_str(pinfo->cinfo, COL_INFO, " ",
610 val_to_str(type, string_norm_type, "Unknown Type (0x%04x)"));
615 dissect_norm_info(norm_tree, pinfo, tvb, offset, hlen);
618 dissect_norm_data(norm_tree, pinfo, tvb, offset, hlen);
621 dissect_norm_cmd(norm_tree, pinfo, tvb, offset, hlen);
624 dissect_norm_ack(norm_tree, pinfo, tvb, offset, hlen);
627 dissect_norm_nack(norm_tree, pinfo, tvb, offset, hlen);
630 /* Add the Payload item */
631 if (tvb_reported_length_remaining(tvb, offset) > 0)
632 proto_tree_add_item(norm_tree, hf_payload, tvb, offset, -1, ENC_NA);
636 return tvb_reported_length(tvb);
640 dissect_norm_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
644 if (tvb_reported_length(tvb) < 12)
645 return FALSE; /* not enough to check */
646 byte1 = tvb_get_guint8(tvb, 0);
648 if (hi_nibble(byte1) != 1) return FALSE;
649 if (lo_nibble(byte1) < 1 || lo_nibble(byte1) > 6) return FALSE;
650 if (tvb_get_guint8(tvb, 1) > 20) return FALSE;
652 dissect_norm(tvb, pinfo, tree, data);
653 return TRUE; /* appears to be a NORM packet */
656 void proto_register_norm(void)
658 /* Setup NORM header fields */
659 static hf_register_info hf[] = {
662 { "Version", "norm.version",
663 FT_UINT8, BASE_DEC, NULL, 0x0,
667 { "Message Type", "norm.type",
668 FT_UINT8, BASE_DEC, VALS(string_norm_type), 0x0,
672 { "Header length", "norm.hlen",
673 FT_UINT8, BASE_DEC, NULL, 0x0,
677 { "Sequence", "norm.sequence",
678 FT_UINT16, BASE_DEC, NULL, 0x0,
682 { "Source ID", "norm.source_id",
683 FT_IPv4, BASE_NONE, NULL, 0x0,
687 { "Instance", "norm.instance_id",
688 FT_UINT16, BASE_DEC, NULL, 0x0,
692 { "grtt", "norm.grtt",
693 FT_DOUBLE, BASE_NONE, NULL, 0x0,
697 { "Backoff", "norm.backoff",
698 FT_UINT8, BASE_DEC, NULL, 0x0,
702 { "Group Size", "norm.gsize",
703 FT_DOUBLE, BASE_NONE, NULL, 0x0,
707 { "Flags", "norm.flags",
708 FT_UINT8, BASE_HEX, NULL, 0x0,
712 { "Repair Flag", "norm.flag.repair",
713 FT_BOOLEAN, 8, NULL, NORM_FLAG_REPAIR,
716 { &hf_flag_norm_explicit,
717 { "Explicit Flag", "norm.flag.explicit",
718 FT_BOOLEAN, 8, NULL, NORM_FLAG_EXPLICIT,
722 { "Info Flag", "norm.flag.info",
723 FT_BOOLEAN, 8, NULL, NORM_FLAG_INFO,
726 { &hf_flag_unreliable,
727 { "Unreliable Flag", "norm.flag.unreliable",
728 FT_BOOLEAN, 8, NULL, NORM_FLAG_UNRELIABLE,
732 { "File Flag", "norm.flag.file",
733 FT_BOOLEAN, 8, NULL, NORM_FLAG_FILE,
737 { "Stream Flag", "norm.flag.stream",
738 FT_BOOLEAN, 8, NULL, NORM_FLAG_STREAM,
742 { "Msg Start Flag", "norm.flag.msgstart",
743 FT_BOOLEAN, 8, NULL, NORM_FLAG_MSG_START,
746 { &hf_object_transport_id,
747 { "Object Transport ID", "norm.object_transport_id",
748 FT_UINT16, BASE_HEX, NULL, 0x0,
752 { "Hdr Extension", "norm.hexext",
753 FT_UINT16, BASE_DEC, NULL, 0x0,
757 { "Reserved", "norm.reserved",
758 FT_UINT16, BASE_HEX, NULL, 0x0,
762 { "Payload Len", "norm.payload.len",
763 FT_UINT16, BASE_DEC, NULL, 0x0,
766 { &hf_payload_offset,
767 { "Payload Offset", "norm.payload.offset",
768 FT_UINT32, BASE_DEC, NULL, 0x0,
773 { "Flavor", "norm.flavor",
774 FT_UINT8, BASE_DEC, VALS(string_norm_cmd_type), 0x0,
778 { "CC Sequence", "norm.ccsequence",
779 FT_UINT16, BASE_DEC, NULL, 0x0,
783 { "Send Time secs", "norm.cc_sts",
784 FT_UINT32, BASE_DEC, NULL, 0x0,
788 { "Send Time usecs", "norm.cc_stus",
789 FT_UINT32, BASE_DEC, NULL, 0x0,
793 { "CC Node ID", "norm.cc_node_id",
794 FT_IPv4, BASE_NONE, NULL, 0x0,
798 { "CC Flags", "norm.cc_flags",
799 FT_UINT8, BASE_DEC, NULL, 0x0,
803 { "CLR", "norm.cc_flags.clr",
804 FT_BOOLEAN, 8, NULL, NORM_FLAG_CC_CLR,
808 { "PLR", "norm.cc_flags.plr",
809 FT_BOOLEAN, 8, NULL, NORM_FLAG_CC_PLR,
813 { "RTT", "norm.cc_flags.rtt",
814 FT_BOOLEAN, 8, NULL, NORM_FLAG_CC_RTT,
817 { &hf_cc_flags_start,
818 { "Start", "norm.cc_flags.start",
819 FT_BOOLEAN, 8, NULL, NORM_FLAG_CC_START,
822 { &hf_cc_flags_leave,
823 { "Leave", "norm.cc_flags.leave",
824 FT_BOOLEAN, 8, NULL, NORM_FLAG_CC_LEAVE,
828 { "CC RTT", "norm.cc_rtt",
829 FT_DOUBLE, BASE_NONE, NULL, 0x0,
833 { "CC Rate", "norm.cc_rate",
834 FT_DOUBLE, BASE_NONE, NULL, 0x0,
837 { &hf_cc_transport_id,
838 { "CC Transport ID", "norm.cc_transport_id",
839 FT_UINT16, BASE_DEC, NULL, 0x0,
844 { "Ack Source", "norm.ack.source",
845 FT_IPv4, BASE_NONE, NULL, 0x0,
849 { "Ack Type", "norm.ack.type",
850 FT_UINT8, BASE_DEC, VALS(string_norm_ack_type), 0x0,
854 { "Ack ID", "norm.ack.id",
855 FT_UINT8, BASE_DEC, NULL, 0x0,
859 { "Ack GRTT Sec", "norm.ack.grtt_sec",
860 FT_UINT32, BASE_DEC, NULL, 0x0,
864 { "Ack GRTT usec", "norm.ack.grtt_usec",
865 FT_UINT32, BASE_DEC, NULL, 0x0,
870 { "NAck Server", "norm.nack.server",
871 FT_IPv4, BASE_NONE, NULL, 0x0,
875 { "NAck GRTT Sec", "norm.nack.grtt_sec",
876 FT_UINT32, BASE_DEC, NULL, 0x0,
879 { &hf_nack_grtt_usec,
880 { "NAck GRTT usec", "norm.nack.grtt_usec",
881 FT_UINT32, BASE_DEC, NULL, 0x0,
885 { "NAck FORM", "norm.nack.form",
886 FT_UINT8, BASE_DEC, VALS(string_norm_nack_form), 0x0,
890 { "NAck Flags", "norm.nack.flags",
891 FT_UINT8, BASE_DEC, NULL, 0x0,
894 { &hf_nack_flags_segment,
895 { "Segment", "norm.nack.flags.segment",
896 FT_BOOLEAN, 8, NULL, NORM_NACK_SEGMENT,
899 { &hf_nack_flags_block,
900 { "Block", "norm.nack.flags.block",
901 FT_BOOLEAN, 8, NULL, NORM_NACK_BLOCK,
904 { &hf_nack_flags_info,
905 { "Info", "norm.nack.flags.info",
906 FT_BOOLEAN, 8, NULL, NORM_NACK_INFO,
909 { &hf_nack_flags_object,
910 { "Object", "norm.nack.flags.object",
911 FT_BOOLEAN, 8, NULL, NORM_NACK_OBJECT,
915 { "NAck Length", "norm.nack.length",
916 FT_UINT16, BASE_DEC, NULL, 0x0,
920 { "Payload", "norm.payload",
921 FT_BYTES, BASE_NONE, NULL, 0x0,
924 { &hf_fec_encoding_id,
925 { "FEC Encoding ID", "norm.fec_encoding_id",
926 FT_UINT8, BASE_DEC, VALS(string_fec_encoding_id), 0x0,
931 /* Setup protocol subtree array */
932 static gint *ett[] = {
937 &ett_congestioncontrol,
941 static ei_register_info ei[] = {
942 { &ei_version1_only, { "norm.version1_only", PI_PROTOCOL, PI_WARN, "Sorry, this dissector supports NORM version 1 only", EXPFILL }}
946 expert_module_t* expert_rmt_norm;
948 /* Register the protocol name and description */
949 proto_rmt_norm = proto_register_protocol("Negative-acknowledgment Oriented Reliable Multicast", "NORM", "norm");
951 /* Register the header fields and subtrees used */
952 proto_register_field_array(proto_rmt_norm, hf, array_length(hf));
953 proto_register_subtree_array(ett, array_length(ett));
954 expert_rmt_norm = expert_register_protocol(proto_rmt_norm);
955 expert_register_field_array(expert_rmt_norm, ei, array_length(ei));
958 /* Register preferences */
959 module = prefs_register_protocol(proto_rmt_norm, NULL);
960 prefs_register_obsolete_preference(module, "heuristic_norm");
963 void proto_reg_handoff_norm(void)
965 static dissector_handle_t handle;
967 handle = create_dissector_handle(dissect_norm, proto_rmt_norm);
968 dissector_add_for_decode_as_with_preference("udp.port", handle);
969 heur_dissector_add("udp", dissect_norm_heur, "NORM over UDP", "rmt_norm_udp", proto_rmt_norm, HEURISTIC_DISABLE);
971 rmt_fec_handle = find_dissector_add_dependency("rmt-fec", proto_rmt_norm);
975 * Editor modelines - http://www.wireshark.org/tools/modelines.html
980 * indent-tabs-mode: nil
983 * ex: set shiftwidth=4 tabstop=8 expandtab:
984 * :indentSize=4:tabSize=8:noTabs=true: