1 /* packet-m2pa.c
2  * Routines for MTP2 Peer Adaptation Layer dissection
3  * It is hopefully (needs testing) compliant to
4  * https://tools.ietf.org/html/draft-ietf-sigtran-m2pa-02
5  * https://tools.ietf.org/html/draft-ietf-sigtran-m2pa-08
6  * https://tools.ietf.org/html/rfc4165
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  * SPDX-License-Identifier: GPL-2.0-or-later
18  */
19 
20 
21 #include "config.h"
22 
23 #include <epan/packet.h>
24 #include <epan/exceptions.h>
25 #include <epan/prefs.h>
26 #include <epan/sctpppids.h>
27 #include <epan/expert.h>
28 
29 void proto_register_m2pa(void);
30 void proto_reg_handoff_m2pa(void);
31 
32 #define SCTP_PORT_M2PA              3565
33 
34 static guint global_sctp_port       = SCTP_PORT_M2PA;
35 
36 static int proto_m2pa      = -1;
37 static module_t *m2pa_module;
38 
39 static int hf_version      = -1;
40 static int hf_spare        = -1;
41 static int hf_v2_type      = -1;
42 static int hf_v8_type      = -1;
43 static int hf_type         = -1;
44 static int hf_class        = -1;
45 static int hf_length       = -1;
46 static int hf_unused       = -1;
47 static int hf_bsn          = -1;
48 static int hf_fsn          = -1;
49 static int hf_v2_status    = -1;
50 static int hf_v8_status    = -1;
51 static int hf_status       = -1;
52 static int hf_v2_li_spare  = -1;
53 static int hf_v8_li_spare  = -1;
54 static int hf_v2_li_prio   = -1;
55 static int hf_v8_li_prio   = -1;
56 static int hf_filler       = -1;
57 static int hf_unknown_data = -1;
58 static int hf_pri_prio     = -1;
59 static int hf_pri_spare    = -1;
60 static int hf_undecode_data   = -1;
61 
62 static gint ett_m2pa       = -1;
63 static gint ett_m2pa_li    = -1;
64 
65 static expert_field ei_undecode_data = EI_INIT;
66 static expert_field ei_length = EI_INIT;
67 
68 static dissector_handle_t m2pa_handle;
69 static dissector_handle_t mtp3_handle;
70 
71 typedef enum {
72   M2PA_V02 = 1,
73   M2PA_V08 = 2,
74   M2PA_RFC4165 = 3
75 } Version_Type;
76 
77 static gint m2pa_version = M2PA_RFC4165;
78 
79 #define VERSION_LENGTH         1
80 #define SPARE_LENGTH           1
81 #define CLASS_LENGTH           1
82 #define V2_TYPE_LENGTH         2
83 #define V8_TYPE_LENGTH         1
84 #define TYPE_LENGTH            V8_TYPE_LENGTH
85 #define LENGTH_LENGTH          4
86 #define UNUSED_LENGTH          1
87 #define BSN_LENGTH             3
88 #define FSN_LENGTH             3
89 
90 #define V2_HEADER_LENGTH        (VERSION_LENGTH + SPARE_LENGTH + \
91                                 V2_TYPE_LENGTH + LENGTH_LENGTH)
92 
93 #define V8_HEADER_LENGTH        (VERSION_LENGTH + SPARE_LENGTH + \
94                                 CLASS_LENGTH + V8_TYPE_LENGTH + LENGTH_LENGTH + \
95                                 UNUSED_LENGTH + BSN_LENGTH + UNUSED_LENGTH + \
96                                 FSN_LENGTH)
97 #define HEADER_LENGTH           V8_HEADER_LENGTH
98 
99 #define HEADER_OFFSET          0
100 #define VERSION_OFFSET         HEADER_OFFSET
101 #define SPARE_OFFSET           (VERSION_OFFSET + VERSION_LENGTH)
102 #define CLASS_OFFSET           (SPARE_OFFSET + SPARE_LENGTH)
103 #define V2_TYPE_OFFSET         (SPARE_OFFSET + SPARE_LENGTH)
104 #define V8_TYPE_OFFSET         (CLASS_OFFSET + CLASS_LENGTH)
105 #define TYPE_OFFSET            V8_TYPE_OFFSET
106 #define V8_LENGTH_OFFSET       (V8_TYPE_OFFSET + V8_TYPE_LENGTH)
107 #define LENGTH_OFFSET          V8_LENGTH_OFFSET
108 #define V2_LENGTH_OFFSET       (V2_TYPE_OFFSET + V2_TYPE_LENGTH)
109 #define FIRST_UNUSED_OFFSET    (V8_LENGTH_OFFSET + LENGTH_LENGTH)
110 #define BSN_OFFSET             (FIRST_UNUSED_OFFSET + UNUSED_LENGTH)
111 #define SECOND_UNUSED_OFFSET   (BSN_OFFSET + BSN_LENGTH)
112 #define FSN_OFFSET             (SECOND_UNUSED_OFFSET + UNUSED_LENGTH)
113 
114 static const value_string protocol_version_values[] = {
115   { 1,      "Release 1" },
116   { 0,      NULL } };
117 
118 static const value_string message_class_values[] = {
119   { 0xb,    "M2PA" },
120   { 0,      NULL } };
121 
122 #define V2_USER_DATA_TYPE   0x0601
123 #define V2_LINK_STATUS_TYPE 0x0602
124 
125 static const value_string v2_message_type_values[] = {
126   { V2_USER_DATA_TYPE,   "User Data" },
127   { V2_LINK_STATUS_TYPE, "Link Status" },
128   { 0,                   NULL } };
129 
130 #define V8_USER_DATA_TYPE   0x0001
131 #define V8_LINK_STATUS_TYPE 0x0002
132 
133 static const value_string v8_message_type_values[] = {
134   { V8_USER_DATA_TYPE,   "User Data" },
135   { V8_LINK_STATUS_TYPE, "Link Status" },
136   { 0,                   NULL } };
137 
138 #define USER_DATA_TYPE   V8_USER_DATA_TYPE
139 #define LINK_STATUS_TYPE V8_LINK_STATUS_TYPE
140 
141 static const value_string message_type_values[] = {
142   { USER_DATA_TYPE,   "User Data" },
143   { LINK_STATUS_TYPE, "Link Status" },
144   { 0,                NULL } };
145 
146 static void
dissect_v2_header(tvbuff_t * header_tvb,packet_info * pinfo,proto_tree * m2pa_tree)147 dissect_v2_header(tvbuff_t *header_tvb, packet_info *pinfo, proto_tree *m2pa_tree)
148 {
149   guint16 message_type;
150 
151   message_type  = tvb_get_ntohs(header_tvb, V2_TYPE_OFFSET);
152 
153   col_add_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str_const(message_type, v2_message_type_values, "reserved"));
154 
155   proto_tree_add_item(m2pa_tree, hf_version, header_tvb, VERSION_OFFSET,       VERSION_LENGTH, ENC_BIG_ENDIAN);
156   proto_tree_add_item(m2pa_tree, hf_spare,   header_tvb, SPARE_OFFSET,         SPARE_LENGTH,   ENC_BIG_ENDIAN);
157   proto_tree_add_item(m2pa_tree, hf_v2_type, header_tvb, V2_TYPE_OFFSET,       V2_TYPE_LENGTH, ENC_BIG_ENDIAN);
158   proto_tree_add_item(m2pa_tree, hf_length,  header_tvb, V2_LENGTH_OFFSET,     LENGTH_LENGTH,  ENC_BIG_ENDIAN);
159 }
160 
161 static void
dissect_v8_header(tvbuff_t * header_tvb,packet_info * pinfo,proto_tree * m2pa_tree)162 dissect_v8_header(tvbuff_t *header_tvb, packet_info *pinfo, proto_tree *m2pa_tree)
163 {
164   guint8 message_type;
165 
166   message_type  = tvb_get_guint8(header_tvb, V8_TYPE_OFFSET);
167 
168   col_add_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str_const(message_type, v8_message_type_values, "Unknown"));
169 
170   proto_tree_add_item(m2pa_tree, hf_version, header_tvb, VERSION_OFFSET,       VERSION_LENGTH, ENC_BIG_ENDIAN);
171   proto_tree_add_item(m2pa_tree, hf_spare,   header_tvb, SPARE_OFFSET,         SPARE_LENGTH,   ENC_BIG_ENDIAN);
172   proto_tree_add_item(m2pa_tree, hf_class,   header_tvb, CLASS_OFFSET,         CLASS_LENGTH,   ENC_BIG_ENDIAN);
173   proto_tree_add_item(m2pa_tree, hf_v8_type, header_tvb, V8_TYPE_OFFSET,       V8_TYPE_LENGTH, ENC_BIG_ENDIAN);
174   proto_tree_add_item(m2pa_tree, hf_length,  header_tvb, V8_LENGTH_OFFSET,     LENGTH_LENGTH,  ENC_BIG_ENDIAN);
175   proto_tree_add_item(m2pa_tree, hf_unused,  header_tvb, FIRST_UNUSED_OFFSET,  UNUSED_LENGTH,  ENC_BIG_ENDIAN);
176   proto_tree_add_item(m2pa_tree, hf_bsn,     header_tvb, BSN_OFFSET,           BSN_LENGTH,     ENC_BIG_ENDIAN);
177   proto_tree_add_item(m2pa_tree, hf_unused,  header_tvb, SECOND_UNUSED_OFFSET, UNUSED_LENGTH,  ENC_BIG_ENDIAN);
178   proto_tree_add_item(m2pa_tree, hf_fsn,     header_tvb, FSN_OFFSET,           FSN_LENGTH,     ENC_BIG_ENDIAN);
179 }
180 
181 static void
dissect_header(tvbuff_t * header_tvb,packet_info * pinfo,proto_tree * m2pa_tree)182 dissect_header(tvbuff_t *header_tvb, packet_info *pinfo, proto_tree *m2pa_tree)
183 {
184   guint8 message_type;
185 
186   message_type  = tvb_get_guint8(header_tvb, V8_TYPE_OFFSET);
187 
188   col_add_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str_const(message_type, v8_message_type_values, "Unknown"));
189 
190   proto_tree_add_item(m2pa_tree, hf_version,  header_tvb, VERSION_OFFSET,       VERSION_LENGTH,  ENC_BIG_ENDIAN);
191   proto_tree_add_item(m2pa_tree, hf_spare,    header_tvb, SPARE_OFFSET,         SPARE_LENGTH,    ENC_BIG_ENDIAN);
192   proto_tree_add_item(m2pa_tree, hf_class,    header_tvb, CLASS_OFFSET,         CLASS_LENGTH,    ENC_BIG_ENDIAN);
193   proto_tree_add_item(m2pa_tree, hf_type,     header_tvb, TYPE_OFFSET,          TYPE_LENGTH,     ENC_BIG_ENDIAN);
194   proto_tree_add_item(m2pa_tree, hf_length,   header_tvb, LENGTH_OFFSET,        LENGTH_LENGTH,   ENC_BIG_ENDIAN);
195   proto_tree_add_item(m2pa_tree, hf_unused,   header_tvb, FIRST_UNUSED_OFFSET,  UNUSED_LENGTH,   ENC_BIG_ENDIAN);
196   proto_tree_add_item(m2pa_tree, hf_bsn,      header_tvb, BSN_OFFSET,           BSN_LENGTH,      ENC_BIG_ENDIAN);
197   proto_tree_add_item(m2pa_tree, hf_unused,   header_tvb, SECOND_UNUSED_OFFSET, UNUSED_LENGTH,   ENC_BIG_ENDIAN);
198   proto_tree_add_item(m2pa_tree, hf_fsn,      header_tvb, FSN_OFFSET,           FSN_LENGTH,      ENC_BIG_ENDIAN);
199 }
200 
201 #define LI_OFFSET           0
202 #define LI_LENGTH           1
203 #define MTP3_OFFSET         (LI_OFFSET + LI_LENGTH)
204 
205 #define V2_LI_SPARE_MASK    0xfc
206 #define V2_LI_PRIORITY_MASK 0x3
207 
208 static void
dissect_v2_user_data_message(tvbuff_t * message_data_tvb,packet_info * pinfo,proto_item * m2pa_item,proto_tree * m2pa_tree,proto_tree * tree)209 dissect_v2_user_data_message(tvbuff_t *message_data_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
210 {
211   proto_tree *m2pa_li_tree;
212   tvbuff_t *payload_tvb;
213 
214   if (tvb_reported_length(message_data_tvb) > 0) {
215     m2pa_li_tree = proto_tree_add_subtree(m2pa_tree, message_data_tvb, LI_OFFSET, LI_LENGTH, ett_m2pa_li, NULL, "Length Indicator");
216 
217     proto_tree_add_item(m2pa_li_tree, hf_v2_li_spare, message_data_tvb, LI_OFFSET, LI_LENGTH, ENC_BIG_ENDIAN);
218     proto_tree_add_item(m2pa_li_tree, hf_v2_li_prio,  message_data_tvb, LI_OFFSET, LI_LENGTH, ENC_BIG_ENDIAN);
219 
220     /* Re-adjust length of M2PA item since it will be dissected as MTP3 */
221     proto_item_set_len(m2pa_item, V2_HEADER_LENGTH + LI_LENGTH);
222   }
223 
224   payload_tvb = tvb_new_subset_remaining(message_data_tvb, MTP3_OFFSET);
225   call_dissector(mtp3_handle, payload_tvb, pinfo, tree);
226 }
227 
228 #define V8_LI_SPARE_MASK        0x3f
229 #define V8_LI_PRIORITY_MASK     0xc0
230 
231 static void
dissect_v8_user_data_message(tvbuff_t * message_data_tvb,packet_info * pinfo,proto_item * m2pa_item,proto_tree * m2pa_tree,proto_tree * tree)232 dissect_v8_user_data_message(tvbuff_t *message_data_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
233 {
234   proto_tree *m2pa_li_tree;
235   tvbuff_t *payload_tvb;
236 
237   if (tvb_reported_length(message_data_tvb) > 0) {
238     m2pa_li_tree = proto_tree_add_subtree(m2pa_tree, message_data_tvb, LI_OFFSET, LI_LENGTH, ett_m2pa_li, NULL, "Length Indicator");
239     proto_tree_add_item(m2pa_li_tree, hf_v8_li_prio,  message_data_tvb, LI_OFFSET, LI_LENGTH, ENC_BIG_ENDIAN);
240     proto_tree_add_item(m2pa_li_tree, hf_v8_li_spare, message_data_tvb, LI_OFFSET, LI_LENGTH, ENC_BIG_ENDIAN);
241 
242     /* Re-adjust length of M2PA item since it will be dissected as MTP3 */
243     proto_item_set_len(m2pa_item, V8_HEADER_LENGTH + LI_LENGTH);
244 
245     payload_tvb = tvb_new_subset_remaining(message_data_tvb, MTP3_OFFSET);
246     call_dissector(mtp3_handle, payload_tvb, pinfo, tree);
247   }
248 }
249 
250 #define PRIORITY_MASK     0xc0
251 #define SPARE_MASK        0x3f
252 
253 #define PRI_OFFSET           0
254 #define PRI_LENGTH           1
255 
256 static void
dissect_user_data_message(tvbuff_t * message_data_tvb,packet_info * pinfo,proto_item * m2pa_item,proto_tree * m2pa_tree,proto_tree * tree)257 dissect_user_data_message(tvbuff_t *message_data_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
258 {
259   proto_tree *m2pa_li_tree;
260   tvbuff_t *payload_tvb;
261 
262   if (tvb_reported_length(message_data_tvb) > 0) {
263     m2pa_li_tree = proto_tree_add_subtree(m2pa_tree, message_data_tvb, PRI_OFFSET, PRI_LENGTH, ett_m2pa_li, NULL, "Priority");
264     proto_tree_add_item(m2pa_li_tree, hf_pri_prio,  message_data_tvb, PRI_OFFSET, PRI_LENGTH, ENC_BIG_ENDIAN);
265     proto_tree_add_item(m2pa_li_tree, hf_pri_spare, message_data_tvb, PRI_OFFSET, PRI_LENGTH, ENC_BIG_ENDIAN);
266 
267     /* Re-adjust length of M2PA item since it will be dissected as MTP3 */
268     proto_item_set_len(m2pa_item, HEADER_LENGTH + PRI_LENGTH);
269 
270     payload_tvb = tvb_new_subset_remaining(message_data_tvb, MTP3_OFFSET);
271     call_dissector(mtp3_handle, payload_tvb, pinfo, tree);
272   }
273 }
274 
275 static const value_string v2_link_status_values[] = {
276   { 1, "In Service" },
277   { 2, "Processor Outage" },
278   { 3, "Processor Outage Ended" },
279   { 4, "Busy" },
280   { 5, "Busy Ended" },
281   { 0, NULL } };
282 
283 #define STATUS_LENGTH    4
284 #define STATUS_OFFSET    0
285 #define FILLER_OFFSET    (STATUS_OFFSET + STATUS_LENGTH)
286 
287 static void
dissect_v2_link_status_message(tvbuff_t * message_data_tvb,packet_info * pinfo,proto_tree * m2pa_tree)288 dissect_v2_link_status_message(tvbuff_t *message_data_tvb, packet_info *pinfo, proto_tree *m2pa_tree)
289 {
290   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"));
291   proto_tree_add_item(m2pa_tree, hf_v2_status, message_data_tvb, STATUS_OFFSET, STATUS_LENGTH, ENC_BIG_ENDIAN);
292 }
293 
294 static const value_string v8_link_status_values[] = {
295   { 1, "Alignment" },
296   { 2, "Proving Normal" },
297   { 3, "Proving Emergency" },
298   { 4, "Ready" },
299   { 5, "Processor Outage" },
300   { 6, "Processor Outage Ended" },
301   { 7, "Busy" },
302   { 8, "Busy Ended" },
303   { 9, "Out of Service" },
304   { 0, NULL } };
305 
306 static void
dissect_v8_link_status_message(tvbuff_t * message_data_tvb,packet_info * pinfo,proto_tree * m2pa_tree)307 dissect_v8_link_status_message(tvbuff_t *message_data_tvb, packet_info *pinfo, proto_tree *m2pa_tree)
308 {
309   guint16 filler_length;
310 
311   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"));
312 
313   filler_length = tvb_reported_length(message_data_tvb) - STATUS_LENGTH;
314 
315   proto_tree_add_item(m2pa_tree, hf_v8_status, message_data_tvb, STATUS_OFFSET, STATUS_LENGTH, ENC_BIG_ENDIAN);
316   if (filler_length > 0)
317       proto_tree_add_item(m2pa_tree, hf_filler, message_data_tvb, FILLER_OFFSET, filler_length, ENC_NA);
318 }
319 
320 static const value_string link_status_values[] = {
321   { 1, "Alignment" },
322   { 2, "Proving Normal" },
323   { 3, "Proving Emergency" },
324   { 4, "Ready" },
325   { 5, "Processor Outage" },
326   { 6, "Processor Outage Ended" },
327   { 7, "Busy" },
328   { 8, "Busy Ended" },
329   { 9, "Out of Service" },
330   { 0, NULL } };
331 
332 static void
dissect_link_status_message(tvbuff_t * message_data_tvb,packet_info * pinfo,proto_tree * m2pa_tree)333 dissect_link_status_message(tvbuff_t *message_data_tvb, packet_info *pinfo, proto_tree *m2pa_tree)
334 {
335   guint16 filler_length;
336 
337   col_append_fstr(pinfo->cinfo, COL_INFO, "(%s) ", val_to_str_const(tvb_get_ntohl(message_data_tvb, STATUS_OFFSET), link_status_values, "Unknown"));
338 
339   filler_length = tvb_reported_length(message_data_tvb) - STATUS_LENGTH;
340 
341   proto_tree_add_item(m2pa_tree, hf_status, message_data_tvb, STATUS_OFFSET, STATUS_LENGTH, ENC_BIG_ENDIAN);
342   if (filler_length > 0)
343       proto_tree_add_item(m2pa_tree, hf_filler, message_data_tvb, FILLER_OFFSET, filler_length, ENC_NA);
344 }
345 
346 static void
dissect_unknown_message(tvbuff_t * message_data_tvb,proto_tree * m2pa_tree)347 dissect_unknown_message(tvbuff_t *message_data_tvb, proto_tree *m2pa_tree)
348 {
349   guint length;
350 
351   length = tvb_reported_length(message_data_tvb);
352   if (length > 0)
353     proto_tree_add_item(m2pa_tree, hf_unknown_data, message_data_tvb, 0, length, ENC_NA);
354 }
355 
356 #define V2_MESSAGE_DATA_OFFSET (HEADER_OFFSET + V2_HEADER_LENGTH)
357 
358 static void
dissect_v2_message_data(tvbuff_t * message_tvb,packet_info * pinfo,proto_item * m2pa_item,proto_tree * m2pa_tree,proto_tree * tree)359 dissect_v2_message_data(tvbuff_t *message_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
360 {
361   guint32 message_data_length;
362   guint16 type;
363   tvbuff_t *message_data_tvb;
364 
365   message_data_length = tvb_get_ntohl(message_tvb, V2_LENGTH_OFFSET);
366   if (message_data_length < 1 || message_data_length > G_MAXINT) {
367     proto_tree_add_expert_format(m2pa_tree, pinfo, &ei_length, message_tvb, V2_LENGTH_OFFSET, 4,
368         "Invalid message data length: %u", message_data_length);
369     return;
370   }
371 
372   message_data_tvb    = tvb_new_subset_length(message_tvb, V2_MESSAGE_DATA_OFFSET, message_data_length);
373   type                = tvb_get_ntohs(message_tvb, V2_TYPE_OFFSET);
374 
375   switch(type) {
376   case V2_USER_DATA_TYPE:
377     dissect_v2_user_data_message(message_data_tvb, pinfo, m2pa_item, m2pa_tree, tree);
378     break;
379   case V2_LINK_STATUS_TYPE:
380     dissect_v2_link_status_message(message_data_tvb, pinfo, m2pa_tree);
381     break;
382   default:
383     dissect_unknown_message(message_data_tvb, m2pa_tree);
384   }
385 }
386 
387 #define V8_MESSAGE_DATA_OFFSET (HEADER_OFFSET + V8_HEADER_LENGTH)
388 
389 static void
dissect_v8_message_data(tvbuff_t * message_tvb,packet_info * pinfo,proto_item * m2pa_item,proto_tree * m2pa_tree,proto_tree * tree)390 dissect_v8_message_data(tvbuff_t *message_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
391 {
392   guint32 message_data_length;
393   guint8 type;
394   tvbuff_t *message_data_tvb;
395 
396   message_data_length = tvb_get_ntohl(message_tvb, V8_LENGTH_OFFSET) - V8_HEADER_LENGTH;
397   if (message_data_length < 1 || message_data_length > G_MAXINT) {
398     proto_tree_add_expert_format(m2pa_tree, pinfo, &ei_length, message_tvb, V8_LENGTH_OFFSET, 4,
399         "Invalid message data length: %u", message_data_length);
400     return;
401   }
402   message_data_tvb    = tvb_new_subset_length(message_tvb, V8_MESSAGE_DATA_OFFSET, message_data_length);
403   type                = tvb_get_guint8(message_tvb, V8_TYPE_OFFSET);
404 
405 
406   switch(type) {
407   case V8_USER_DATA_TYPE:
408     dissect_v8_user_data_message(message_data_tvb, pinfo, m2pa_item, m2pa_tree, tree);
409     break;
410   case V8_LINK_STATUS_TYPE:
411     dissect_v8_link_status_message(message_data_tvb, pinfo, m2pa_tree);
412     break;
413   default:
414     dissect_unknown_message(message_data_tvb, m2pa_tree);
415   }
416 }
417 
418 #define MESSAGE_DATA_OFFSET (HEADER_OFFSET + HEADER_LENGTH)
419 
420 static void
dissect_message_data(tvbuff_t * message_tvb,packet_info * pinfo,proto_item * m2pa_item,proto_tree * m2pa_tree,proto_tree * tree)421 dissect_message_data(tvbuff_t *message_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
422 {
423   guint32 length, message_data_length, actual_length;
424   guint8 type;
425   tvbuff_t *message_data_tvb;
426 
427   length              = tvb_get_ntohl(message_tvb, LENGTH_OFFSET);
428   message_data_length = length - HEADER_LENGTH;
429   message_data_tvb    = tvb_new_subset_length(message_tvb, MESSAGE_DATA_OFFSET, message_data_length);
430   type                = tvb_get_guint8(message_tvb, TYPE_OFFSET);
431 
432 
433   switch(type) {
434   case USER_DATA_TYPE:
435     dissect_user_data_message(message_data_tvb, pinfo, m2pa_item, m2pa_tree, tree);
436     break;
437   case LINK_STATUS_TYPE:
438     dissect_link_status_message(message_data_tvb, pinfo, m2pa_tree);
439     break;
440   default:
441     dissect_unknown_message(message_data_tvb, m2pa_tree);
442   }
443 
444   actual_length = tvb_reported_length(message_tvb);
445 
446   if (actual_length > length)
447   {
448     proto_item *pi;
449 
450     pi = proto_tree_add_item(m2pa_tree, hf_undecode_data, message_tvb, length, (actual_length - length), ENC_NA);
451     expert_add_info_format(pinfo, pi, &ei_undecode_data,
452                            "There are %d bytes of data which is greater than M2PA's length parameter (%d)",
453                            actual_length, length);
454   }
455 }
456 
457 static void
dissect_v2_message(tvbuff_t * message_tvb,packet_info * pinfo,proto_item * m2pa_item,proto_tree * m2pa_tree,proto_tree * tree)458 dissect_v2_message(tvbuff_t *message_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
459 {
460   dissect_v2_header(message_tvb, pinfo, m2pa_tree);
461   dissect_v2_message_data(message_tvb, pinfo, m2pa_item, m2pa_tree, tree);
462 }
463 
464 static void
dissect_v8_message(tvbuff_t * message_tvb,packet_info * pinfo,proto_item * m2pa_item,proto_tree * m2pa_tree,proto_tree * tree)465 dissect_v8_message(tvbuff_t *message_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
466 {
467   dissect_v8_header(message_tvb, pinfo, m2pa_tree);
468   dissect_v8_message_data(message_tvb, pinfo, m2pa_item, m2pa_tree, tree);
469 }
470 
471 static void
dissect_message(tvbuff_t * message_tvb,packet_info * pinfo,proto_item * m2pa_item,proto_tree * m2pa_tree,proto_tree * tree)472 dissect_message(tvbuff_t *message_tvb, packet_info *pinfo, proto_item *m2pa_item, proto_tree *m2pa_tree, proto_tree *tree)
473 {
474   dissect_header(message_tvb, pinfo, m2pa_tree);
475   dissect_message_data(message_tvb, pinfo, m2pa_item, m2pa_tree, tree);
476 }
477 
478 static int
dissect_m2pa(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)479 dissect_m2pa(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
480 {
481   proto_item *m2pa_item;
482   proto_tree *m2pa_tree;
483 
484   switch(m2pa_version) {
485   case M2PA_V02:
486     col_set_str(pinfo->cinfo, COL_PROTOCOL, "M2PA (ID 02)");
487     break;
488   case M2PA_V08:
489     col_set_str(pinfo->cinfo, COL_PROTOCOL, "M2PA (ID 08)");
490     break;
491   case M2PA_RFC4165:
492     col_set_str(pinfo->cinfo, COL_PROTOCOL, "M2PA");
493     break;
494   };
495 
496   m2pa_item = proto_tree_add_item(tree, proto_m2pa, tvb, 0, -1, ENC_NA);
497   m2pa_tree = proto_item_add_subtree(m2pa_item, ett_m2pa);
498 
499   switch(m2pa_version) {
500     case M2PA_V02:
501       dissect_v2_message(tvb, pinfo, m2pa_item, m2pa_tree, tree);
502       break;
503     case M2PA_V08:
504       dissect_v8_message(tvb, pinfo, m2pa_item, m2pa_tree, tree);
505       break;
506     case M2PA_RFC4165:
507       dissect_message(tvb, pinfo, m2pa_item, m2pa_tree, tree);
508       break;
509   };
510   return tvb_captured_length(tvb);
511 }
512 
513 void
proto_register_m2pa(void)514 proto_register_m2pa(void)
515 {
516   static hf_register_info hf[] =
517   { { &hf_version,      { "Version",        "m2pa.version",        FT_UINT8,  BASE_DEC,  VALS(protocol_version_values), 0x0,                 NULL, HFILL} },
518     { &hf_spare,        { "Spare",          "m2pa.spare",          FT_UINT8,  BASE_HEX,  NULL,                          0x0,                 NULL, HFILL} },
519     { &hf_v2_type,      { "Message Type",   "m2pa.type_v2",        FT_UINT16, BASE_HEX,  VALS(v2_message_type_values),  0x0,                 NULL, HFILL} },
520     { &hf_v8_type,      { "Message Type",   "m2pa.type_v8",        FT_UINT8,  BASE_DEC,  VALS(v8_message_type_values),  0x0,                 NULL, HFILL} },
521     { &hf_type,         { "Message Type",   "m2pa.type",           FT_UINT8,  BASE_DEC,  VALS(message_type_values),     0x0,                 NULL, HFILL} },
522     { &hf_class,        { "Message Class",  "m2pa.class",          FT_UINT8,  BASE_DEC,  VALS(message_class_values),    0x0,                 NULL, HFILL} },
523     { &hf_length,       { "Message length", "m2pa.length",         FT_UINT32, BASE_DEC,  NULL,                          0x0,                 NULL, HFILL} },
524     { &hf_unused,       { "Unused",         "m2pa.unused",         FT_UINT8,  BASE_DEC,  NULL,                          0x0,                 NULL, HFILL} },
525     { &hf_bsn,          { "BSN",            "m2pa.bsn",            FT_UINT24, BASE_DEC,  NULL,                          0x0,                 NULL, HFILL} },
526     { &hf_fsn,          { "FSN",            "m2pa.fsn",            FT_UINT24, BASE_DEC,  NULL,                          0x0,                 NULL, HFILL} },
527     { &hf_v2_li_spare,  { "Spare",          "m2pa.li_spare_v2",    FT_UINT8,  BASE_DEC,  NULL,                          V2_LI_SPARE_MASK,    NULL, HFILL} },
528     { &hf_v8_li_spare,  { "Spare",          "m2pa.li_spare_v8",    FT_UINT8,  BASE_HEX,  NULL,                          V8_LI_SPARE_MASK,    NULL, HFILL} },
529     { &hf_pri_spare,    { "Spare",          "m2pa.priority_spare", FT_UINT8,  BASE_HEX,  NULL,                          SPARE_MASK,          NULL, HFILL} },
530     { &hf_v2_li_prio,   { "Priority",       "m2pa.li_priority_v2", FT_UINT8,  BASE_DEC,  NULL,                          V2_LI_PRIORITY_MASK, NULL, HFILL} },
531     { &hf_v8_li_prio,   { "Priority",       "m2pa.li_priority_v8", FT_UINT8,  BASE_HEX,  NULL,                          V8_LI_PRIORITY_MASK, NULL, HFILL} },
532     { &hf_pri_prio,     { "Priority",       "m2pa.priority",       FT_UINT8,  BASE_HEX,  NULL,                          PRIORITY_MASK,       NULL, HFILL} },
533     { &hf_v2_status,    { "Link Status",    "m2pa.status_v2",      FT_UINT32, BASE_DEC,  VALS(v2_link_status_values),   0x0,                 NULL, HFILL} },
534     { &hf_v8_status,    { "Link Status",    "m2pa.status_v8",      FT_UINT32, BASE_DEC,  VALS(v8_link_status_values),   0x0,                 NULL, HFILL} },
535     { &hf_status,       { "Link Status",    "m2pa.status",         FT_UINT32, BASE_DEC,  VALS(link_status_values),      0x0,                 NULL, HFILL} },
536     { &hf_filler,       { "Filler",         "m2pa.filler",         FT_BYTES,  BASE_NONE, NULL,                          0x0,                 NULL, HFILL} },
537     { &hf_unknown_data, { "Unknown Data",   "m2pa.unknown_data",   FT_BYTES,  BASE_NONE, NULL,                          0x0,                 NULL, HFILL} },
538     { &hf_undecode_data,{ "Undecoded data", "m2pa.undecoded_data", FT_BYTES,  BASE_NONE, NULL,                          0x0,                 NULL, HFILL} }
539   };
540 
541   static gint *ett[] = {
542     &ett_m2pa,
543     &ett_m2pa_li
544   };
545 
546   static const enum_val_t m2pa_version_options[] = {
547     { "draft-2",  "Internet Draft version 2",  M2PA_V02     },
548     { "draft-8",  "Internet Draft version 8",  M2PA_V08     },
549     { "RFC4165",  "RFC 4165",                  M2PA_RFC4165 },
550     { NULL, NULL, 0 }
551   };
552 
553   static ei_register_info ei[] = {
554      { &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 }},
555      { &ei_length, { "m2pa.length.invalid", PI_MALFORMED, PI_ERROR, "Invalid message data length", EXPFILL }},
556   };
557 
558   expert_module_t* expert_m2pa;
559 
560   proto_m2pa = proto_register_protocol("MTP2 Peer Adaptation Layer", "M2PA", "m2pa");
561 
562   proto_register_field_array(proto_m2pa, hf, array_length(hf));
563   proto_register_subtree_array(ett, array_length(ett));
564   expert_m2pa = expert_register_protocol(proto_m2pa);
565   expert_register_field_array(expert_m2pa, ei, array_length(ei));
566 
567   /* Allow other dissectors to find this one by name. */
568   m2pa_handle = register_dissector("m2pa", dissect_m2pa, proto_m2pa);
569 
570   m2pa_module = prefs_register_protocol(proto_m2pa, proto_reg_handoff_m2pa);
571 
572   prefs_register_enum_preference(m2pa_module, "version", "M2PA version", "Version used by Wireshark", &m2pa_version, m2pa_version_options, FALSE);
573   prefs_register_uint_preference(m2pa_module, "port", "M2PA SCTP Port", "Set the port for M2PA messages (default: " G_STRINGIFY(SCTP_PORT_M2PA) ")", 10, &global_sctp_port);
574 }
575 
576 void
proto_reg_handoff_m2pa(void)577 proto_reg_handoff_m2pa(void)
578 {
579   static gboolean prefs_initialized = FALSE;
580   static guint sctp_port;
581 
582   /* Port preferences code shamelessly copied from packet-beep.c */
583   if (!prefs_initialized) {
584     mtp3_handle   = find_dissector_add_dependency("mtp3", proto_m2pa);
585 
586     dissector_add_uint("sctp.ppi", M2PA_PAYLOAD_PROTOCOL_ID, m2pa_handle);
587 
588     prefs_initialized = TRUE;
589 
590   } else {
591 
592     dissector_delete_uint("sctp.port", sctp_port, m2pa_handle);
593 
594   }
595 
596   /* Set our port number for future use */
597   sctp_port = global_sctp_port;
598 
599   dissector_add_uint("sctp.port", sctp_port, m2pa_handle);
600 }
601 
602 /*
603  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
604  *
605  * Local Variables:
606  * c-basic-offset: 2
607  * tab-width: 8
608  * indent-tabs-mode: nil
609  * End:
610  *
611  * ex: set shiftwidth=2 tabstop=8 expandtab:
612  * :indentSize=2:tabSize=8:noTabs=true:
613  */
614