Create a new REP_NA value for fields where there are no representations
[metze/wireshark/wip.git] / epan / dissectors / packet-m2pa.c
1 /* packet-m2pa.c
2  * Routines for MTP2 Peer Adaptation Layer dissection
3  * It is hopefully (needs testing) compliant to
4  * http://www.ietf.org/internet-drafts/draft-ietf-sigtran-m2pa-02.txt
5  * http://www.ietf.org/internet-drafts/draft-ietf-sigtran-m2pa-08.txt
6  * http://www.ietf.org/internet-drafts/draft-ietf-sigtran-m2pa-12.txt
7  *
8  * Copyright 2001, 2002, Jeff Morriss <jeff.morriss[AT]ulticom.com>,
9  * updated by Michael Tuexen <tuexen [AT] fh-muenster.de>
10  *
11  * $Id$
12  *
13  * Wireshark - Network traffic analyzer
14  * By Gerald Combs <gerald@wireshark.org>
15  * Copyright 1998 Gerald Combs
16  *
17  * Copied from packet-m3ua.c
18  *
19  * This program is free software; you can redistribute it and/or
20  * modify it under the terms of the GNU General Public License
21  * as published by the Free Software Foundation; either version 2
22  * of the License, or (at your option) any later version.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  * GNU General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License
30  * along with this program; if not, write to the Free Software
31  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32  */
33
34
35 #ifdef HAVE_CONFIG_H
36 # include "config.h"
37 #endif
38
39 #include <epan/packet.h>
40 #include <epan/prefs.h>
41 #include <epan/sctpppids.h>
42 #include <epan/expert.h>
43
44 #define SCTP_PORT_M2PA              3565
45
46 static guint global_sctp_port       = SCTP_PORT_M2PA;
47
48 void proto_reg_handoff_m2pa(void);
49
50 static int proto_m2pa      = -1;
51 static module_t *m2pa_module;
52
53 static int hf_version      = -1;
54 static int hf_spare        = -1;
55 static int hf_v2_type      = -1;
56 static int hf_v8_type      = -1;
57 static int hf_v12_type     = -1;
58 static int hf_class        = -1;
59 static int hf_length       = -1;
60 static int hf_unused       = -1;
61 static int hf_bsn          = -1;
62 static int hf_fsn          = -1;
63 static int hf_v2_status    = -1;
64 static int hf_v8_status    = -1;
65 static int hf_v12_status   = -1;
66 static int hf_v2_li_spare  = -1;
67 static int hf_v8_li_spare  = -1;
68 static int hf_v2_li_prio   = -1;
69 static int hf_v8_li_prio   = -1;
70 static int hf_filler       = -1;
71 static int hf_unknown_data = -1;
72 static int hf_pri_prio     = -1;
73 static int hf_pri_spare    = -1;
74 static int hf_undecode_data   = -1;
75
76 static gint ett_m2pa       = -1;
77 static gint ett_m2pa_li    = -1;
78
79 static dissector_handle_t mtp3_handle;
80
81 typedef enum {
82   M2PA_V02 = 1,
83   M2PA_V08 = 2,
84   M2PA_V12 = 3
85 } Version_Type;
86
87 static gint m2pa_version = M2PA_V12;
88
89 #define VERSION_LENGTH         1
90 #define SPARE_LENGTH           1
91 #define CLASS_LENGTH           1
92 #define V2_TYPE_LENGTH         2
93 #define V8_TYPE_LENGTH         1
94 #define V12_TYPE_LENGTH        V8_TYPE_LENGTH
95 #define LENGTH_LENGTH          4
96 #define UNUSED_LENGTH          1
97 #define BSN_LENGTH             3
98 #define FSN_LENGTH             3
99
100 #define V2_HEADER_LENGTH        (VERSION_LENGTH + SPARE_LENGTH + \
101                                 V2_TYPE_LENGTH + LENGTH_LENGTH)
102
103 #define V8_HEADER_LENGTH        (VERSION_LENGTH + SPARE_LENGTH + \
104                                 CLASS_LENGTH + V8_TYPE_LENGTH + LENGTH_LENGTH + \
105                                 UNUSED_LENGTH + BSN_LENGTH + UNUSED_LENGTH + \
106                                 FSN_LENGTH)
107 #define V12_HEADER_LENGTH       V8_HEADER_LENGTH
108
109 #define HEADER_OFFSET          0
110 #define VERSION_OFFSET         HEADER_OFFSET
111 #define SPARE_OFFSET           (VERSION_OFFSET + VERSION_LENGTH)
112 #define CLASS_OFFSET           (SPARE_OFFSET + SPARE_LENGTH)
113 #define V2_TYPE_OFFSET         (SPARE_OFFSET + SPARE_LENGTH)
114 #define V8_TYPE_OFFSET         (CLASS_OFFSET + CLASS_LENGTH)
115 #define V12_TYPE_OFFSET        V8_TYPE_OFFSET
116 #define V8_LENGTH_OFFSET       (V8_TYPE_OFFSET + V8_TYPE_LENGTH)
117 #define V12_LENGTH_OFFSET      V8_LENGTH_OFFSET
118 #define V2_LENGTH_OFFSET       (V2_TYPE_OFFSET + V2_TYPE_LENGTH)
119 #define FIRST_UNUSED_OFFSET    (V8_LENGTH_OFFSET + LENGTH_LENGTH)
120 #define BSN_OFFSET             (FIRST_UNUSED_OFFSET + UNUSED_LENGTH)
121 #define SECOND_UNUSED_OFFSET   (BSN_OFFSET + BSN_LENGTH)
122 #define FSN_OFFSET             (SECOND_UNUSED_OFFSET + UNUSED_LENGTH)
123
124 static const value_string protocol_version_values[] = {
125   { 1,      "Release 1" },
126   { 0,      NULL } };
127
128 static const value_string message_class_values[] = {
129   { 0xb,    "M2PA" },
130   { 0,      NULL } };
131
132 #define V2_USER_DATA_TYPE   0x0601
133 #define V2_LINK_STATUS_TYPE 0x0602
134
135 static const value_string v2_message_type_values[] = {
136   { V2_USER_DATA_TYPE,   "User Data" },
137   { V2_LINK_STATUS_TYPE, "Link Status" },
138   { 0,                   NULL } };
139
140 #define V8_USER_DATA_TYPE   0x0001
141 #define V8_LINK_STATUS_TYPE 0x0002
142
143 static const value_string v8_message_type_values[] = {
144   { V8_USER_DATA_TYPE,   "User Data" },
145   { V8_LINK_STATUS_TYPE, "Link Status" },
146   { 0,                   NULL } };
147
148 #define V12_USER_DATA_TYPE   V8_USER_DATA_TYPE
149 #define V12_LINK_STATUS_TYPE V8_LINK_STATUS_TYPE
150
151 static const value_string v12_message_type_values[] = {
152   { V12_USER_DATA_TYPE,   "User Data" },
153   { V12_LINK_STATUS_TYPE, "Link Status" },
154   { 0,                   NULL } };
155
156 static void
157 dissect_v2_header(tvbuff_t *header_tvb, packet_info *pinfo, proto_tree *m2pa_tree)
158 {
159   guint16 message_type;
160
161   message_type  = tvb_get_ntohs(header_tvb, V2_TYPE_OFFSET);
162
163   if (check_col(pinfo->cinfo, COL_INFO))
164     col_add_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str(message_type, v2_message_type_values, "reserved"));
165
166   if (m2pa_tree) {
167     proto_tree_add_item(m2pa_tree, hf_version, header_tvb, VERSION_OFFSET,       VERSION_LENGTH, REP_BIG_ENDIAN);
168     proto_tree_add_item(m2pa_tree, hf_spare,   header_tvb, SPARE_OFFSET,         SPARE_LENGTH,   REP_BIG_ENDIAN);
169     proto_tree_add_item(m2pa_tree, hf_v2_type, header_tvb, V2_TYPE_OFFSET,       V2_TYPE_LENGTH, REP_BIG_ENDIAN);
170     proto_tree_add_item(m2pa_tree, hf_length,  header_tvb, V2_LENGTH_OFFSET,     LENGTH_LENGTH,  REP_BIG_ENDIAN);
171   }
172 }
173
174 static void
175 dissect_v8_header(tvbuff_t *header_tvb, packet_info *pinfo, proto_tree *m2pa_tree)
176 {
177   guint8 message_type;
178
179   message_type  = tvb_get_guint8(header_tvb, V8_TYPE_OFFSET);
180
181   if (check_col(pinfo->cinfo, COL_INFO))
182     col_add_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str(message_type, v8_message_type_values, "Unknown"));
183
184   if (m2pa_tree) {
185     proto_tree_add_item(m2pa_tree, hf_version, header_tvb, VERSION_OFFSET,       VERSION_LENGTH, REP_BIG_ENDIAN);
186     proto_tree_add_item(m2pa_tree, hf_spare,   header_tvb, SPARE_OFFSET,         SPARE_LENGTH,   REP_BIG_ENDIAN);
187     proto_tree_add_item(m2pa_tree, hf_class,   header_tvb, CLASS_OFFSET,         CLASS_LENGTH,   REP_BIG_ENDIAN);
188     proto_tree_add_item(m2pa_tree, hf_v8_type, header_tvb, V8_TYPE_OFFSET,       V8_TYPE_LENGTH, REP_BIG_ENDIAN);
189     proto_tree_add_item(m2pa_tree, hf_length,  header_tvb, V8_LENGTH_OFFSET,     LENGTH_LENGTH,  REP_BIG_ENDIAN);
190     proto_tree_add_item(m2pa_tree, hf_unused,  header_tvb, FIRST_UNUSED_OFFSET,  UNUSED_LENGTH,  REP_BIG_ENDIAN);
191     proto_tree_add_item(m2pa_tree, hf_bsn,     header_tvb, BSN_OFFSET,           BSN_LENGTH,     REP_BIG_ENDIAN);
192     proto_tree_add_item(m2pa_tree, hf_unused,  header_tvb, SECOND_UNUSED_OFFSET, UNUSED_LENGTH,  REP_BIG_ENDIAN);
193     proto_tree_add_item(m2pa_tree, hf_fsn,     header_tvb, FSN_OFFSET,           FSN_LENGTH,     REP_BIG_ENDIAN);
194   }
195 }
196
197 static void
198 dissect_v12_header(tvbuff_t *header_tvb, packet_info *pinfo, proto_tree *m2pa_tree)
199 {
200   guint8 message_type;
201
202   message_type  = tvb_get_guint8(header_tvb, V8_TYPE_OFFSET);
203
204   if (check_col(pinfo->cinfo, COL_INFO))
205     col_add_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str(message_type, v8_message_type_values, "Unknown"));
206
207   if (m2pa_tree) {
208     proto_tree_add_item(m2pa_tree, hf_version,  header_tvb, VERSION_OFFSET,       VERSION_LENGTH,  REP_BIG_ENDIAN);
209     proto_tree_add_item(m2pa_tree, hf_spare,    header_tvb, SPARE_OFFSET,         SPARE_LENGTH,    REP_BIG_ENDIAN);
210     proto_tree_add_item(m2pa_tree, hf_class,    header_tvb, CLASS_OFFSET,         CLASS_LENGTH,    REP_BIG_ENDIAN);
211     proto_tree_add_item(m2pa_tree, hf_v12_type, header_tvb, V12_TYPE_OFFSET,      V12_TYPE_LENGTH, REP_BIG_ENDIAN);
212     proto_tree_add_item(m2pa_tree, hf_length,   header_tvb, V12_LENGTH_OFFSET,    LENGTH_LENGTH,   REP_BIG_ENDIAN);
213     proto_tree_add_item(m2pa_tree, hf_unused,   header_tvb, FIRST_UNUSED_OFFSET,  UNUSED_LENGTH,   REP_BIG_ENDIAN);
214     proto_tree_add_item(m2pa_tree, hf_bsn,      header_tvb, BSN_OFFSET,           BSN_LENGTH,      REP_BIG_ENDIAN);
215     proto_tree_add_item(m2pa_tree, hf_unused,   header_tvb, SECOND_UNUSED_OFFSET, UNUSED_LENGTH,   REP_BIG_ENDIAN);
216     proto_tree_add_item(m2pa_tree, hf_fsn,      header_tvb, FSN_OFFSET,           FSN_LENGTH,      REP_BIG_ENDIAN);
217   }
218 }
219
220 #define LI_OFFSET           0
221 #define LI_LENGTH           1
222 #define MTP3_OFFSET         (LI_OFFSET + LI_LENGTH)
223
224 #define V2_LI_SPARE_MASK    0xfc
225 #define V2_LI_PRIORITY_MASK 0x3
226
227 static void
228 dissect_v2_user_data_message(tvbuff_t *message_data_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
229 {
230   proto_item *m2pa_li_item;
231   proto_tree *m2pa_li_tree;
232   tvbuff_t *payload_tvb;
233
234   if (tvb_length(message_data_tvb) > 0) {
235     if (m2pa_tree) {
236       m2pa_li_item = proto_tree_add_text(m2pa_tree, message_data_tvb, LI_OFFSET, LI_LENGTH, "Length Indicator");
237       m2pa_li_tree = proto_item_add_subtree(m2pa_li_item, ett_m2pa_li);
238
239       proto_tree_add_item(m2pa_li_tree, hf_v2_li_spare, message_data_tvb, LI_OFFSET, LI_LENGTH, REP_BIG_ENDIAN);
240       proto_tree_add_item(m2pa_li_tree, hf_v2_li_prio,  message_data_tvb, LI_OFFSET, LI_LENGTH, REP_BIG_ENDIAN);
241
242       /* Re-adjust length of M2PA item since it will be dissected as MTP3 */
243       proto_item_set_len(m2pa_item, V2_HEADER_LENGTH + LI_LENGTH);
244      }
245   }
246
247   payload_tvb = tvb_new_subset_remaining(message_data_tvb, MTP3_OFFSET);
248   call_dissector(mtp3_handle, payload_tvb, pinfo, tree);
249 }
250
251 #define V8_LI_SPARE_MASK        0x3f
252 #define V8_LI_PRIORITY_MASK     0xc0
253
254 static void
255 dissect_v8_user_data_message(tvbuff_t *message_data_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
256 {
257   proto_item *m2pa_li_item;
258   proto_tree *m2pa_li_tree;
259   tvbuff_t *payload_tvb;
260
261   if (tvb_length(message_data_tvb) > 0) {
262     if (m2pa_tree) {
263       m2pa_li_item = proto_tree_add_text(m2pa_tree, message_data_tvb, LI_OFFSET, LI_LENGTH, "Length Indicator");
264       m2pa_li_tree = proto_item_add_subtree(m2pa_li_item, ett_m2pa_li);
265       proto_tree_add_item(m2pa_li_tree, hf_v8_li_prio,  message_data_tvb, LI_OFFSET, LI_LENGTH, REP_BIG_ENDIAN);
266       proto_tree_add_item(m2pa_li_tree, hf_v8_li_spare, message_data_tvb, LI_OFFSET, LI_LENGTH, REP_BIG_ENDIAN);
267
268         /* Re-adjust length of M2PA item since it will be dissected as MTP3 */
269       proto_item_set_len(m2pa_item, V8_HEADER_LENGTH + LI_LENGTH);
270     }
271
272     payload_tvb = tvb_new_subset_remaining(message_data_tvb, MTP3_OFFSET);
273     call_dissector(mtp3_handle, payload_tvb, pinfo, tree);
274   }
275 }
276
277 #define PRIORITY_MASK     0xc0
278 #define SPARE_MASK        0x3f
279
280 #define PRI_OFFSET           0
281 #define PRI_LENGTH           1
282
283 static void
284 dissect_v12_user_data_message(tvbuff_t *message_data_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
285 {
286   proto_item *m2pa_li_item;
287   proto_tree *m2pa_li_tree;
288   tvbuff_t *payload_tvb;
289
290   if (tvb_length(message_data_tvb) > 0) {
291     if (m2pa_tree) {
292       m2pa_li_item = proto_tree_add_text(m2pa_tree, message_data_tvb, PRI_OFFSET, PRI_LENGTH, "Priority");
293       m2pa_li_tree = proto_item_add_subtree(m2pa_li_item, ett_m2pa_li);
294       proto_tree_add_item(m2pa_li_tree, hf_pri_prio,  message_data_tvb, PRI_OFFSET, PRI_LENGTH, REP_BIG_ENDIAN);
295       proto_tree_add_item(m2pa_li_tree, hf_pri_spare, message_data_tvb, PRI_OFFSET, PRI_LENGTH, REP_BIG_ENDIAN);
296
297         /* Re-adjust length of M2PA item since it will be dissected as MTP3 */
298       proto_item_set_len(m2pa_item, V12_HEADER_LENGTH + PRI_LENGTH);
299     }
300
301     payload_tvb = tvb_new_subset_remaining(message_data_tvb, MTP3_OFFSET);
302     call_dissector(mtp3_handle, payload_tvb, pinfo, tree);
303   }
304 }
305
306 static const value_string v2_link_status_values[] = {
307   { 1, "In Service" },
308   { 2, "Processor Outage" },
309   { 3, "Processor Outage Ended" },
310   { 4, "Busy" },
311   { 5, "Busy Ended" },
312   { 0, NULL } };
313
314 #define STATUS_LENGTH    4
315 #define STATUS_OFFSET    0
316 #define FILLER_OFFSET    (STATUS_OFFSET + STATUS_LENGTH)
317
318 static void
319 dissect_v2_link_status_message(tvbuff_t *message_data_tvb, packet_info *pinfo, proto_tree *m2pa_tree)
320 {
321   if (check_col(pinfo->cinfo, COL_INFO))
322     col_append_fstr(pinfo->cinfo, COL_INFO, "(%s) ", val_to_str(tvb_get_ntohl(message_data_tvb, STATUS_OFFSET), v2_link_status_values, "Unknown"));
323   if (m2pa_tree)
324     proto_tree_add_item(m2pa_tree, hf_v2_status, message_data_tvb, STATUS_OFFSET, STATUS_LENGTH, REP_BIG_ENDIAN);
325 }
326
327 static const value_string v8_link_status_values[] = {
328   { 1, "Alignment" },
329   { 2, "Proving Normal" },
330   { 3, "Proving Emergency" },
331   { 4, "Ready" },
332   { 5, "Processor Outage" },
333   { 6, "Processor Outage Ended" },
334   { 7, "Busy" },
335   { 8, "Busy Ended" },
336   { 9, "Out of Service" },
337   { 0, NULL } };
338
339 static void
340 dissect_v8_link_status_message(tvbuff_t *message_data_tvb, packet_info *pinfo, proto_tree *m2pa_tree)
341 {
342   guint16 filler_length;
343
344   if (check_col(pinfo->cinfo, COL_INFO))
345     col_append_fstr(pinfo->cinfo, COL_INFO, "(%s) ", val_to_str(tvb_get_ntohl(message_data_tvb, STATUS_OFFSET), v8_link_status_values, "Unknown"));
346
347   filler_length = tvb_length(message_data_tvb) - STATUS_LENGTH;
348
349   proto_tree_add_item(m2pa_tree, hf_v8_status, message_data_tvb, STATUS_OFFSET, STATUS_LENGTH, REP_BIG_ENDIAN);
350   if (filler_length > 0)
351       proto_tree_add_item(m2pa_tree, hf_filler, message_data_tvb, FILLER_OFFSET, filler_length, REP_BIG_ENDIAN);
352 }
353
354 static const value_string v12_link_status_values[] = {
355   { 1, "Alignment" },
356   { 2, "Proving Normal" },
357   { 3, "Proving Emergency" },
358   { 4, "Ready" },
359   { 5, "Processor Outage" },
360   { 6, "Processor Outage Ended" },
361   { 7, "Busy" },
362   { 8, "Busy Ended" },
363   { 9, "Out of Service" },
364   { 0, NULL } };
365
366 static void
367 dissect_v12_link_status_message(tvbuff_t *message_data_tvb, packet_info *pinfo, proto_tree *m2pa_tree)
368 {
369   guint16 filler_length;
370
371   if (check_col(pinfo->cinfo, COL_INFO))
372     col_append_fstr(pinfo->cinfo, COL_INFO, "(%s) ", val_to_str(tvb_get_ntohl(message_data_tvb, STATUS_OFFSET), v12_link_status_values, "Unknown"));
373
374   filler_length = tvb_length(message_data_tvb) - STATUS_LENGTH;
375
376   proto_tree_add_item(m2pa_tree, hf_v12_status, message_data_tvb, STATUS_OFFSET, STATUS_LENGTH, REP_BIG_ENDIAN);
377   if (filler_length > 0)
378       proto_tree_add_item(m2pa_tree, hf_filler, message_data_tvb, FILLER_OFFSET, filler_length, REP_BIG_ENDIAN);
379 }
380
381 static void
382 dissect_unknown_message(tvbuff_t *message_data_tvb, proto_tree *m2pa_tree)
383 {
384   guint length;
385
386   length = tvb_length(message_data_tvb);
387   if ((m2pa_tree) && (length > 0))
388     proto_tree_add_item(m2pa_tree, hf_unknown_data, message_data_tvb, 0, length, REP_BIG_ENDIAN);
389 }
390
391 #define V2_MESSAGE_DATA_OFFSET (HEADER_OFFSET + V2_HEADER_LENGTH)
392
393 static void
394 dissect_v2_message_data(tvbuff_t *message_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
395 {
396   guint32 message_data_length;
397   guint16 type;
398   tvbuff_t *message_data_tvb;
399
400   message_data_length = (gint) tvb_get_ntohl(message_tvb, V2_LENGTH_OFFSET);
401   if ((gint) message_data_length < 1) {
402     if (m2pa_tree)
403       proto_tree_add_text(m2pa_tree, message_tvb, V2_LENGTH_OFFSET, 4,
404         "Invalid message data length: %u", message_data_length);
405     THROW(ReportedBoundsError);
406   }
407
408   message_data_tvb    = tvb_new_subset(message_tvb, V2_MESSAGE_DATA_OFFSET, message_data_length, message_data_length);
409   type                = tvb_get_ntohs(message_tvb, V2_TYPE_OFFSET);
410
411   switch(type) {
412   case V2_USER_DATA_TYPE:
413     dissect_v2_user_data_message(message_data_tvb, pinfo, m2pa_item, m2pa_tree, tree);
414     break;
415   case V2_LINK_STATUS_TYPE:
416     dissect_v2_link_status_message(message_data_tvb, pinfo, m2pa_tree);
417     break;
418   default:
419     dissect_unknown_message(message_data_tvb, m2pa_tree);
420   }
421 }
422
423 #define V8_MESSAGE_DATA_OFFSET (HEADER_OFFSET + V8_HEADER_LENGTH)
424
425 static void
426 dissect_v8_message_data(tvbuff_t *message_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
427 {
428   guint32 message_data_length;
429   guint8 type;
430   tvbuff_t *message_data_tvb;
431
432   message_data_length = tvb_get_ntohl(message_tvb, V8_LENGTH_OFFSET) - V8_HEADER_LENGTH;
433   if ((gint) message_data_length < 1) {
434     if (m2pa_tree)
435       proto_tree_add_text(m2pa_tree, message_tvb, V8_LENGTH_OFFSET, 4,
436         "Invalid message data length: %u", message_data_length);
437     THROW(ReportedBoundsError);
438   }
439   message_data_tvb    = tvb_new_subset(message_tvb, V8_MESSAGE_DATA_OFFSET, message_data_length, message_data_length);
440   type                = tvb_get_guint8(message_tvb, V8_TYPE_OFFSET);
441
442
443   switch(type) {
444   case V8_USER_DATA_TYPE:
445     dissect_v8_user_data_message(message_data_tvb, pinfo, m2pa_item, m2pa_tree, tree);
446     break;
447   case V8_LINK_STATUS_TYPE:
448     dissect_v8_link_status_message(message_data_tvb, pinfo, m2pa_tree);
449     break;
450   default:
451     dissect_unknown_message(message_data_tvb, m2pa_tree);
452   }
453 }
454
455 #define V12_MESSAGE_DATA_OFFSET (HEADER_OFFSET + V12_HEADER_LENGTH)
456
457 static void
458 dissect_v12_message_data(tvbuff_t *message_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
459 {
460   guint32 length, message_data_length, actual_length;
461   guint8 type;
462   tvbuff_t *message_data_tvb;
463
464   length              = tvb_get_ntohl(message_tvb, V12_LENGTH_OFFSET);
465   message_data_length = length - V12_HEADER_LENGTH;
466   message_data_tvb    = tvb_new_subset(message_tvb, V12_MESSAGE_DATA_OFFSET, message_data_length, message_data_length);
467   type                = tvb_get_guint8(message_tvb, V12_TYPE_OFFSET);
468
469
470   switch(type) {
471   case V12_USER_DATA_TYPE:
472     dissect_v12_user_data_message(message_data_tvb, pinfo, m2pa_item, m2pa_tree, tree);
473     break;
474   case V12_LINK_STATUS_TYPE:
475     dissect_v12_link_status_message(message_data_tvb, pinfo, m2pa_tree);
476     break;
477   default:
478     dissect_unknown_message(message_data_tvb, m2pa_tree);
479   }
480
481   actual_length = tvb_reported_length(message_tvb);
482
483   if (actual_length > length)
484   {
485     proto_item *pi;
486
487     pi = proto_tree_add_item(m2pa_tree, hf_undecode_data, message_tvb, length, (actual_length - length), REP_BIG_ENDIAN);
488     expert_add_info_format(pinfo, pi, PI_MALFORMED, PI_WARN,
489                            "There are %d bytes of data which is greater than M2PA's length parameter (%d)",
490                            actual_length, length);
491   }
492 }
493
494 static void
495 dissect_v2_message(tvbuff_t *message_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
496 {
497   dissect_v2_header(message_tvb, pinfo, m2pa_tree);
498   dissect_v2_message_data(message_tvb, pinfo, m2pa_item, m2pa_tree, tree);
499 }
500
501 static void
502 dissect_v8_message(tvbuff_t *message_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
503 {
504   dissect_v8_header(message_tvb, pinfo, m2pa_tree);
505   dissect_v8_message_data(message_tvb, pinfo, m2pa_item, m2pa_tree, tree);
506 }
507
508 static void
509 dissect_v12_message(tvbuff_t *message_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
510 {
511   dissect_v12_header(message_tvb, pinfo, m2pa_tree);
512   dissect_v12_message_data(message_tvb, pinfo, m2pa_item, m2pa_tree, tree);
513 }
514
515 static void
516 dissect_m2pa(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
517 {
518   proto_item *m2pa_item;
519   proto_tree *m2pa_tree;
520
521   if (check_col(pinfo->cinfo, COL_PROTOCOL))
522     switch(m2pa_version) {
523     case M2PA_V02:
524       col_set_str(pinfo->cinfo, COL_PROTOCOL, "M2PA (ID 02)");
525       break;
526     case M2PA_V08:
527       col_set_str(pinfo->cinfo, COL_PROTOCOL, "M2PA (ID 08)");
528       break;
529     case M2PA_V12:
530       col_set_str(pinfo->cinfo, COL_PROTOCOL, "M2PA (ID 12)");
531       break;
532     };
533
534   if (tree) {
535     m2pa_item = proto_tree_add_item(tree, proto_m2pa, tvb, 0, -1, REP_NA);
536     m2pa_tree = proto_item_add_subtree(m2pa_item, ett_m2pa);
537   } else {
538     m2pa_item = NULL;
539     m2pa_tree = NULL;
540   }
541
542   switch(m2pa_version) {
543     case M2PA_V02:
544       dissect_v2_message(tvb, pinfo, m2pa_item, m2pa_tree, tree);
545       break;
546     case M2PA_V08:
547       dissect_v8_message(tvb, pinfo, m2pa_item, m2pa_tree, tree);
548       break;
549     case M2PA_V12:
550       dissect_v12_message(tvb, pinfo, m2pa_item, m2pa_tree, tree);
551       break;
552   };
553 }
554
555 void
556 proto_register_m2pa(void)
557 {
558   static hf_register_info hf[] =
559   { { &hf_version,      { "Version",        "m2pa.version",        FT_UINT8,  BASE_DEC,  VALS(protocol_version_values), 0x0,                 NULL, HFILL} },
560     { &hf_spare,        { "Spare",          "m2pa.spare",          FT_UINT8,  BASE_HEX,  NULL,                          0x0,                 NULL, HFILL} },
561     { &hf_v2_type,      { "Message Type",   "m2pa.type",           FT_UINT16, BASE_HEX,  VALS(v2_message_type_values),  0x0,                 NULL, HFILL} },
562     { &hf_v8_type,      { "Message Type",   "m2pa.type",           FT_UINT8,  BASE_DEC,  VALS(v8_message_type_values),  0x0,                 NULL, HFILL} },
563     { &hf_v12_type,     { "Message Type",   "m2pa.type",           FT_UINT8,  BASE_DEC,  VALS(v12_message_type_values), 0x0,                 NULL, HFILL} },
564     { &hf_class,        { "Message Class",  "m2pa.class",          FT_UINT8,  BASE_DEC,  VALS(message_class_values),    0x0,                 NULL, HFILL} },
565     { &hf_length,       { "Message length", "m2pa.length",         FT_UINT32, BASE_DEC,  NULL,                          0x0,                 NULL, HFILL} },
566     { &hf_unused,       { "Unused",         "m2pa.unused",         FT_UINT8,  BASE_DEC,  NULL,                          0x0,                 NULL, HFILL} },
567     { &hf_bsn,          { "BSN",            "m2pa.bsn",            FT_UINT24, BASE_DEC,  NULL,                          0x0,                 NULL, HFILL} },
568     { &hf_fsn,          { "FSN",            "m2pa.fsn",            FT_UINT24, BASE_DEC,  NULL,                          0x0,                 NULL, HFILL} },
569     { &hf_v2_li_spare,  { "Spare",          "m2pa.li_spare",       FT_UINT8,  BASE_DEC,  NULL,                          V2_LI_SPARE_MASK,    NULL, HFILL} },
570     { &hf_v8_li_spare,  { "Spare",          "m2pa.li_spare",       FT_UINT8,  BASE_HEX,  NULL,                          V8_LI_SPARE_MASK,    NULL, HFILL} },
571     { &hf_pri_spare,    { "Spare",          "m2pa.priority_spare", FT_UINT8,  BASE_HEX,  NULL,                          SPARE_MASK,          NULL, HFILL} },
572     { &hf_v2_li_prio,   { "Priority",       "m2pa.li_priority",    FT_UINT8,  BASE_DEC,  NULL,                          V2_LI_PRIORITY_MASK, NULL, HFILL} },
573     { &hf_v8_li_prio,   { "Priority",       "m2pa.li_priority",    FT_UINT8,  BASE_HEX,  NULL,                          V8_LI_PRIORITY_MASK, NULL, HFILL} },
574     { &hf_pri_prio,     { "Priority",       "m2pa.priority",       FT_UINT8,  BASE_HEX,  NULL,                          PRIORITY_MASK,       NULL, HFILL} },
575     { &hf_v2_status,    { "Link Status",    "m2pa.status",         FT_UINT32, BASE_DEC,  VALS(v2_link_status_values),   0x0,                 NULL, HFILL} },
576     { &hf_v8_status,    { "Link Status",    "m2pa.status",         FT_UINT32, BASE_DEC,  VALS(v8_link_status_values),   0x0,                 NULL, HFILL} },
577     { &hf_v12_status,   { "Link Status",    "m2pa.status",         FT_UINT32, BASE_DEC,  VALS(v12_link_status_values),  0x0,                 NULL, HFILL} },
578     { &hf_filler,       { "Filler",         "m2pa.filler",         FT_BYTES,  BASE_NONE, NULL,                          0x0,                 NULL, HFILL} },
579     { &hf_unknown_data, { "Unknown Data",   "m2pa.unknown_data",   FT_BYTES,  BASE_NONE, NULL,                          0x0,                 NULL, HFILL} },
580     { &hf_undecode_data,{ "Undecoded data", "m2pa.undecoded_data", FT_BYTES,  BASE_NONE, NULL,                          0x0,                 NULL, HFILL} }
581   };
582
583   static gint *ett[] = {
584     &ett_m2pa,
585     &ett_m2pa_li
586   };
587
588   static enum_val_t m2pa_version_options[] = {
589     { "draft-2",  "Internet Draft version 2",  M2PA_V02 },
590     { "draft-8",  "Internet Draft version 8",  M2PA_V08 },
591     { "draft-12", "Internet Draft version 12", M2PA_V12 },
592     { NULL, NULL, 0 }
593   };
594
595   proto_m2pa = proto_register_protocol("MTP2 Peer Adaptation Layer", "M2PA", "m2pa");
596
597   proto_register_field_array(proto_m2pa, hf, array_length(hf));
598   proto_register_subtree_array(ett, array_length(ett));
599
600   /* Allow other dissectors to find this one by name. */
601   register_dissector("m2pa", dissect_m2pa, proto_m2pa);
602
603   m2pa_module = prefs_register_protocol(proto_m2pa, proto_reg_handoff_m2pa);
604
605   prefs_register_enum_preference(m2pa_module, "version", "M2PA version", "Version used by Wireshark", &m2pa_version, m2pa_version_options, FALSE);
606   prefs_register_uint_preference(m2pa_module, "port", "M2PA SCTP Port", "Set the port for M2PA messages (Default of 3565)", 10, &global_sctp_port);
607 }
608
609 void
610 proto_reg_handoff_m2pa(void)
611 {
612   static gboolean prefs_initialized = FALSE;
613   static dissector_handle_t m2pa_handle;
614   static guint sctp_port;
615
616   /* Port preferences code shamelessly copied from packet-beep.c */
617   if (!prefs_initialized) {
618     m2pa_handle   = find_dissector("m2pa");
619     mtp3_handle   = find_dissector("mtp3");
620
621     dissector_add("sctp.ppi", M2PA_PAYLOAD_PROTOCOL_ID, m2pa_handle);
622
623     prefs_initialized = TRUE;
624
625   } else {
626
627     dissector_delete("sctp.port", sctp_port, m2pa_handle);
628
629   }
630
631   /* Set our port number for future use */
632   sctp_port = global_sctp_port;
633
634   dissector_add("sctp.port", sctp_port, m2pa_handle);
635 }