GTPv2: fix dissection of APN IE
[metze/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  * Wireshark - Network traffic analyzer
22  * By Gerald Combs <gerald@wireshark.org>
23  * Copyright 1998 Gerald Combs
24  *
25  * SPDX-License-Identifier: GPL-2.0-or-later
26  */
27
28 #include "config.h"
29
30 #include <math.h>
31
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"
37
38 void proto_register_norm(void);
39 void proto_reg_handoff_norm(void);
40
41 /* String tables */
42
43 #define NORM_INFO       1
44 #define NORM_DATA       2
45 #define NORM_CMD        3
46 #define NORM_NACK       4
47 #define NORM_ACK        5
48 #define NORM_REPORT     6
49
50 static const value_string string_norm_type[] =
51 {
52     { NORM_INFO,   "INFO" },
53     { NORM_DATA,   "DATA" },
54     { NORM_CMD,    "CMD" },
55     { NORM_NACK,   "NACK" },
56     { NORM_ACK,    "ACK" },
57     { NORM_REPORT, "REPORT" },
58     { 0, NULL }
59 };
60
61 #define NORM_CMD_FLUSH          1
62 #define NORM_CMD_EOT            2
63 #define NORM_CMD_SQUELCH        3
64 #define NORM_CMD_CC             4
65 #define NORM_CMD_REPAIR_ADV     5
66 #define NORM_CMD_ACK_REQ        6
67 #define NORM_CMD_APPLICATION    7
68
69 static const value_string string_norm_cmd_type[] =
70 {
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" },
78     { 0, NULL }
79 };
80
81 #define NORM_ACK_CC         1
82 #define NORM_ACK_FLUSH      2
83
84 static const value_string string_norm_ack_type[] =
85 {
86     { NORM_ACK_CC,    "ACK CC" },
87     { NORM_ACK_FLUSH, "ACK FLUSH" },
88     { 0, NULL }
89 };
90
91 #define NORM_NACK_ITEMS         1
92 #define NORM_NACK_RANGES        2
93 #define NORM_NACK_ERASURES      3
94
95 static const value_string string_norm_nack_form[] =
96 {
97     { NORM_NACK_ITEMS,    "Items" },
98     { NORM_NACK_RANGES,   "Ranges" },
99     { NORM_NACK_ERASURES, "Erasures" },
100     { 0, NULL }
101 };
102
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
110
111 #define NORM_NACK_SEGMENT       0x01
112 #define NORM_NACK_BLOCK         0x02
113 #define NORM_NACK_INFO          0x04
114 #define NORM_NACK_OBJECT        0x08
115
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
121
122 #define hdrlen2bytes(x) ((x)*4U)
123
124 typedef struct norm_packet_data
125 {
126     guint8 encoding_id;
127 } norm_packet_data_t;
128
129 /* Initialize the protocol and registered fields */
130 /* ============================================= */
131 static dissector_handle_t rmt_fec_handle;
132
133 static int proto_rmt_norm = -1;
134
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;
188
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;
195
196 static expert_field ei_version1_only = EI_INIT;
197
198 static const double RTT_MIN = 1.0e-06;
199 static const double RTT_MAX = 1000;
200
201 static double UnquantizeRtt(unsigned char qrtt)
202 {
203     return ((qrtt <= 31) ? (((double)(qrtt+1))*(double)RTT_MIN) :
204             (RTT_MAX/exp(((double)(255-qrtt))/(double)13.0)));
205 }
206
207 static double UnquantizeGSize(guint8 gsizex)
208 {
209     guint mant = (gsizex & 0x8) ? 5 : 1;
210     guint exponent = gsizex & 0x7;
211
212     exponent += 1;
213     return mant * pow(10, exponent);
214 }
215
216 /* code to dissect fairly common sequence in NORM packets */
217 static guint dissect_grrtetc(proto_tree *tree, tvbuff_t *tvb, guint offset)
218 {
219     guint8 backoff;
220     double gsizex;
221     double grtt;
222
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);
230     offset += 1;
231     return offset;
232 }
233
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)
237 {
238     norm_packet_data_t *norm_data;
239     guint8              encoding_id = tvb_get_guint8(tvb, offset);
240
241     /* Save encoding ID */
242     norm_data = wmem_new0(wmem_file_scope(), norm_packet_data_t);
243     norm_data->encoding_id = encoding_id;
244
245     p_add_proto_data(wmem_file_scope(), pinfo, proto_rmt_norm, 0, norm_data);
246
247     proto_tree_add_item(tree, hf_fec_encoding_id, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1;
248     if (reserved) {
249         proto_tree_add_item(tree, hf_reserved, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1;
250     }
251     proto_tree_add_item(tree, hf_object_transport_id, tvb, offset, 2, ENC_BIG_ENDIAN); offset+=2;
252
253     if (tvb_reported_length_remaining(tvb, offset) > 0) {
254         fec_data_exchange_t  fec;
255         tvbuff_t            *new_tvb;
256         int                  len;
257
258         new_tvb = tvb_new_subset_remaining(tvb, offset);
259
260         fec.encoding_id = encoding_id;
261         len = call_dissector_with_data(rmt_fec_handle, new_tvb, pinfo, tree, &fec);
262         if (len > 0)
263             offset += len;
264     }
265
266     return offset;
267 }
268
269 static guint dissect_norm_hdrext(proto_tree *tree, packet_info *pinfo,
270                                  tvbuff_t *tvb, guint offset, guint8 hlen)
271 {
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);
274
275     memset(&data_exchange, 0, sizeof(data_exchange));
276
277     if (packet_data != NULL)
278         data_exchange.codepoint = packet_data->encoding_id;
279
280     offset += lct_ext_decode(tree, tvb, pinfo, offset, hdrlen2bytes(hlen), &data_exchange,
281                              hf_extension, ett_hdrext);
282
283     return offset;
284 }
285
286 static guint dissect_nack_data(proto_tree *tree, tvbuff_t *tvb, guint offset,
287                                packet_info *pinfo)
288 {
289     proto_item *ti, *tif;
290     proto_tree *nack_tree, *flag_tree;
291     guint16     len;
292
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;
295
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);
302     offset += 1;
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);
306     if (len > 4) {
307         dissect_feccode(nack_tree, tvb, offset, pinfo, 1);
308     }
309     offset += len;
310     return offset;
311 }
312
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)
316 {
317     guint8      flags;
318     proto_item *ti;
319     proto_tree *flag_tree;
320
321     offset = dissect_grrtetc(tree, tvb, offset);
322
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);
333     offset += 1;
334
335     offset = dissect_feccode(tree, tvb, offset, pinfo, 0);
336
337     if (offset < hdrlen2bytes(hlen)) {
338         offset = dissect_norm_hdrext(tree, pinfo, tvb, offset, hlen);
339     }
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;
345
346     }
347     if (tvb_reported_length_remaining(tvb, offset) > 0)
348         proto_tree_add_item(tree, hf_payload, tvb, offset, -1, ENC_NA);
349 }
350
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)
353 {
354     proto_item         *ti;
355     proto_tree         *flag_tree;
356     norm_packet_data_t *norm_data;
357
358     offset = dissect_grrtetc(tree, tvb, offset);
359
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);
369     offset += 1;
370
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);
374
375     p_add_proto_data(wmem_file_scope(), pinfo, proto_rmt_norm, 0, norm_data);
376
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;
379
380     if (offset < hdrlen2bytes(hlen)) {
381         offset = dissect_norm_hdrext(tree, pinfo, tvb, offset, hlen);
382     }
383     if (tvb_reported_length_remaining(tvb, offset) > 0)
384         proto_tree_add_item(tree, hf_payload, tvb, offset, -1, ENC_NA);
385 }
386
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)
390 {
391     offset = dissect_feccode(tree, tvb, offset, pinfo, 0);
392     if (offset < hdrlen2bytes(hlen)) {
393         offset = dissect_norm_hdrext(tree, pinfo, tvb, offset, hlen);
394     }
395     return offset;
396 }
397
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)
401 {
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;
404
405     if (offset < hdrlen2bytes(hlen)) {
406         offset = dissect_norm_hdrext(tree, pinfo, tvb, offset, hlen);
407     }
408     while (tvb_reported_length_remaining(tvb, offset) > 0) {
409         offset = dissect_nack_data(tree, tvb, offset, pinfo);
410     }
411     return offset;
412 }
413
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)
417 {
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;
420
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);
425     }
426     while (offset < hdrlen2bytes(hlen)) {
427         proto_item *tif;
428         proto_tree *cc_tree, *flag_tree;
429         double grtt;
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);
439         offset += 1;
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;
444     }
445     return offset;
446 }
447
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)
451 {
452     offset = dissect_feccode(tree, tvb, offset, pinfo, 0);
453
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;
456     }
457     return offset;
458 }
459
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)
463 {
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;
467     return offset;
468 }
469
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)
473 {
474     guint8 flavor;
475
476     offset = dissect_grrtetc(tree, tvb, offset);
477     flavor = tvb_get_guint8(tvb, offset);
478
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;
482     switch(flavor) {
483     case NORM_CMD_CC:
484         offset = dissect_norm_cmd_cc(tree, pinfo, tvb, offset, hlen);
485         break;
486     case NORM_CMD_FLUSH:
487         offset = dissect_norm_cmd_flush(tree, pinfo, tvb, offset, hlen);
488         break;
489     case NORM_CMD_SQUELCH:
490         offset = dissect_norm_cmd_squelch(tree, pinfo, tvb, offset);
491         break;
492     case NORM_CMD_REPAIR_ADV:
493         offset = dissect_norm_cmd_repairadv(tree, pinfo, tvb, offset, hlen);
494         break;
495     case NORM_CMD_ACK_REQ:
496         offset = dissect_norm_cmd_ackreq(tree, pinfo, tvb, offset);
497         break;
498     }
499     if (tvb_reported_length_remaining(tvb, offset) > 0)
500         proto_tree_add_item(tree, hf_payload, tvb, offset, -1, ENC_NA);
501 }
502
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)
506 {
507     guint8 acktype;
508
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);
512
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);
521     }
522
523     if (tvb_reported_length_remaining(tvb, offset) > 0)
524         proto_tree_add_item(tree, hf_payload, tvb, offset, -1, ENC_NA);
525 }
526
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)
530 {
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);
538     }
539
540     while (tvb_reported_length_remaining(tvb, offset) > 0) {
541         offset = dissect_nack_data(tree, tvb, offset, pinfo);
542     }
543     if (tvb_reported_length_remaining(tvb, offset) > 0)
544         proto_tree_add_item(tree, hf_payload, tvb, offset, -1, ENC_NA);
545 }
546
547 /* Code to actually dissect the packets */
548 /* ==================================== */
549 static int
550 dissect_norm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
551 {
552     /* Logical packet representation */
553     guint8 version;
554     guint8 type;
555     guint8 hlen;
556
557     /* Offset for subpacket dissection */
558     guint offset = 0;
559
560     /* Set up structures needed to add the protocol subtree and manage it */
561     proto_item *ti;
562     proto_tree *norm_tree;
563
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);
567
568     /* NORM header dissection, part 1 */
569     /* ------------------------------ */
570
571     version = hi_nibble(tvb_get_guint8(tvb, offset));
572
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);
576
577     /* Fill the NORM subtree */
578     proto_tree_add_uint(norm_tree, hf_version, tvb, offset, 1, version);
579
580     /* This dissector supports only NORMv1 packets.
581      * If version > 1 print only version field and quit.
582      */
583     if (version != 1) {
584         expert_add_info(pinfo, ti, &ei_version1_only);
585
586         /* Complete entry in Info column on summary display */
587         col_add_fstr(pinfo->cinfo, COL_INFO, "Version: %u (not supported)", version);
588         return 0;
589     }
590
591     /* NORM header dissection, part 2 */
592     /* ------------------------------ */
593
594     type = lo_nibble(tvb_get_guint8(tvb, offset));
595     hlen = tvb_get_guint8(tvb, offset+1);
596
597     if (tree) {
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);
602     }
603
604     offset += 8;
605
606
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)"));
611
612
613     switch(type) {
614     case NORM_INFO:
615         dissect_norm_info(norm_tree, pinfo, tvb, offset, hlen);
616         break;
617     case NORM_DATA:
618         dissect_norm_data(norm_tree, pinfo, tvb, offset, hlen);
619         break;
620     case NORM_CMD:
621         dissect_norm_cmd(norm_tree, pinfo, tvb, offset, hlen);
622         break;
623     case NORM_ACK:
624         dissect_norm_ack(norm_tree, pinfo, tvb, offset, hlen);
625         break;
626     case NORM_NACK:
627         dissect_norm_nack(norm_tree, pinfo, tvb, offset, hlen);
628         break;
629     default:
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);
633         break;
634     }
635
636     return tvb_reported_length(tvb);
637 }
638
639 static gboolean
640 dissect_norm_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
641 {
642     guint8 byte1;
643
644     if (tvb_reported_length(tvb) < 12)
645         return FALSE;  /* not enough to check */
646     byte1 = tvb_get_guint8(tvb, 0);
647
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;
651
652     dissect_norm(tvb, pinfo, tree, data);
653     return TRUE; /* appears to be a NORM packet */
654 }
655
656 void proto_register_norm(void)
657 {
658     /* Setup NORM header fields */
659     static hf_register_info hf[] = {
660
661         { &hf_version,
662           { "Version", "norm.version",
663             FT_UINT8, BASE_DEC, NULL, 0x0,
664             NULL, HFILL }
665         },
666         { &hf_type,
667           { "Message Type", "norm.type",
668             FT_UINT8, BASE_DEC, VALS(string_norm_type), 0x0,
669             NULL, HFILL }
670         },
671         { &hf_hlen,
672           { "Header length", "norm.hlen",
673             FT_UINT8, BASE_DEC, NULL, 0x0,
674             NULL, HFILL }
675         },
676         { &hf_sequence,
677           { "Sequence", "norm.sequence",
678             FT_UINT16, BASE_DEC, NULL, 0x0,
679             NULL, HFILL }
680         },
681         { &hf_source_id,
682           { "Source ID", "norm.source_id",
683             FT_IPv4, BASE_NONE, NULL, 0x0,
684             NULL, HFILL }
685         },
686         { &hf_instance_id,
687           { "Instance", "norm.instance_id",
688             FT_UINT16, BASE_DEC, NULL, 0x0,
689             NULL, HFILL}
690         },
691         { &hf_grtt,
692           { "grtt", "norm.grtt",
693             FT_DOUBLE, BASE_NONE, NULL, 0x0,
694             NULL, HFILL}
695         },
696         { &hf_backoff,
697           { "Backoff", "norm.backoff",
698             FT_UINT8, BASE_DEC, NULL, 0x0,
699             NULL, HFILL}
700         },
701         { &hf_gsize,
702           { "Group Size", "norm.gsize",
703             FT_DOUBLE, BASE_NONE, NULL, 0x0,
704             NULL, HFILL}
705         },
706         { &hf_flags,
707           { "Flags", "norm.flags",
708             FT_UINT8, BASE_HEX, NULL, 0x0,
709             NULL, HFILL}
710         },
711         { &hf_flag_repair,
712           { "Repair Flag", "norm.flag.repair",
713             FT_BOOLEAN, 8, NULL, NORM_FLAG_REPAIR,
714             NULL, HFILL }
715         },
716         { &hf_flag_norm_explicit,
717           { "Explicit Flag", "norm.flag.explicit",
718             FT_BOOLEAN, 8, NULL, NORM_FLAG_EXPLICIT,
719             NULL, HFILL }
720         },
721         { &hf_flag_info,
722           { "Info Flag", "norm.flag.info",
723             FT_BOOLEAN, 8, NULL, NORM_FLAG_INFO,
724             NULL, HFILL }
725         },
726         { &hf_flag_unreliable,
727           { "Unreliable Flag", "norm.flag.unreliable",
728             FT_BOOLEAN, 8, NULL, NORM_FLAG_UNRELIABLE,
729             NULL, HFILL }
730         },
731         { &hf_flag_file,
732           { "File Flag", "norm.flag.file",
733             FT_BOOLEAN, 8, NULL, NORM_FLAG_FILE,
734             NULL, HFILL }
735         },
736         { &hf_flag_stream,
737           { "Stream Flag", "norm.flag.stream",
738             FT_BOOLEAN, 8, NULL, NORM_FLAG_STREAM,
739             NULL, HFILL }
740         },
741         { &hf_flag_msgstart,
742           { "Msg Start Flag", "norm.flag.msgstart",
743             FT_BOOLEAN, 8, NULL, NORM_FLAG_MSG_START,
744             NULL, HFILL }
745         },
746         { &hf_object_transport_id,
747           { "Object Transport ID", "norm.object_transport_id",
748             FT_UINT16, BASE_HEX, NULL, 0x0,
749             NULL, HFILL}
750         },
751         { &hf_extension,
752           { "Hdr Extension", "norm.hexext",
753             FT_UINT16, BASE_DEC, NULL, 0x0,
754             NULL, HFILL}
755         },
756         { &hf_reserved,
757           { "Reserved", "norm.reserved",
758             FT_UINT16, BASE_HEX, NULL, 0x0,
759             NULL, HFILL}
760         },
761         { &hf_payload_len,
762           { "Payload Len", "norm.payload.len",
763             FT_UINT16, BASE_DEC, NULL, 0x0,
764             NULL, HFILL}
765         },
766         { &hf_payload_offset,
767           { "Payload Offset", "norm.payload.offset",
768             FT_UINT32, BASE_DEC, NULL, 0x0,
769             NULL, HFILL}
770         },
771
772         { &hf_cmd_flavor,
773           { "Flavor", "norm.flavor",
774             FT_UINT8, BASE_DEC, VALS(string_norm_cmd_type), 0x0,
775             NULL, HFILL}
776         },
777         { &hf_cc_sequence,
778           { "CC Sequence", "norm.ccsequence",
779             FT_UINT16, BASE_DEC, NULL, 0x0,
780             NULL, HFILL}
781         },
782         { &hf_cc_sts,
783           { "Send Time secs", "norm.cc_sts",
784             FT_UINT32, BASE_DEC, NULL, 0x0,
785             NULL, HFILL}
786         },
787         { &hf_cc_stus,
788           { "Send Time usecs", "norm.cc_stus",
789             FT_UINT32, BASE_DEC, NULL, 0x0,
790             NULL, HFILL}
791         },
792         { &hf_cc_node_id,
793           { "CC Node ID", "norm.cc_node_id",
794             FT_IPv4, BASE_NONE, NULL, 0x0,
795             NULL, HFILL}
796         },
797         { &hf_cc_flags,
798           { "CC Flags", "norm.cc_flags",
799             FT_UINT8, BASE_DEC, NULL, 0x0,
800             NULL, HFILL}
801         },
802         { &hf_cc_flags_clr,
803           { "CLR", "norm.cc_flags.clr",
804             FT_BOOLEAN, 8, NULL, NORM_FLAG_CC_CLR,
805             NULL, HFILL}
806         },
807         { &hf_cc_flags_plr,
808           { "PLR", "norm.cc_flags.plr",
809             FT_BOOLEAN, 8, NULL, NORM_FLAG_CC_PLR,
810             NULL, HFILL}
811         },
812         { &hf_cc_flags_rtt,
813           { "RTT", "norm.cc_flags.rtt",
814             FT_BOOLEAN, 8, NULL, NORM_FLAG_CC_RTT,
815             NULL, HFILL}
816         },
817         { &hf_cc_flags_start,
818           { "Start", "norm.cc_flags.start",
819             FT_BOOLEAN, 8, NULL, NORM_FLAG_CC_START,
820             NULL, HFILL}
821         },
822         { &hf_cc_flags_leave,
823           { "Leave", "norm.cc_flags.leave",
824             FT_BOOLEAN, 8, NULL, NORM_FLAG_CC_LEAVE,
825             NULL, HFILL}
826         },
827         { &hf_cc_rtt,
828           { "CC RTT", "norm.cc_rtt",
829             FT_DOUBLE, BASE_NONE, NULL, 0x0,
830             NULL, HFILL}
831         },
832         { &hf_cc_rate,
833           { "CC Rate", "norm.cc_rate",
834             FT_DOUBLE, BASE_NONE, NULL, 0x0,
835             NULL, HFILL}
836         },
837         { &hf_cc_transport_id,
838           { "CC Transport ID", "norm.cc_transport_id",
839             FT_UINT16, BASE_DEC, NULL, 0x0,
840             NULL, HFILL}
841         },
842
843         { &hf_ack_source,
844           { "Ack Source", "norm.ack.source",
845             FT_IPv4, BASE_NONE, NULL, 0x0,
846             NULL, HFILL}
847         },
848         { &hf_ack_type,
849           { "Ack Type", "norm.ack.type",
850             FT_UINT8, BASE_DEC, VALS(string_norm_ack_type), 0x0,
851             NULL, HFILL}
852         },
853         { &hf_ack_id,
854           { "Ack ID", "norm.ack.id",
855             FT_UINT8, BASE_DEC, NULL, 0x0,
856             NULL, HFILL}
857         },
858         { &hf_ack_grtt_sec,
859           { "Ack GRTT Sec", "norm.ack.grtt_sec",
860             FT_UINT32, BASE_DEC, NULL, 0x0,
861             NULL, HFILL}
862         },
863         { &hf_ack_grtt_usec,
864           { "Ack GRTT usec", "norm.ack.grtt_usec",
865             FT_UINT32, BASE_DEC, NULL, 0x0,
866             NULL, HFILL}
867         },
868
869         { &hf_nack_server,
870           { "NAck Server", "norm.nack.server",
871             FT_IPv4, BASE_NONE, NULL, 0x0,
872             NULL, HFILL}
873         },
874         { &hf_nack_grtt_sec,
875           { "NAck GRTT Sec", "norm.nack.grtt_sec",
876             FT_UINT32, BASE_DEC, NULL, 0x0,
877             NULL, HFILL}
878         },
879         { &hf_nack_grtt_usec,
880           { "NAck GRTT usec", "norm.nack.grtt_usec",
881             FT_UINT32, BASE_DEC, NULL, 0x0,
882             NULL, HFILL}
883         },
884         { &hf_nack_form,
885           { "NAck FORM", "norm.nack.form",
886             FT_UINT8, BASE_DEC, VALS(string_norm_nack_form), 0x0,
887             NULL, HFILL}
888         },
889         { &hf_nack_flags,
890           { "NAck Flags", "norm.nack.flags",
891             FT_UINT8, BASE_DEC, NULL, 0x0,
892             NULL, HFILL}
893         },
894         { &hf_nack_flags_segment,
895           { "Segment", "norm.nack.flags.segment",
896             FT_BOOLEAN, 8, NULL, NORM_NACK_SEGMENT,
897             NULL, HFILL}
898         },
899         { &hf_nack_flags_block,
900           { "Block", "norm.nack.flags.block",
901             FT_BOOLEAN, 8, NULL, NORM_NACK_BLOCK,
902             NULL, HFILL}
903         },
904         { &hf_nack_flags_info,
905           { "Info", "norm.nack.flags.info",
906             FT_BOOLEAN, 8, NULL, NORM_NACK_INFO,
907             NULL, HFILL}
908         },
909         { &hf_nack_flags_object,
910           { "Object", "norm.nack.flags.object",
911             FT_BOOLEAN, 8, NULL, NORM_NACK_OBJECT,
912             NULL, HFILL}
913         },
914         { &hf_nack_length,
915           { "NAck Length", "norm.nack.length",
916             FT_UINT16, BASE_DEC, NULL, 0x0,
917             NULL, HFILL}
918         },
919         { &hf_payload,
920           { "Payload", "norm.payload",
921             FT_BYTES, BASE_NONE, NULL, 0x0,
922             NULL, HFILL }
923         },
924         { &hf_fec_encoding_id,
925           { "FEC Encoding ID", "norm.fec_encoding_id",
926             FT_UINT8, BASE_DEC, VALS(string_fec_encoding_id), 0x0,
927             NULL, HFILL}
928         }
929     };
930
931     /* Setup protocol subtree array */
932     static gint *ett[] = {
933         &ett_main,
934         &ett_hdrext,
935         &ett_flags,
936         &ett_streampayload,
937         &ett_congestioncontrol,
938         &ett_nackdata
939     };
940
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 }}
943     };
944
945     module_t *module;
946     expert_module_t* expert_rmt_norm;
947
948     /* Register the protocol name and description */
949     proto_rmt_norm = proto_register_protocol("Negative-acknowledgment Oriented Reliable Multicast", "NORM", "norm");
950
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));
956
957
958     /* Register preferences */
959     module = prefs_register_protocol(proto_rmt_norm, NULL);
960     prefs_register_obsolete_preference(module, "heuristic_norm");
961 }
962
963 void proto_reg_handoff_norm(void)
964 {
965     static dissector_handle_t handle;
966
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);
970
971     rmt_fec_handle = find_dissector_add_dependency("rmt-fec", proto_rmt_norm);
972 }
973
974 /*
975  * Editor modelines - http://www.wireshark.org/tools/modelines.html
976  *
977  * Local variables:
978  * c-basic-offset: 4
979  * tab-width: 8
980  * indent-tabs-mode: nil
981  * End:
982  *
983  * ex: set shiftwidth=4 tabstop=8 expandtab:
984  * :indentSize=4:tabSize=8:noTabs=true:
985  */