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