from todd s
[obnox/wireshark/wip.git] / 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-11.txt
6  *
7  * Copyright 2001, 2002, Jeff Morriss <jeff.morriss[AT]ulticom.com>,
8  * updated by Michael Tuexen <tuexen [AT] fh-muenster.de>
9  *
10  * $Id: packet-m2pa.c,v 1.24 2004/05/24 02:25:18 guy Exp $
11  *
12  * Ethereal - Network traffic analyzer
13  * By Gerald Combs <gerald@ethereal.com>
14  * Copyright 1998 Gerald Combs
15  *
16  * Copied from packet-m3ua.c
17  *
18  * This program is free software; you can redistribute it and/or
19  * modify it under the terms of the GNU General Public License
20  * as published by the Free Software Foundation; either version 2
21  * of the License, or (at your option) any later version.
22  *
23  * This program is distributed in the hope that it will be useful,
24  * but WITHOUT ANY WARRANTY; without even the implied warranty of
25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26  * GNU General Public License for more details.
27  *
28  * You should have received a copy of the GNU General Public License
29  * along with this program; if not, write to the Free Software
30  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31  */
32
33
34 #ifdef HAVE_CONFIG_H
35 # include "config.h"
36 #endif
37
38 #include <epan/packet.h>
39 #include "prefs.h"
40 #include "sctpppids.h"
41
42 #define NETWORK_BYTE_ORDER          FALSE
43 #define SCTP_PORT_M2PA              3565
44
45 static int global_sctp_port       = SCTP_PORT_M2PA;
46 static int sctp_port              = 0;
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_class        = -1;
58 static int hf_length       = -1;
59 static int hf_unused       = -1;
60 static int hf_bsn          = -1;
61 static int hf_fsn          = -1;
62 static int hf_v2_status    = -1;
63 static int hf_v8_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
71 static gint ett_m2pa       = -1;
72 static gint ett_m2pa_li    = -1;
73
74 static int mtp3_proto_id;
75 static dissector_handle_t mtp3_handle;
76
77 typedef enum {
78   M2PA_V2 = 1,
79   M2PA_V11 = 2
80 } Version_Type;
81
82 static Version_Type m2pa_version = M2PA_V11;
83
84 #define VERSION_LENGTH         1
85 #define SPARE_LENGTH           1
86 #define CLASS_LENGTH           1
87 #define V2_TYPE_LENGTH         2
88 #define V8_TYPE_LENGTH         1
89 #define LENGTH_LENGTH          4
90 #define UNUSED_LENGTH          1
91 #define BSN_LENGTH             3
92 #define FSN_LENGTH             3
93
94 #define V2_HEADER_LENGTH        (VERSION_LENGTH + SPARE_LENGTH + \
95                                 V2_TYPE_LENGTH + LENGTH_LENGTH)
96                                 
97 #define V8_HEADER_LENGTH        (VERSION_LENGTH + SPARE_LENGTH + \
98                                 CLASS_LENGTH + V8_TYPE_LENGTH + LENGTH_LENGTH + \
99                                 UNUSED_LENGTH + BSN_LENGTH + UNUSED_LENGTH + \
100                                 FSN_LENGTH)
101
102 #define HEADER_OFFSET          0
103 #define VERSION_OFFSET         HEADER_OFFSET
104 #define SPARE_OFFSET           (VERSION_OFFSET + VERSION_LENGTH)
105 #define CLASS_OFFSET           (SPARE_OFFSET + SPARE_LENGTH)
106 #define V2_TYPE_OFFSET         (SPARE_OFFSET + SPARE_LENGTH)
107 #define V8_TYPE_OFFSET         (CLASS_OFFSET + CLASS_LENGTH)
108 #define V8_LENGTH_OFFSET       (V8_TYPE_OFFSET + V8_TYPE_LENGTH)
109 #define V2_LENGTH_OFFSET       (V2_TYPE_OFFSET + V2_TYPE_LENGTH)
110 #define FIRST_UNUSED_OFFSET    (V8_LENGTH_OFFSET + LENGTH_LENGTH)
111 #define BSN_OFFSET             (FIRST_UNUSED_OFFSET + UNUSED_LENGTH)
112 #define SECOND_UNUSED_OFFSET   (BSN_OFFSET + BSN_LENGTH)
113 #define FSN_OFFSET             (SECOND_UNUSED_OFFSET + UNUSED_LENGTH)
114
115 static const value_string protocol_version_values[] = {
116   { 1,      "Release 1" },
117   { 0,      NULL } };
118
119 static const value_string message_class_values[] = {
120   { 0xb,    "M2PA" },
121   { 0,      NULL } };
122   
123 #define V2_USER_DATA_TYPE   0x0601
124 #define V2_LINK_STATUS_TYPE 0x0602
125
126 static const value_string v2_message_type_values[] = {
127   { V2_USER_DATA_TYPE,   "User Data" },
128   { V2_LINK_STATUS_TYPE, "Link Status" },
129   { 0,                   NULL } };
130
131 #define V8_USER_DATA_TYPE   0x0001
132 #define V8_LINK_STATUS_TYPE 0x0002
133
134 static const value_string v8_message_type_values[] = {
135   { V8_USER_DATA_TYPE,   "User Data" },
136   { V8_LINK_STATUS_TYPE, "Link Status" },
137   { 0,                   NULL } };
138
139 static void
140 dissect_v2_header(tvbuff_t *header_tvb, packet_info *pinfo, proto_tree *m2pa_tree)
141 {
142   guint16 message_type;
143   
144   message_type  = tvb_get_ntohs(header_tvb, V2_TYPE_OFFSET);
145
146   if (check_col(pinfo->cinfo, COL_INFO))
147     col_add_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str(message_type, v2_message_type_values, "reserved"));
148
149   if (m2pa_tree) {
150     proto_tree_add_item(m2pa_tree, hf_version, header_tvb, VERSION_OFFSET,       VERSION_LENGTH, NETWORK_BYTE_ORDER);
151     proto_tree_add_item(m2pa_tree, hf_spare,   header_tvb, SPARE_OFFSET,         SPARE_LENGTH,   NETWORK_BYTE_ORDER);
152     proto_tree_add_item(m2pa_tree, hf_v2_type, header_tvb, V2_TYPE_OFFSET,       V2_TYPE_LENGTH, NETWORK_BYTE_ORDER);
153     proto_tree_add_item(m2pa_tree, hf_length,  header_tvb, V2_LENGTH_OFFSET,     LENGTH_LENGTH,  NETWORK_BYTE_ORDER);
154   }
155 }
156
157 static void
158 dissect_v8_header(tvbuff_t *header_tvb, packet_info *pinfo, proto_tree *m2pa_tree)
159 {
160   guint8 message_type;
161   
162   message_type  = tvb_get_guint8(header_tvb, V8_TYPE_OFFSET);
163
164   if (check_col(pinfo->cinfo, COL_INFO))
165     col_add_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str(message_type, v8_message_type_values, "Unknown"));
166
167   if (m2pa_tree) {
168     proto_tree_add_item(m2pa_tree, hf_version, header_tvb, VERSION_OFFSET,       VERSION_LENGTH, NETWORK_BYTE_ORDER);
169     proto_tree_add_item(m2pa_tree, hf_spare,   header_tvb, SPARE_OFFSET,         SPARE_LENGTH,   NETWORK_BYTE_ORDER);
170     proto_tree_add_item(m2pa_tree, hf_class,   header_tvb, CLASS_OFFSET,         CLASS_LENGTH,   NETWORK_BYTE_ORDER);
171     proto_tree_add_item(m2pa_tree, hf_v8_type, header_tvb, V8_TYPE_OFFSET,       V8_TYPE_LENGTH, NETWORK_BYTE_ORDER);
172     proto_tree_add_item(m2pa_tree, hf_length,  header_tvb, V8_LENGTH_OFFSET,     LENGTH_LENGTH,  NETWORK_BYTE_ORDER);
173     proto_tree_add_item(m2pa_tree, hf_unused,  header_tvb, FIRST_UNUSED_OFFSET,  UNUSED_LENGTH,  NETWORK_BYTE_ORDER);
174     proto_tree_add_item(m2pa_tree, hf_bsn,     header_tvb, BSN_OFFSET,           BSN_LENGTH,     NETWORK_BYTE_ORDER);
175     proto_tree_add_item(m2pa_tree, hf_unused,  header_tvb, SECOND_UNUSED_OFFSET, UNUSED_LENGTH,  NETWORK_BYTE_ORDER);
176     proto_tree_add_item(m2pa_tree, hf_fsn,     header_tvb, FSN_OFFSET,           FSN_LENGTH,     NETWORK_BYTE_ORDER);
177   }
178 }
179
180 #define LI_OFFSET           0
181 #define LI_LENGTH           1
182 #define MTP3_OFFSET         (LI_OFFSET + LI_LENGTH)
183
184 #define V2_LI_SPARE_MASK    0xfc
185 #define V2_LI_PRIORITY_MASK 0x3
186
187 static void
188 dissect_v2_user_data_message(tvbuff_t *message_data_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
189 {
190   proto_item *m2pa_li_item;
191   proto_tree *m2pa_li_tree;
192   tvbuff_t *payload_tvb;
193
194   if (tvb_length(message_data_tvb) > 0) {
195     if (m2pa_tree) {
196       m2pa_li_item = proto_tree_add_text(m2pa_tree, message_data_tvb, LI_OFFSET, LI_LENGTH, "Length Indicator");
197       m2pa_li_tree = proto_item_add_subtree(m2pa_li_item, ett_m2pa_li);
198
199       proto_tree_add_item(m2pa_li_tree, hf_v2_li_spare, message_data_tvb, LI_OFFSET, LI_LENGTH, NETWORK_BYTE_ORDER);
200       proto_tree_add_item(m2pa_li_tree, hf_v2_li_prio,  message_data_tvb, LI_OFFSET, LI_LENGTH, NETWORK_BYTE_ORDER);
201
202       /* Re-adjust length of M2PA item since it will be dissected as MTP3 */
203       proto_item_set_len(m2pa_item, V2_HEADER_LENGTH + LI_LENGTH);
204      }
205   }
206
207   payload_tvb = tvb_new_subset(message_data_tvb, MTP3_OFFSET, -1, -1);
208   call_dissector(mtp3_handle, payload_tvb, pinfo, tree);
209 }
210
211 #define V8_LI_SPARE_MASK         0x3f
212 #define V8_LI_PRIORITY_MASK      0xc0
213
214 static void
215 dissect_v8_user_data_message(tvbuff_t *message_data_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
216 {
217   proto_item *m2pa_li_item;
218   proto_tree *m2pa_li_tree;
219   tvbuff_t *payload_tvb;
220
221   if (tvb_length(message_data_tvb) > 0) {
222     if (m2pa_tree) {
223       m2pa_li_item = proto_tree_add_text(m2pa_tree, message_data_tvb, LI_OFFSET, LI_LENGTH, "Length Indicator");
224       m2pa_li_tree = proto_item_add_subtree(m2pa_li_item, ett_m2pa_li);
225       proto_tree_add_item(m2pa_li_tree, hf_v8_li_prio,  message_data_tvb, LI_OFFSET, LI_LENGTH, NETWORK_BYTE_ORDER);
226       proto_tree_add_item(m2pa_li_tree, hf_v8_li_spare, message_data_tvb, LI_OFFSET, LI_LENGTH, NETWORK_BYTE_ORDER);
227
228         /* Re-adjust length of M2PA item since it will be dissected as MTP3 */
229       proto_item_set_len(m2pa_item, V8_HEADER_LENGTH + LI_LENGTH);
230     }
231
232     payload_tvb = tvb_new_subset(message_data_tvb, MTP3_OFFSET, -1, -1);
233     call_dissector(mtp3_handle, payload_tvb, pinfo, tree);
234   }
235 }
236
237 static const value_string v2_link_status_values[] = {
238   { 1, "In Service" },
239   { 2, "Processor Outage" },
240   { 3, "Processor Outage Ended" },
241   { 4, "Busy" },
242   { 5, "Busy Ended" },
243   { 0, NULL } };
244
245 #define STATUS_LENGTH    4
246 #define STATUS_OFFSET    0
247 #define FILLER_OFFSET    (STATUS_OFFSET + STATUS_LENGTH)
248
249 static void
250 dissect_v2_link_status_message(tvbuff_t *message_data_tvb, packet_info *pinfo, proto_tree *m2pa_tree)
251 {
252   if (check_col(pinfo->cinfo, COL_INFO))
253     col_append_fstr(pinfo->cinfo, COL_INFO, "(%s) ", val_to_str(tvb_get_ntohl(message_data_tvb, STATUS_OFFSET), v2_link_status_values, "Unknown"));
254   if (m2pa_tree)
255     proto_tree_add_item(m2pa_tree, hf_v2_status, message_data_tvb, STATUS_OFFSET, STATUS_LENGTH, NETWORK_BYTE_ORDER);
256 }
257
258 static const value_string v8_link_status_values[] = {
259   { 1, "Alignment" },
260   { 2, "Proving Normal" },
261   { 3, "Proving Emergency" },
262   { 4, "Ready" },
263   { 5, "Processor Outage" },
264   { 6, "Processor Outage Ended" },
265   { 7, "Busy" },
266   { 8, "Busy Ended" },
267   { 9, "Out of Service" },
268   { 0, NULL } };
269
270 static void
271 dissect_v8_link_status_message(tvbuff_t *message_data_tvb, packet_info *pinfo, proto_tree *m2pa_tree)
272 {
273   guint16 filler_length;
274   
275   if (check_col(pinfo->cinfo, COL_INFO))
276     col_append_fstr(pinfo->cinfo, COL_INFO, "(%s) ", val_to_str(tvb_get_ntohl(message_data_tvb, STATUS_OFFSET), v8_link_status_values, "Unknown"));
277
278   filler_length = tvb_length(message_data_tvb) - STATUS_LENGTH;
279   
280   proto_tree_add_item(m2pa_tree, hf_v8_status, message_data_tvb, STATUS_OFFSET, STATUS_LENGTH, NETWORK_BYTE_ORDER);
281   if (filler_length > 0)
282       proto_tree_add_item(m2pa_tree, hf_filler, message_data_tvb, FILLER_OFFSET, filler_length, NETWORK_BYTE_ORDER);
283 }
284
285 static void
286 dissect_unknown_message(tvbuff_t *message_data_tvb, proto_tree *m2pa_tree)
287 {
288   guint length;
289    
290   length = tvb_length(message_data_tvb);
291   if ((m2pa_tree) && (length > 0))
292     proto_tree_add_item(m2pa_tree, hf_unknown_data, message_data_tvb, 0, length, NETWORK_BYTE_ORDER);
293 }
294
295 #define V2_MESSAGE_DATA_OFFSET (HEADER_OFFSET + V2_HEADER_LENGTH)
296
297 static void
298 dissect_v2_message_data(tvbuff_t *message_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
299 {
300   guint32 message_data_length;
301   guint16 type;
302   tvbuff_t *message_data_tvb;
303
304   message_data_length = tvb_get_ntohl(message_tvb, V2_LENGTH_OFFSET);
305   message_data_tvb    = tvb_new_subset(message_tvb, V2_MESSAGE_DATA_OFFSET, message_data_length, message_data_length);
306   type                = tvb_get_ntohs(message_tvb, V2_TYPE_OFFSET);
307
308   switch(type) {
309   case V2_USER_DATA_TYPE:
310     dissect_v2_user_data_message(message_data_tvb, pinfo, m2pa_item, m2pa_tree, tree);
311     break;
312   case V2_LINK_STATUS_TYPE:
313     dissect_v2_link_status_message(message_data_tvb, pinfo, m2pa_tree);
314     break;
315   default:
316     dissect_unknown_message(message_data_tvb, m2pa_tree);
317   }
318 }
319
320 #define V8_MESSAGE_DATA_OFFSET (HEADER_OFFSET + V8_HEADER_LENGTH)
321
322 static void
323 dissect_v8_message_data(tvbuff_t *message_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
324 {
325   guint32 message_data_length;
326   guint8 type;
327   tvbuff_t *message_data_tvb;
328
329   message_data_length = tvb_get_ntohl(message_tvb, V8_LENGTH_OFFSET) - V8_HEADER_LENGTH;
330   message_data_tvb    = tvb_new_subset(message_tvb, V8_MESSAGE_DATA_OFFSET, message_data_length, message_data_length);
331   type                = tvb_get_guint8(message_tvb, V8_TYPE_OFFSET);
332
333
334   switch(type) {
335   case V8_USER_DATA_TYPE:
336     dissect_v8_user_data_message(message_data_tvb, pinfo, m2pa_item, m2pa_tree, tree);
337     break;
338   case V8_LINK_STATUS_TYPE:
339     dissect_v8_link_status_message(message_data_tvb, pinfo, m2pa_tree);
340     break;
341   default:
342     dissect_unknown_message(message_data_tvb, m2pa_tree);
343   }
344 }
345
346 static void
347 dissect_v2_message(tvbuff_t *message_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
348 {
349   dissect_v2_header(message_tvb, pinfo, m2pa_tree);
350   dissect_v2_message_data(message_tvb, pinfo, m2pa_item, m2pa_tree, tree);
351 }
352
353 static void
354 dissect_v8_message(tvbuff_t *message_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
355 {
356   dissect_v8_header(message_tvb, pinfo, m2pa_tree);
357   dissect_v8_message_data(message_tvb, pinfo, m2pa_item, m2pa_tree, tree);
358 }
359
360 static void
361 dissect_m2pa(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
362 {
363   proto_item *m2pa_item;
364   proto_tree *m2pa_tree;
365
366   if (check_col(pinfo->cinfo, COL_PROTOCOL))
367     switch(m2pa_version) {
368     case M2PA_V2:
369       col_set_str(pinfo->cinfo, COL_PROTOCOL, "M2PA (ID 02)");
370       break;
371     case M2PA_V11:
372       col_set_str(pinfo->cinfo, COL_PROTOCOL, "M2PA (ID 11)");
373       break;
374     };      
375
376   if (tree) {
377     m2pa_item = proto_tree_add_item(tree, proto_m2pa, tvb, 0, -1, FALSE);
378     m2pa_tree = proto_item_add_subtree(m2pa_item, ett_m2pa);
379   } else {
380     m2pa_item = NULL;
381     m2pa_tree = NULL;
382   }
383
384   switch(m2pa_version) {
385     case M2PA_V2:
386       dissect_v2_message(tvb, pinfo, m2pa_item, m2pa_tree, tree);
387       break;
388     case M2PA_V11:
389       dissect_v8_message(tvb, pinfo, m2pa_item, m2pa_tree, tree);
390       break;
391   };      
392 }
393
394 void
395 proto_register_m2pa(void)
396 {
397   static hf_register_info hf[] = 
398   { { &hf_version,      { "Version",        "m2pa.version",      FT_UINT8,  BASE_DEC,  VALS(protocol_version_values), 0x0,                 "", HFILL} },
399     { &hf_spare,        { "Spare",          "m2pa.spare",        FT_UINT8,  BASE_HEX,  NULL,                          0x0,                 "", HFILL} },
400     { &hf_v2_type,      { "Message Type",   "m2pa.type",         FT_UINT16, BASE_HEX,  VALS(v2_message_type_values),  0x0,                 "", HFILL} },
401     { &hf_v8_type,      { "Message Type",   "m2pa.type",         FT_UINT8,  BASE_DEC,  VALS(v8_message_type_values),  0x0,                 "", HFILL} },
402     { &hf_class,        { "Message Class",  "m2pa.class",        FT_UINT8,  BASE_DEC,  VALS(message_class_values),    0x0,                 "", HFILL} },
403     { &hf_length,       { "Message length", "m2pa.length",       FT_UINT32, BASE_DEC,  NULL,                          0x0,                 "", HFILL} },
404     { &hf_unused,       { "Unused",         "m2pa.unused",       FT_UINT8,  BASE_DEC,  NULL,                          0x0,                 "", HFILL} },
405     { &hf_bsn,          { "BSN",            "m2pa.bsn",          FT_UINT24, BASE_DEC,  NULL,                          0x0,                 "", HFILL} },
406     { &hf_fsn,          { "FSN",            "m2pa.fsn",          FT_UINT24, BASE_DEC,  NULL,                          0x0,                 "", HFILL} },
407     { &hf_v2_li_spare,  { "Spare",          "m2pa.li_spare",     FT_UINT8,  BASE_DEC,  NULL,                          V2_LI_SPARE_MASK,    "", HFILL} },
408     { &hf_v8_li_spare,  { "Spare",          "m2pa.li_spare",     FT_UINT8,  BASE_HEX,  NULL,                          V8_LI_SPARE_MASK,    "", HFILL} },
409     { &hf_v2_li_prio,   { "Priority",       "m2pa.li_priority",  FT_UINT8,  BASE_DEC,  NULL,                          V2_LI_PRIORITY_MASK, "", HFILL} },
410     { &hf_v8_li_prio,   { "Priority",       "m2pa.li_priority",  FT_UINT8,  BASE_HEX,  NULL,                          V8_LI_PRIORITY_MASK, "", HFILL} },
411     { &hf_v2_status,    { "Link Status",    "m2pa.status",       FT_UINT32, BASE_DEC,  VALS(v2_link_status_values),   0x0,                 "", HFILL} },
412     { &hf_v8_status,    { "Link Status",    "m2pa.status",       FT_UINT32, BASE_DEC,  VALS(v8_link_status_values),   0x0,                 "", HFILL} },
413     { &hf_filler,       { "Filler",         "m2pa.filler",       FT_BYTES,  BASE_NONE, NULL,                          0x0,                 "", HFILL} },
414     { &hf_unknown_data, { "Unknown Data",   "m2pa.unknown_data", FT_BYTES,  BASE_NONE, NULL,                          0x0,                 "", HFILL} }
415   };
416
417   static gint *ett[] = {
418     &ett_m2pa,
419     &ett_m2pa_li
420   };
421
422   static enum_val_t m2pa_version_options[] = {
423     { "draft-2", "Internet Draft version 2", M2PA_V2 },
424     { "draft-11", "Internet Draft version 11", M2PA_V11 },
425     { NULL, NULL, 0 }
426   };
427
428   proto_m2pa = proto_register_protocol("MTP2 Peer Adaptation Layer", "M2PA", "m2pa");
429
430   proto_register_field_array(proto_m2pa, hf, array_length(hf));
431   proto_register_subtree_array(ett, array_length(ett));
432
433   m2pa_module = prefs_register_protocol(proto_m2pa, proto_reg_handoff_m2pa);
434
435   prefs_register_enum_preference(m2pa_module, "version", "M2PA version", "Version used by Ethereal", (gint *)&m2pa_version, m2pa_version_options, FALSE);
436   prefs_register_uint_preference(m2pa_module, "port", "M2PA SCTP Port", "Set the port for M2PA messages (Default of 3565)", 10, &global_sctp_port);
437 }
438
439 void
440 proto_reg_handoff_m2pa(void)
441 {
442   static int prefs_initialized = FALSE;
443   static dissector_handle_t m2pa_handle;
444
445   /* Port preferences code shamelessly copied from packet-beep.c */
446   if (!prefs_initialized) {
447     mtp3_handle   = find_dissector("mtp3");
448     mtp3_proto_id = proto_get_id_by_filter_name("mtp3");
449     m2pa_handle   = create_dissector_handle(dissect_m2pa, proto_m2pa);
450
451     dissector_add("sctp.ppi", M2PA_PAYLOAD_PROTOCOL_ID, m2pa_handle);
452
453     prefs_initialized = TRUE;
454
455   } else {
456
457     dissector_delete("sctp.port", sctp_port, m2pa_handle);
458
459   }
460
461   /* Set our port number for future use */
462   sctp_port = global_sctp_port;
463
464   dissector_add("sctp.port", sctp_port, m2pa_handle);
465 }