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);
41 static dissector_handle_t norm_handle
;
52 static const value_string string_norm_type
[] =
54 { NORM_INFO
, "INFO" },
55 { NORM_DATA
, "DATA" },
57 { NORM_NACK
, "NACK" },
59 { NORM_REPORT
, "REPORT" },
63 #define NORM_CMD_FLUSH 1
64 #define NORM_CMD_EOT 2
65 #define NORM_CMD_SQUELCH 3
67 #define NORM_CMD_REPAIR_ADV 5
68 #define NORM_CMD_ACK_REQ 6
69 #define NORM_CMD_APPLICATION 7
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" },
84 #define NORM_ACK_FLUSH 2
86 static const value_string string_norm_ack_type
[] =
88 { NORM_ACK_CC
, "ACK CC" },
89 { NORM_ACK_FLUSH
, "ACK FLUSH" },
93 #define NORM_NACK_ITEMS 1
94 #define NORM_NACK_RANGES 2
95 #define NORM_NACK_ERASURES 3
97 static const value_string string_norm_nack_form
[] =
99 { NORM_NACK_ITEMS
, "Items" },
100 { NORM_NACK_RANGES
, "Ranges" },
101 { NORM_NACK_ERASURES
, "Erasures" },
105 #define NORM_FLAG_REPAIR 0x01
106 #define NORM_FLAG_EXPLICIT 0x02
107 #define NORM_FLAG_INFO 0x04
108 #define NORM_FLAG_UNRELIABLE 0x08
109 #define NORM_FLAG_FILE 0x10
110 #define NORM_FLAG_STREAM 0x20
111 #define NORM_FLAG_MSG_START 0x40
113 #define NORM_NACK_SEGMENT 0x01
114 #define NORM_NACK_BLOCK 0x02
115 #define NORM_NACK_INFO 0x04
116 #define NORM_NACK_OBJECT 0x08
118 #define NORM_FLAG_CC_CLR 0x01
119 #define NORM_FLAG_CC_PLR 0x02
120 #define NORM_FLAG_CC_RTT 0x04
121 #define NORM_FLAG_CC_START 0x08
122 #define NORM_FLAG_CC_LEAVE 0x10
124 #define hdrlen2bytes(x) ((x)*4U)
126 typedef struct norm_packet_data
129 } norm_packet_data_t
;
131 /* Initialize the protocol and registered fields */
132 /* ============================================= */
133 static dissector_handle_t rmt_fec_handle
;
135 static int proto_rmt_norm
;
137 static int hf_version
;
140 static int hf_sequence
;
141 static int hf_source_id
;
142 static int hf_instance_id
;
144 static int hf_backoff
;
147 static int hf_flag_repair
;
148 static int hf_flag_norm_explicit
;
149 static int hf_flag_info
;
150 static int hf_flag_unreliable
;
151 static int hf_flag_file
;
152 static int hf_flag_stream
;
153 static int hf_flag_msgstart
;
154 static int hf_object_transport_id
;
155 static int hf_extension
;
156 static int hf_reserved
;
157 static int hf_payload_len
;
158 static int hf_payload_offset
;
159 static int hf_cmd_flavor
;
160 static int hf_cc_sequence
;
161 static int hf_cc_sts
;
162 static int hf_cc_stus
;
163 static int hf_cc_node_id
;
164 static int hf_cc_flags
;
165 static int hf_cc_flags_clr
;
166 static int hf_cc_flags_plr
;
167 static int hf_cc_flags_rtt
;
168 static int hf_cc_flags_start
;
169 static int hf_cc_flags_leave
;
170 static int hf_cc_rtt
;
171 static int hf_cc_rate
;
172 static int hf_cc_transport_id
;
173 static int hf_ack_source
;
174 static int hf_ack_type
;
175 static int hf_ack_id
;
176 static int hf_ack_grtt_sec
;
177 static int hf_ack_grtt_usec
;
178 static int hf_nack_server
;
179 static int hf_nack_grtt_sec
;
180 static int hf_nack_grtt_usec
;
181 static int hf_nack_form
;
182 static int hf_nack_flags
;
183 static int hf_nack_flags_segment
;
184 static int hf_nack_flags_block
;
185 static int hf_nack_flags_info
;
186 static int hf_nack_flags_object
;
187 static int hf_nack_length
;
188 static int hf_payload
;
189 static int hf_fec_encoding_id
;
192 static int ett_hdrext
;
193 static int ett_flags
;
194 static int ett_streampayload
;
195 static int ett_congestioncontrol
;
196 static int ett_nackdata
;
198 static expert_field ei_version1_only
;
200 static const double RTT_MIN
= 1.0e-06;
201 static const double RTT_MAX
= 1000;
203 static double UnquantizeRtt(unsigned char qrtt
)
205 return ((qrtt
<= 31) ? (((double)(qrtt
+1))*(double)RTT_MIN
) :
206 (RTT_MAX
/exp(((double)(255-qrtt
))/(double)13.0)));
209 static double UnquantizeGSize(uint8_t gsizex
)
211 unsigned mant
= (gsizex
& 0x8) ? 5 : 1;
212 unsigned exponent
= gsizex
& 0x7;
215 return mant
* pow(10, exponent
);
218 /* code to dissect fairly common sequence in NORM packets */
219 static unsigned dissect_grrtetc(proto_tree
*tree
, tvbuff_t
*tvb
, unsigned offset
)
225 proto_tree_add_item(tree
, hf_instance_id
, tvb
, offset
, 2, ENC_BIG_ENDIAN
); offset
+=2;
226 grtt
= UnquantizeRtt(tvb_get_uint8(tvb
, offset
));
227 proto_tree_add_double(tree
, hf_grtt
, tvb
, offset
, 1, grtt
); offset
+= 1;
228 backoff
= hi_nibble(tvb_get_uint8(tvb
, offset
));
229 gsizex
= UnquantizeGSize((uint8_t)lo_nibble(tvb_get_uint8(tvb
, offset
)));
230 proto_tree_add_uint(tree
, hf_backoff
, tvb
, offset
, 1, backoff
);
231 proto_tree_add_double(tree
, hf_gsize
, tvb
, offset
, 1, gsizex
);
236 /* split out some common FEC handling */
237 static unsigned dissect_feccode(proto_tree
*tree
, tvbuff_t
*tvb
, unsigned offset
,
238 packet_info
*pinfo
, int reserved
)
240 norm_packet_data_t
*norm_data
;
241 uint8_t encoding_id
= tvb_get_uint8(tvb
, offset
);
243 /* Save encoding ID */
244 norm_data
= wmem_new0(wmem_file_scope(), norm_packet_data_t
);
245 norm_data
->encoding_id
= encoding_id
;
247 p_add_proto_data(wmem_file_scope(), pinfo
, proto_rmt_norm
, 0, norm_data
);
249 proto_tree_add_item(tree
, hf_fec_encoding_id
, tvb
, offset
, 1, ENC_BIG_ENDIAN
); offset
+= 1;
251 proto_tree_add_item(tree
, hf_reserved
, tvb
, offset
, 1, ENC_BIG_ENDIAN
); offset
+= 1;
253 proto_tree_add_item(tree
, hf_object_transport_id
, tvb
, offset
, 2, ENC_BIG_ENDIAN
); offset
+=2;
255 if (tvb_reported_length_remaining(tvb
, offset
) > 0) {
256 fec_data_exchange_t fec
;
260 new_tvb
= tvb_new_subset_remaining(tvb
, offset
);
262 fec
.encoding_id
= encoding_id
;
263 len
= call_dissector_with_data(rmt_fec_handle
, new_tvb
, pinfo
, tree
, &fec
);
271 static unsigned dissect_norm_hdrext(proto_tree
*tree
, packet_info
*pinfo
,
272 tvbuff_t
*tvb
, unsigned offset
, uint8_t hlen
)
274 lct_data_exchange_t data_exchange
;
275 norm_packet_data_t
*packet_data
= (norm_packet_data_t
*)p_get_proto_data(wmem_file_scope(), pinfo
, proto_rmt_norm
, 0);
277 memset(&data_exchange
, 0, sizeof(data_exchange
));
279 if (packet_data
!= NULL
)
280 data_exchange
.codepoint
= packet_data
->encoding_id
;
282 offset
+= lct_ext_decode(tree
, tvb
, pinfo
, offset
, hdrlen2bytes(hlen
), &data_exchange
,
283 hf_extension
, ett_hdrext
);
288 static unsigned dissect_nack_data(proto_tree
*tree
, tvbuff_t
*tvb
, unsigned offset
,
291 proto_item
*ti
, *tif
;
292 proto_tree
*nack_tree
, *flag_tree
;
295 nack_tree
= proto_tree_add_subtree(tree
, tvb
, offset
, -1, ett_nackdata
, &ti
, "NACK Data");
296 proto_tree_add_item(nack_tree
, hf_nack_form
, tvb
, offset
, 1, ENC_BIG_ENDIAN
); offset
+= 1;
298 tif
= proto_tree_add_item(nack_tree
, hf_nack_flags
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
299 flag_tree
= proto_item_add_subtree(tif
, ett_flags
);
300 proto_tree_add_item(flag_tree
, hf_nack_flags_segment
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
301 proto_tree_add_item(flag_tree
, hf_nack_flags_block
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
302 proto_tree_add_item(flag_tree
, hf_nack_flags_info
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
303 proto_tree_add_item(flag_tree
, hf_nack_flags_object
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
305 len
= tvb_get_ntohs(tvb
, offset
);
306 proto_tree_add_item(nack_tree
, hf_nack_length
, tvb
, offset
, 2, ENC_BIG_ENDIAN
); offset
+= 2;
307 proto_item_set_len(ti
, 4+len
);
309 dissect_feccode(nack_tree
, tvb
, offset
, pinfo
, 1);
315 /* code to dissect NORM data packets */
316 static void dissect_norm_data(proto_tree
*tree
, packet_info
*pinfo
,
317 tvbuff_t
*tvb
, unsigned offset
, uint8_t hlen
)
321 proto_tree
*flag_tree
;
323 offset
= dissect_grrtetc(tree
, tvb
, offset
);
325 ti
= proto_tree_add_item(tree
, hf_flags
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
326 flags
= tvb_get_uint8(tvb
, offset
);
327 flag_tree
= proto_item_add_subtree(ti
, ett_flags
);
328 proto_tree_add_item(flag_tree
, hf_flag_repair
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
329 proto_tree_add_item(flag_tree
, hf_flag_norm_explicit
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
330 proto_tree_add_item(flag_tree
, hf_flag_info
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
331 proto_tree_add_item(flag_tree
, hf_flag_unreliable
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
332 proto_tree_add_item(flag_tree
, hf_flag_file
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
333 proto_tree_add_item(flag_tree
, hf_flag_stream
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
334 proto_tree_add_item(flag_tree
, hf_flag_msgstart
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
337 offset
= dissect_feccode(tree
, tvb
, offset
, pinfo
, 0);
339 if (offset
< hdrlen2bytes(hlen
)) {
340 offset
= dissect_norm_hdrext(tree
, pinfo
, tvb
, offset
, hlen
);
342 if (flags
& NORM_FLAG_STREAM
) {
343 flag_tree
= proto_tree_add_subtree(tree
, tvb
, offset
, 8, ett_streampayload
, NULL
, "Stream Data");
344 proto_tree_add_item(flag_tree
, hf_reserved
, tvb
, offset
, 2, ENC_BIG_ENDIAN
); offset
+= 2;
345 proto_tree_add_item(flag_tree
, hf_payload_len
, tvb
, offset
, 2, ENC_BIG_ENDIAN
); offset
+= 2;
346 proto_tree_add_item(flag_tree
, hf_payload_offset
, tvb
, offset
, 4, ENC_BIG_ENDIAN
); offset
+= 4;
349 if (tvb_reported_length_remaining(tvb
, offset
) > 0)
350 proto_tree_add_item(tree
, hf_payload
, tvb
, offset
, -1, ENC_NA
);
353 /* code to dissect NORM info packets */
354 static void dissect_norm_info(proto_tree
*tree
, packet_info
*pinfo
, tvbuff_t
*tvb
, unsigned offset
, uint8_t hlen
)
357 proto_tree
*flag_tree
;
358 norm_packet_data_t
*norm_data
;
360 offset
= dissect_grrtetc(tree
, tvb
, offset
);
362 ti
= proto_tree_add_item(tree
, hf_flags
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
363 flag_tree
= proto_item_add_subtree(ti
, ett_flags
);
364 proto_tree_add_item(flag_tree
, hf_flag_repair
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
365 proto_tree_add_item(flag_tree
, hf_flag_norm_explicit
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
366 proto_tree_add_item(flag_tree
, hf_flag_info
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
367 proto_tree_add_item(flag_tree
, hf_flag_unreliable
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
368 proto_tree_add_item(flag_tree
, hf_flag_file
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
369 proto_tree_add_item(flag_tree
, hf_flag_stream
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
370 proto_tree_add_item(flag_tree
, hf_flag_msgstart
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
373 /* Save encoding ID */
374 norm_data
= wmem_new0(wmem_file_scope(), norm_packet_data_t
);
375 norm_data
->encoding_id
= tvb_get_uint8(tvb
, offset
);
377 p_add_proto_data(wmem_file_scope(), pinfo
, proto_rmt_norm
, 0, norm_data
);
379 proto_tree_add_item(tree
, hf_fec_encoding_id
, tvb
, offset
, 1, ENC_BIG_ENDIAN
); offset
+= 1;
380 proto_tree_add_item(tree
, hf_object_transport_id
, tvb
, offset
, 2, ENC_BIG_ENDIAN
); offset
+= 2;
382 if (offset
< hdrlen2bytes(hlen
)) {
383 offset
= dissect_norm_hdrext(tree
, pinfo
, tvb
, offset
, hlen
);
385 if (tvb_reported_length_remaining(tvb
, offset
) > 0)
386 proto_tree_add_item(tree
, hf_payload
, tvb
, offset
, -1, ENC_NA
);
389 /* code to dissect NORM cmd(flush) packets */
390 static unsigned dissect_norm_cmd_flush(proto_tree
*tree
, packet_info
*pinfo
,
391 tvbuff_t
*tvb
, unsigned offset
, uint8_t hlen
)
393 offset
= dissect_feccode(tree
, tvb
, offset
, pinfo
, 0);
394 if (offset
< hdrlen2bytes(hlen
)) {
395 offset
= dissect_norm_hdrext(tree
, pinfo
, tvb
, offset
, hlen
);
400 /* code to dissect NORM cmd(flush) packets */
401 static unsigned dissect_norm_cmd_repairadv(proto_tree
*tree
, packet_info
*pinfo
,
402 tvbuff_t
*tvb
, unsigned offset
, uint8_t hlen
)
404 proto_tree_add_item(tree
, hf_flags
, tvb
, offset
, 1, ENC_BIG_ENDIAN
); offset
+= 1;
405 proto_tree_add_item(tree
, hf_reserved
, tvb
, offset
, 2, ENC_BIG_ENDIAN
); offset
+= 2;
407 if (offset
< hdrlen2bytes(hlen
)) {
408 offset
= dissect_norm_hdrext(tree
, pinfo
, tvb
, offset
, hlen
);
410 while (tvb_reported_length_remaining(tvb
, offset
) > 0) {
411 offset
= dissect_nack_data(tree
, tvb
, offset
, pinfo
);
416 /* code to dissect NORM cmd(cc) packets */
417 static unsigned dissect_norm_cmd_cc(proto_tree
*tree
, packet_info
*pinfo
,
418 tvbuff_t
*tvb
, unsigned offset
, uint8_t hlen
)
420 proto_tree_add_item(tree
, hf_reserved
, tvb
, offset
, 1, ENC_BIG_ENDIAN
); offset
+= 1;
421 proto_tree_add_item(tree
, hf_cc_sequence
, tvb
, offset
, 2, ENC_BIG_ENDIAN
); offset
+= 2;
423 proto_tree_add_item(tree
, hf_cc_sts
, tvb
, offset
, 4, ENC_BIG_ENDIAN
); offset
+= 4;
424 proto_tree_add_item(tree
, hf_cc_stus
, tvb
, offset
, 4, ENC_BIG_ENDIAN
); offset
+= 4;
425 if (offset
< hdrlen2bytes(hlen
)) {
426 offset
= dissect_norm_hdrext(tree
, pinfo
, tvb
, offset
, hlen
);
428 while (offset
< hdrlen2bytes(hlen
)) {
430 proto_tree
*cc_tree
, *flag_tree
;
432 cc_tree
= proto_tree_add_subtree(tree
, tvb
, offset
, 8, ett_congestioncontrol
, NULL
, "Congestion Control");
433 proto_tree_add_item(cc_tree
, hf_cc_node_id
, tvb
, offset
, 4, ENC_BIG_ENDIAN
); offset
+= 4;
434 tif
= proto_tree_add_item(cc_tree
, hf_cc_flags
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
435 flag_tree
= proto_item_add_subtree(tif
, ett_flags
);
436 proto_tree_add_item(flag_tree
, hf_cc_flags_clr
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
437 proto_tree_add_item(flag_tree
, hf_cc_flags_plr
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
438 proto_tree_add_item(flag_tree
, hf_cc_flags_rtt
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
439 proto_tree_add_item(flag_tree
, hf_cc_flags_start
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
440 proto_tree_add_item(flag_tree
, hf_cc_flags_leave
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
442 grtt
= UnquantizeRtt(tvb_get_uint8(tvb
, offset
));
443 proto_tree_add_double(cc_tree
, hf_cc_rtt
, tvb
, offset
, 1, grtt
); offset
+= 1;
444 grtt
= rmt_decode_send_rate(tvb_get_ntohs(tvb
, offset
));
445 proto_tree_add_double(cc_tree
, hf_cc_rate
, tvb
, offset
, 2, grtt
); offset
+= 2;
450 /* code to dissect NORM cmd(squelch) packets */
451 static unsigned dissect_norm_cmd_squelch(proto_tree
*tree
, packet_info
*pinfo
,
452 tvbuff_t
*tvb
, unsigned offset
)
454 offset
= dissect_feccode(tree
, tvb
, offset
, pinfo
, 0);
456 while (tvb_reported_length_remaining(tvb
, offset
) > 0) {
457 proto_tree_add_item(tree
, hf_cc_transport_id
, tvb
, offset
, 2, ENC_BIG_ENDIAN
); offset
+= 2;
462 /* code to dissect NORM cmd(squelch) packets */
463 static unsigned dissect_norm_cmd_ackreq(proto_tree
*tree
, packet_info
*pinfo _U_
,
464 tvbuff_t
*tvb
, unsigned offset
)
466 proto_tree_add_item(tree
, hf_reserved
, tvb
, offset
, 1, ENC_BIG_ENDIAN
); offset
+= 1;
467 proto_tree_add_item(tree
, hf_ack_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
); offset
+= 1;
468 proto_tree_add_item(tree
, hf_ack_id
, tvb
, offset
, 1, ENC_BIG_ENDIAN
); offset
+= 1;
472 /* code to dissect NORM cmd packets */
473 static void dissect_norm_cmd(proto_tree
*tree
, packet_info
*pinfo
,
474 tvbuff_t
*tvb
, unsigned offset
, uint8_t hlen
)
478 offset
= dissect_grrtetc(tree
, tvb
, offset
);
479 flavor
= tvb_get_uint8(tvb
, offset
);
481 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, " ",
482 val_to_str(flavor
, string_norm_cmd_type
, "Unknown Cmd Type (0x%04x)"));
483 proto_tree_add_item(tree
, hf_cmd_flavor
, tvb
, offset
, 1, ENC_BIG_ENDIAN
); offset
+= 1;
486 offset
= dissect_norm_cmd_cc(tree
, pinfo
, tvb
, offset
, hlen
);
489 offset
= dissect_norm_cmd_flush(tree
, pinfo
, tvb
, offset
, hlen
);
491 case NORM_CMD_SQUELCH
:
492 offset
= dissect_norm_cmd_squelch(tree
, pinfo
, tvb
, offset
);
494 case NORM_CMD_REPAIR_ADV
:
495 offset
= dissect_norm_cmd_repairadv(tree
, pinfo
, tvb
, offset
, hlen
);
497 case NORM_CMD_ACK_REQ
:
498 offset
= dissect_norm_cmd_ackreq(tree
, pinfo
, tvb
, offset
);
501 if (tvb_reported_length_remaining(tvb
, offset
) > 0)
502 proto_tree_add_item(tree
, hf_payload
, tvb
, offset
, -1, ENC_NA
);
505 /* code to dissect NORM ack packets */
506 static void dissect_norm_ack(proto_tree
*tree
, packet_info
*pinfo
,
507 tvbuff_t
*tvb
, unsigned offset
, uint8_t hlen
)
511 proto_tree_add_item(tree
, hf_ack_source
, tvb
, offset
, 4, ENC_BIG_ENDIAN
); offset
+= 4;
512 proto_tree_add_item(tree
, hf_instance_id
, tvb
, offset
, 2, ENC_BIG_ENDIAN
); offset
+= 2;
513 acktype
= tvb_get_uint8(tvb
, offset
);
515 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, " ",
516 val_to_str(acktype
, string_norm_ack_type
, "Unknown Ack Type (0x%04x)"));
517 proto_tree_add_item(tree
, hf_ack_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
); offset
+= 1;
518 proto_tree_add_item(tree
, hf_ack_id
, tvb
, offset
, 1, ENC_BIG_ENDIAN
); offset
+= 1;
519 proto_tree_add_item(tree
, hf_ack_grtt_sec
, tvb
, offset
, 4, ENC_BIG_ENDIAN
); offset
+= 4;
520 proto_tree_add_item(tree
, hf_ack_grtt_usec
, tvb
, offset
, 4, ENC_BIG_ENDIAN
); offset
+= 4;
521 if (offset
< hdrlen2bytes(hlen
)) {
522 offset
= dissect_norm_hdrext(tree
, pinfo
, tvb
, offset
, hlen
);
525 if (tvb_reported_length_remaining(tvb
, offset
) > 0)
526 proto_tree_add_item(tree
, hf_payload
, tvb
, offset
, -1, ENC_NA
);
529 /* code to dissect NORM nack packets */
530 static void dissect_norm_nack(proto_tree
*tree
, packet_info
*pinfo
,
531 tvbuff_t
*tvb
, unsigned offset
, uint8_t hlen
)
533 proto_tree_add_item(tree
, hf_nack_server
, tvb
, offset
, 4, ENC_BIG_ENDIAN
); offset
+= 4;
534 proto_tree_add_item(tree
, hf_instance_id
, tvb
, offset
, 2, ENC_BIG_ENDIAN
); offset
+= 2;
535 proto_tree_add_item(tree
, hf_reserved
, tvb
, offset
, 2, ENC_BIG_ENDIAN
); offset
+= 2;
536 proto_tree_add_item(tree
, hf_nack_grtt_sec
, tvb
, offset
, 4, ENC_BIG_ENDIAN
); offset
+= 4;
537 proto_tree_add_item(tree
, hf_nack_grtt_usec
, tvb
, offset
, 4, ENC_BIG_ENDIAN
); offset
+= 4;
538 if (offset
< hdrlen2bytes(hlen
)) {
539 offset
= dissect_norm_hdrext(tree
, pinfo
, tvb
, offset
, hlen
);
542 while (tvb_reported_length_remaining(tvb
, offset
) > 0) {
543 offset
= dissect_nack_data(tree
, tvb
, offset
, pinfo
);
545 if (tvb_reported_length_remaining(tvb
, offset
) > 0)
546 proto_tree_add_item(tree
, hf_payload
, tvb
, offset
, -1, ENC_NA
);
549 /* Code to actually dissect the packets */
550 /* ==================================== */
552 dissect_norm(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
554 /* Logical packet representation */
559 /* Offset for subpacket dissection */
562 /* Set up structures needed to add the protocol subtree and manage it */
564 proto_tree
*norm_tree
;
566 /* Make entries in Protocol column and Info column on summary display */
567 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "NORM");
568 col_clear(pinfo
->cinfo
, COL_INFO
);
570 /* NORM header dissection, part 1 */
571 /* ------------------------------ */
573 version
= hi_nibble(tvb_get_uint8(tvb
, offset
));
575 /* Create subtree for the NORM protocol */
576 ti
= proto_tree_add_item(tree
, proto_rmt_norm
, tvb
, offset
, -1, ENC_NA
);
577 norm_tree
= proto_item_add_subtree(ti
, ett_main
);
579 /* Fill the NORM subtree */
580 proto_tree_add_uint(norm_tree
, hf_version
, tvb
, offset
, 1, version
);
582 /* This dissector supports only NORMv1 packets.
583 * If version > 1 print only version field and quit.
586 expert_add_info(pinfo
, ti
, &ei_version1_only
);
588 /* Complete entry in Info column on summary display */
589 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Version: %u (not supported)", version
);
593 /* NORM header dissection, part 2 */
594 /* ------------------------------ */
596 type
= lo_nibble(tvb_get_uint8(tvb
, offset
));
597 hlen
= tvb_get_uint8(tvb
, offset
+1);
600 proto_tree_add_uint(norm_tree
, hf_type
, tvb
, offset
, 1, type
);
601 proto_tree_add_item(norm_tree
, hf_hlen
, tvb
, offset
+1, 1, ENC_BIG_ENDIAN
);
602 proto_tree_add_item(norm_tree
, hf_sequence
, tvb
, offset
+2, 2, ENC_BIG_ENDIAN
);
603 proto_tree_add_item(norm_tree
, hf_source_id
, tvb
, offset
+4, 4, ENC_BIG_ENDIAN
);
609 /* Complete entry in Info column on summary display */
610 /* ------------------------------------------------ */
611 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, " ",
612 val_to_str(type
, string_norm_type
, "Unknown Type (0x%04x)"));
617 dissect_norm_info(norm_tree
, pinfo
, tvb
, offset
, hlen
);
620 dissect_norm_data(norm_tree
, pinfo
, tvb
, offset
, hlen
);
623 dissect_norm_cmd(norm_tree
, pinfo
, tvb
, offset
, hlen
);
626 dissect_norm_ack(norm_tree
, pinfo
, tvb
, offset
, hlen
);
629 dissect_norm_nack(norm_tree
, pinfo
, tvb
, offset
, hlen
);
632 /* Add the Payload item */
633 if (tvb_reported_length_remaining(tvb
, offset
) > 0)
634 proto_tree_add_item(norm_tree
, hf_payload
, tvb
, offset
, -1, ENC_NA
);
638 return tvb_reported_length(tvb
);
642 dissect_norm_heur(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
646 if (tvb_reported_length(tvb
) < 12)
647 return false; /* not enough to check */
648 byte1
= tvb_get_uint8(tvb
, 0);
650 if (hi_nibble(byte1
) != 1) return false;
651 if (lo_nibble(byte1
) < 1 || lo_nibble(byte1
) > 6) return false;
652 if (tvb_get_uint8(tvb
, 1) > 20) return false;
654 dissect_norm(tvb
, pinfo
, tree
, data
);
655 return true; /* appears to be a NORM packet */
658 void proto_register_norm(void)
660 /* Setup NORM header fields */
661 static hf_register_info hf
[] = {
664 { "Version", "norm.version",
665 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
669 { "Message Type", "norm.type",
670 FT_UINT8
, BASE_DEC
, VALS(string_norm_type
), 0x0,
674 { "Header length", "norm.hlen",
675 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
679 { "Sequence", "norm.sequence",
680 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
684 { "Source ID", "norm.source_id",
685 FT_IPv4
, BASE_NONE
, NULL
, 0x0,
689 { "Instance", "norm.instance_id",
690 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
694 { "grtt", "norm.grtt",
695 FT_DOUBLE
, BASE_NONE
, NULL
, 0x0,
699 { "Backoff", "norm.backoff",
700 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
704 { "Group Size", "norm.gsize",
705 FT_DOUBLE
, BASE_NONE
, NULL
, 0x0,
709 { "Flags", "norm.flags",
710 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
714 { "Repair Flag", "norm.flag.repair",
715 FT_BOOLEAN
, 8, NULL
, NORM_FLAG_REPAIR
,
718 { &hf_flag_norm_explicit
,
719 { "Explicit Flag", "norm.flag.explicit",
720 FT_BOOLEAN
, 8, NULL
, NORM_FLAG_EXPLICIT
,
724 { "Info Flag", "norm.flag.info",
725 FT_BOOLEAN
, 8, NULL
, NORM_FLAG_INFO
,
728 { &hf_flag_unreliable
,
729 { "Unreliable Flag", "norm.flag.unreliable",
730 FT_BOOLEAN
, 8, NULL
, NORM_FLAG_UNRELIABLE
,
734 { "File Flag", "norm.flag.file",
735 FT_BOOLEAN
, 8, NULL
, NORM_FLAG_FILE
,
739 { "Stream Flag", "norm.flag.stream",
740 FT_BOOLEAN
, 8, NULL
, NORM_FLAG_STREAM
,
744 { "Msg Start Flag", "norm.flag.msgstart",
745 FT_BOOLEAN
, 8, NULL
, NORM_FLAG_MSG_START
,
748 { &hf_object_transport_id
,
749 { "Object Transport ID", "norm.object_transport_id",
750 FT_UINT16
, BASE_HEX
, NULL
, 0x0,
754 { "Hdr Extension", "norm.hexext",
755 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
759 { "Reserved", "norm.reserved",
760 FT_UINT16
, BASE_HEX
, NULL
, 0x0,
764 { "Payload Len", "norm.payload.len",
765 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
768 { &hf_payload_offset
,
769 { "Payload Offset", "norm.payload.offset",
770 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
775 { "Flavor", "norm.flavor",
776 FT_UINT8
, BASE_DEC
, VALS(string_norm_cmd_type
), 0x0,
780 { "CC Sequence", "norm.ccsequence",
781 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
785 { "Send Time secs", "norm.cc_sts",
786 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
790 { "Send Time usecs", "norm.cc_stus",
791 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
795 { "CC Node ID", "norm.cc_node_id",
796 FT_IPv4
, BASE_NONE
, NULL
, 0x0,
800 { "CC Flags", "norm.cc_flags",
801 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
805 { "CLR", "norm.cc_flags.clr",
806 FT_BOOLEAN
, 8, NULL
, NORM_FLAG_CC_CLR
,
810 { "PLR", "norm.cc_flags.plr",
811 FT_BOOLEAN
, 8, NULL
, NORM_FLAG_CC_PLR
,
815 { "RTT", "norm.cc_flags.rtt",
816 FT_BOOLEAN
, 8, NULL
, NORM_FLAG_CC_RTT
,
819 { &hf_cc_flags_start
,
820 { "Start", "norm.cc_flags.start",
821 FT_BOOLEAN
, 8, NULL
, NORM_FLAG_CC_START
,
824 { &hf_cc_flags_leave
,
825 { "Leave", "norm.cc_flags.leave",
826 FT_BOOLEAN
, 8, NULL
, NORM_FLAG_CC_LEAVE
,
830 { "CC RTT", "norm.cc_rtt",
831 FT_DOUBLE
, BASE_NONE
, NULL
, 0x0,
835 { "CC Rate", "norm.cc_rate",
836 FT_DOUBLE
, BASE_NONE
, NULL
, 0x0,
839 { &hf_cc_transport_id
,
840 { "CC Transport ID", "norm.cc_transport_id",
841 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
846 { "Ack Source", "norm.ack.source",
847 FT_IPv4
, BASE_NONE
, NULL
, 0x0,
851 { "Ack Type", "norm.ack.type",
852 FT_UINT8
, BASE_DEC
, VALS(string_norm_ack_type
), 0x0,
856 { "Ack ID", "norm.ack.id",
857 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
861 { "Ack GRTT Sec", "norm.ack.grtt_sec",
862 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
866 { "Ack GRTT usec", "norm.ack.grtt_usec",
867 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
872 { "NAck Server", "norm.nack.server",
873 FT_IPv4
, BASE_NONE
, NULL
, 0x0,
877 { "NAck GRTT Sec", "norm.nack.grtt_sec",
878 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
881 { &hf_nack_grtt_usec
,
882 { "NAck GRTT usec", "norm.nack.grtt_usec",
883 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
887 { "NAck FORM", "norm.nack.form",
888 FT_UINT8
, BASE_DEC
, VALS(string_norm_nack_form
), 0x0,
892 { "NAck Flags", "norm.nack.flags",
893 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
896 { &hf_nack_flags_segment
,
897 { "Segment", "norm.nack.flags.segment",
898 FT_BOOLEAN
, 8, NULL
, NORM_NACK_SEGMENT
,
901 { &hf_nack_flags_block
,
902 { "Block", "norm.nack.flags.block",
903 FT_BOOLEAN
, 8, NULL
, NORM_NACK_BLOCK
,
906 { &hf_nack_flags_info
,
907 { "Info", "norm.nack.flags.info",
908 FT_BOOLEAN
, 8, NULL
, NORM_NACK_INFO
,
911 { &hf_nack_flags_object
,
912 { "Object", "norm.nack.flags.object",
913 FT_BOOLEAN
, 8, NULL
, NORM_NACK_OBJECT
,
917 { "NAck Length", "norm.nack.length",
918 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
922 { "Payload", "norm.payload",
923 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
926 { &hf_fec_encoding_id
,
927 { "FEC Encoding ID", "norm.fec_encoding_id",
928 FT_UINT8
, BASE_DEC
, VALS(string_fec_encoding_id
), 0x0,
933 /* Setup protocol subtree array */
934 static int *ett
[] = {
939 &ett_congestioncontrol
,
943 static ei_register_info ei
[] = {
944 { &ei_version1_only
, { "norm.version1_only", PI_PROTOCOL
, PI_WARN
, "Sorry, this dissector supports NORM version 1 only", EXPFILL
}}
948 expert_module_t
* expert_rmt_norm
;
950 /* Register the protocol name and description */
951 proto_rmt_norm
= proto_register_protocol("Negative-acknowledgment Oriented Reliable Multicast", "NORM", "norm");
953 /* Register the header fields and subtrees used */
954 proto_register_field_array(proto_rmt_norm
, hf
, array_length(hf
));
955 proto_register_subtree_array(ett
, array_length(ett
));
956 expert_rmt_norm
= expert_register_protocol(proto_rmt_norm
);
957 expert_register_field_array(expert_rmt_norm
, ei
, array_length(ei
));
959 /* Register the subdissector handle */
960 norm_handle
= register_dissector("norm", dissect_norm
, proto_rmt_norm
);
962 /* Register preferences */
963 module
= prefs_register_protocol(proto_rmt_norm
, NULL
);
964 prefs_register_obsolete_preference(module
, "heuristic_norm");
967 void proto_reg_handoff_norm(void)
969 dissector_add_for_decode_as_with_preference("udp.port", norm_handle
);
970 heur_dissector_add("udp", dissect_norm_heur
, "NORM over UDP", "rmt_norm_udp", proto_rmt_norm
, HEURISTIC_DISABLE
);
972 rmt_fec_handle
= find_dissector_add_dependency("rmt-fec", proto_rmt_norm
);
976 * Editor modelines - https://www.wireshark.org/tools/modelines.html
981 * indent-tabs-mode: nil
984 * ex: set shiftwidth=4 tabstop=8 expandtab:
985 * :indentSize=4:tabSize=8:noTabs=true: