/src/wireshark/epan/dissectors/packet-cesoeth.c
Line | Count | Source |
1 | | /* packet-cesoeth.c |
2 | | * Dissection of Circuit Emulation Service over Ethernet (MEF 8) |
3 | | * www.mplify.net |
4 | | * |
5 | | * Copyright 2018, AimValley B.V. |
6 | | * Jaap Keuter <jaap.keuter@aimvalley.com> |
7 | | * |
8 | | * Wireshark - Network traffic analyzer |
9 | | * By Gerald Combs <gerald@wireshark.org> |
10 | | * Copyright 1998 Gerald Combs |
11 | | * |
12 | | * SPDX-License-Identifier: GPL-2.0-or-later |
13 | | */ |
14 | | |
15 | | #include "config.h" |
16 | | |
17 | | #include <epan/packet.h> |
18 | | #include <epan/prefs.h> |
19 | | #include <epan/decode_as.h> |
20 | | #include <epan/proto_data.h> |
21 | | #include <epan/expert.h> |
22 | | #include <epan/etypes.h> |
23 | | #include "packet-rtp.h" |
24 | | |
25 | | void proto_register_cesoeth(void); |
26 | | void proto_reg_handoff_cesoeth(void); |
27 | | |
28 | | static dissector_handle_t cesoeth_handle; |
29 | | |
30 | | static dissector_table_t cesoeth_dissector_table; |
31 | | |
32 | | static int proto_cesoeth; |
33 | | static int hf_cesoeth_pw_ecid; |
34 | | static int hf_cesoeth_pw_res; |
35 | | static int hf_cesoeth_cw; |
36 | | static int hf_cesoeth_cw_reserved1; |
37 | | static int hf_cesoeth_cw_l; |
38 | | static int hf_cesoeth_cw_r; |
39 | | static int hf_cesoeth_cw_l0_m; |
40 | | static int hf_cesoeth_cw_l1_m; |
41 | | static int hf_cesoeth_cw_frg; |
42 | | static int hf_cesoeth_cw_len; |
43 | | static int hf_cesoeth_cw_seq; |
44 | | static int hf_cesoeth_padding; |
45 | | |
46 | | static int ett_cesoeth; |
47 | | static int ett_cesoeth_cw; |
48 | | |
49 | | static expert_field ei_cesoeth_reserved; |
50 | | static expert_field ei_cesoeth_length; |
51 | | |
52 | | static int* const cesoeth_l0_cw[] = |
53 | | { |
54 | | &hf_cesoeth_cw_reserved1, |
55 | | &hf_cesoeth_cw_l, |
56 | | &hf_cesoeth_cw_r, |
57 | | &hf_cesoeth_cw_l0_m, |
58 | | &hf_cesoeth_cw_frg, |
59 | | &hf_cesoeth_cw_len, |
60 | | &hf_cesoeth_cw_seq, |
61 | | NULL |
62 | | }; |
63 | | |
64 | | static int* const cesoeth_l1_cw[] = |
65 | | { |
66 | | &hf_cesoeth_cw_reserved1, |
67 | | &hf_cesoeth_cw_l, |
68 | | &hf_cesoeth_cw_r, |
69 | | &hf_cesoeth_cw_l1_m, |
70 | | &hf_cesoeth_cw_frg, |
71 | | &hf_cesoeth_cw_len, |
72 | | &hf_cesoeth_cw_seq, |
73 | | NULL |
74 | | }; |
75 | | |
76 | | static const value_string frg_names[] = |
77 | | { |
78 | | { 0, "No fragmentation" }, |
79 | | { 1, "First fragment" }, |
80 | | { 2, "Last fragment" }, |
81 | | { 3, "Intermediate fragment" }, |
82 | | { 0, NULL } |
83 | | }; |
84 | | |
85 | | static const value_string l0_m_names[] = |
86 | | { |
87 | | { 0, "No local TDM defect" }, |
88 | | { 1, "Reserved" }, |
89 | | { 2, "RDI on TDM input" }, |
90 | | { 3, "Non-TDM data" }, |
91 | | { 0, NULL } |
92 | | }; |
93 | | |
94 | | static const value_string l1_m_names[] = |
95 | | { |
96 | | { 0, "TDM defect" }, |
97 | | { 1, "Reserved" }, |
98 | | { 2, "Reserved" }, |
99 | | { 3, "Reserved" }, |
100 | | { 0, NULL } |
101 | | }; |
102 | | |
103 | | /* Preferences */ |
104 | | static bool has_rtp_header; |
105 | | static bool heuristic_rtp_header = true; |
106 | | |
107 | | static void cesoeth_ecid_prompt(packet_info *pinfo, char* result) |
108 | 0 | { |
109 | 0 | uint32_t *ecid = (uint32_t *)p_get_proto_data(pinfo->pool, pinfo, proto_cesoeth, pinfo->curr_layer_num); |
110 | 0 | snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "ECID 0x%05x as", *ecid); |
111 | 0 | } |
112 | | |
113 | | static void *cesoeth_ecid_value(packet_info *pinfo) |
114 | 0 | { |
115 | 0 | uint32_t *ecid = (uint32_t *)p_get_proto_data(pinfo->pool, pinfo, proto_cesoeth, pinfo->curr_layer_num); |
116 | 0 | return GUINT_TO_POINTER(*ecid); |
117 | 0 | } |
118 | | |
119 | | |
120 | | static int |
121 | | dissect_cesoeth(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
122 | 36 | { |
123 | 36 | proto_tree *cesoeth_tree; |
124 | 36 | proto_item *cesoeth_ti; |
125 | 36 | proto_item *bitmask_ti; |
126 | 36 | int offset = 0; |
127 | 36 | uint32_t *ecid = wmem_new(pinfo->pool, uint32_t); |
128 | 36 | uint32_t reserved; |
129 | 36 | bool l_bit, r_bit; |
130 | 36 | uint8_t m_bits, frg; |
131 | 36 | int cw_len, padding_len, tail_len, payload_len; |
132 | 36 | uint16_t sn; |
133 | 36 | tvbuff_t *next_tvb; |
134 | | |
135 | 36 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "CESoETH"); |
136 | 36 | col_clear(pinfo->cinfo, COL_INFO); |
137 | | |
138 | 36 | cesoeth_ti = proto_tree_add_item(tree, proto_cesoeth, tvb, 0, -1, ENC_NA); |
139 | 36 | cesoeth_tree = proto_item_add_subtree(cesoeth_ti, ett_cesoeth); |
140 | | |
141 | 36 | proto_tree_add_item_ret_uint(cesoeth_tree, hf_cesoeth_pw_ecid, tvb, offset, 4, ENC_BIG_ENDIAN, ecid); |
142 | 36 | col_append_fstr(pinfo->cinfo, COL_INFO, "ECID: 0x%05x", *ecid); |
143 | 36 | p_add_proto_data(pinfo->pool, pinfo, proto_cesoeth, pinfo->curr_layer_num, ecid); |
144 | 36 | bitmask_ti = proto_tree_add_item_ret_uint(cesoeth_tree, hf_cesoeth_pw_res, tvb, offset, 4, ENC_BIG_ENDIAN, &reserved); |
145 | 36 | if (reserved != 0x102) |
146 | 36 | expert_add_info_format(pinfo, bitmask_ti, &ei_cesoeth_reserved, "Reserved field must be 0x102"); |
147 | 36 | offset += 4; |
148 | | |
149 | 36 | next_tvb = tvb_new_subset_length(tvb, offset, tvb_reported_length_remaining(tvb, offset)); |
150 | 36 | payload_len = dissector_try_uint(cesoeth_dissector_table, *ecid, next_tvb, pinfo, tree); |
151 | 36 | if (payload_len) |
152 | 0 | return payload_len; |
153 | | |
154 | | /* |
155 | | * CES header control word |
156 | | * |
157 | | * bits name description |
158 | | * 31-28 reserved set to 0 |
159 | | * 27 L-bit set to 1 to indicate local TDM failure |
160 | | * 26 R-bit set to 1 to indicate remote loss of frame |
161 | | * 25-24 M-bits modifier bits |
162 | | * 23-22 FRG bits fragmentation bits |
163 | | * 21-16 length length (0 if no padding applied) |
164 | | * 15-0 sequence sequence number |
165 | | */ |
166 | | |
167 | 36 | l_bit = (tvb_get_uint8(tvb, offset) & 0x08) ? true : false; |
168 | 36 | r_bit = (tvb_get_uint8(tvb, offset) & 0x04) ? true : false; |
169 | 36 | m_bits = (tvb_get_uint8(tvb, offset) & 0x03); |
170 | 36 | frg = tvb_get_bits8(tvb, 40, 2); |
171 | 36 | cw_len = tvb_get_bits8(tvb, 42, 6); |
172 | 36 | sn = tvb_get_ntohs(tvb, offset + 2); |
173 | | |
174 | 36 | col_append_fstr(pinfo->cinfo, COL_INFO, ", SN: %u", sn); |
175 | | |
176 | 36 | if (l_bit) |
177 | 18 | { |
178 | 18 | bitmask_ti = proto_tree_add_bitmask(cesoeth_tree, tvb, offset, hf_cesoeth_cw, ett_cesoeth_cw, cesoeth_l1_cw, ENC_BIG_ENDIAN); |
179 | 18 | col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "%s", val_to_str_const(m_bits, l1_m_names, "Unknown")); |
180 | 18 | } else { |
181 | 18 | bitmask_ti = proto_tree_add_bitmask(cesoeth_tree, tvb, offset, hf_cesoeth_cw, ett_cesoeth_cw, cesoeth_l0_cw, ENC_BIG_ENDIAN); |
182 | 18 | if (m_bits) |
183 | 6 | col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "%s", val_to_str_const(m_bits, l0_m_names, "Unknown")); |
184 | 18 | } |
185 | | |
186 | 36 | if (cw_len >= 42) |
187 | 28 | { |
188 | | /* |
189 | | * Now we have to go spelunking in the bitmask tree for the length item |
190 | | * in order to add an expert item to it. |
191 | | */ |
192 | 28 | proto_tree *bm_tree = proto_item_get_subtree(bitmask_ti); |
193 | 28 | if (bm_tree) { |
194 | 28 | proto_item *pi; |
195 | | |
196 | 766 | for (pi = tree->first_child; pi; pi = pi->next) |
197 | 738 | { |
198 | 738 | field_info *fi; |
199 | 738 | fi = PITEM_FINFO(pi); |
200 | 738 | if (fi && (fi->hfinfo->id == hf_cesoeth_cw_len)) |
201 | 0 | break; |
202 | 738 | } |
203 | | |
204 | 28 | expert_add_info_format(pinfo, pi, &ei_cesoeth_length, "Length can not be 42 or larger"); |
205 | 28 | } |
206 | | |
207 | 28 | cw_len = 0; /* Put a stop to this madness */ |
208 | 28 | } |
209 | | |
210 | 36 | if (r_bit) |
211 | 9 | col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "Remote loss of frame"); |
212 | | |
213 | 36 | if (frg) |
214 | 13 | col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "%s", val_to_str_const(frg, frg_names, "Unknown")); |
215 | | |
216 | 36 | offset += 4; |
217 | | |
218 | | /* |
219 | | * When L is set the TDM payload MAY be missing. |
220 | | * But also when snap value is used on capture. |
221 | | * Will the optional RTP header be there when the TDM payload is missing? I guess so. |
222 | | * Length includes the size of the CW (being 4), the optional RTP header (being 12) and |
223 | | * the TDM payload, as long as it doesn't exceed 42 octets. Length > 0 indicates padding, |
224 | | * which must NOT be passed to the RTP dissector, it's not RTP padding. |
225 | | */ |
226 | | |
227 | 36 | padding_len = (cw_len > 0) ? (42 - cw_len) : 0; |
228 | 36 | tail_len = tvb_reported_length_remaining(tvb, offset); |
229 | 36 | payload_len = tail_len - padding_len; |
230 | | |
231 | 36 | if (payload_len > 0) |
232 | 35 | { |
233 | 35 | next_tvb = tvb_new_subset_length(tvb, offset, payload_len); |
234 | | |
235 | 35 | if ((has_rtp_header) || |
236 | 35 | ((heuristic_rtp_header) && |
237 | | /* Check for RTP version 2, the other fields must be zero */ |
238 | 35 | (tvb_get_uint8(tvb, offset) == 0x80) && |
239 | | /* Check the marker is zero. Unfortunately PT is not always from the dynamic range */ |
240 | 1 | ((tvb_get_uint8(tvb, offset + 1) & 0x80) == 0) && |
241 | | /* The sequence numbers from cw and RTP header must match */ |
242 | 1 | (tvb_get_ntohs(tvb, offset + 2) == sn))) |
243 | 0 | { |
244 | 0 | struct _rtp_info rtp_info; |
245 | |
|
246 | 0 | int rtp_header_len = dissect_rtp_shim_header(tvb, offset, pinfo, cesoeth_tree, &rtp_info); |
247 | |
|
248 | 0 | if (rtp_header_len > 0) |
249 | 0 | { |
250 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "CESoETH (w RTP)"); |
251 | 0 | col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "RTP PT: %u, SSRC: 0x%X, Seq: %u, Time=%u", |
252 | 0 | rtp_info.info_payload_type, |
253 | 0 | rtp_info.info_sync_src, |
254 | 0 | rtp_info.info_seq_num, |
255 | 0 | rtp_info.info_timestamp |
256 | 0 | ); |
257 | |
|
258 | 0 | next_tvb = tvb_new_subset_length(tvb, offset + rtp_header_len, payload_len - rtp_header_len); |
259 | 0 | } |
260 | 0 | else |
261 | 0 | { |
262 | 0 | col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "RTP header missing"); |
263 | 0 | } |
264 | 0 | } |
265 | | |
266 | 35 | call_data_dissector(next_tvb, pinfo, tree); |
267 | | |
268 | 35 | offset += payload_len; |
269 | 35 | } |
270 | | |
271 | 36 | if (padding_len > 0) |
272 | 6 | { |
273 | 6 | proto_tree_add_item(cesoeth_tree, hf_cesoeth_padding, tvb, offset, padding_len, ENC_NA); |
274 | | |
275 | 6 | offset += padding_len; |
276 | 6 | } |
277 | | |
278 | 36 | return offset; |
279 | 36 | } |
280 | | |
281 | | void |
282 | | proto_register_cesoeth(void) |
283 | 15 | { |
284 | 15 | static hf_register_info hf[] = { |
285 | 15 | { &hf_cesoeth_pw_ecid, |
286 | 15 | { "ECID", "cesoeth.ecid", FT_UINT32, BASE_HEX, |
287 | 15 | NULL, 0xFFFFF000, NULL, HFILL }}, |
288 | 15 | { &hf_cesoeth_pw_res, |
289 | 15 | { "Reserved", "cesoeth.res", FT_UINT32, BASE_HEX, |
290 | 15 | NULL, 0x00000FFF, "Reserved (0x102)", HFILL }}, |
291 | | |
292 | 15 | { &hf_cesoeth_cw, |
293 | 15 | { "Control word", "cesoeth.cw", FT_UINT32, BASE_HEX, |
294 | 15 | NULL, 0x0, NULL, HFILL }}, |
295 | | |
296 | 15 | { &hf_cesoeth_cw_reserved1, |
297 | 15 | { "Reserved", "cesoeth.cw.reserved", FT_UINT32, BASE_HEX, |
298 | 15 | NULL, 0xF0000000, NULL, HFILL }}, |
299 | 15 | { &hf_cesoeth_cw_l, |
300 | 15 | { "L-bit", "cesoeth.cw.l", FT_BOOLEAN, 32, |
301 | 15 | NULL, 0x08000000, "Local TDM failure", HFILL }}, |
302 | 15 | { &hf_cesoeth_cw_r, |
303 | 15 | { "R-bit", "cesoeth.cw.r", FT_BOOLEAN, 32, |
304 | 15 | NULL, 0x04000000, "Remote Loss of Frames indication", HFILL }}, |
305 | 15 | { &hf_cesoeth_cw_l0_m, |
306 | 15 | { "M-bits", "cesoeth.cw.m", FT_UINT32, BASE_HEX, |
307 | 15 | VALS(l0_m_names), 0x03000000, "Modifier bits", HFILL }}, |
308 | 15 | { &hf_cesoeth_cw_l1_m, |
309 | 15 | { "M-bits", "cesoeth.cw.m", FT_UINT32, BASE_HEX, |
310 | 15 | VALS(l1_m_names), 0x03000000, "Modifier bits", HFILL }}, |
311 | 15 | { &hf_cesoeth_cw_frg, |
312 | 15 | { "Frg", "cesoeth.cw.frg", FT_UINT32, BASE_HEX, |
313 | 15 | VALS(frg_names), 0x00C00000, "Fragmentation bits", HFILL }}, |
314 | 15 | { &hf_cesoeth_cw_len, |
315 | 15 | { "Len", "cesoeth.cw.len", FT_UINT32, BASE_DEC, |
316 | 15 | NULL, 0x003F0000, "Length", HFILL }}, |
317 | 15 | { &hf_cesoeth_cw_seq, |
318 | 15 | { "SN", "cesoeth.cw.sn", FT_UINT32, BASE_DEC, |
319 | 15 | NULL, 0x0000FFFF, "Sequence number", HFILL }}, |
320 | | |
321 | 15 | { &hf_cesoeth_padding, |
322 | 15 | { "Padding", "cesoeth.padding", FT_BYTES, BASE_NONE, |
323 | 15 | NULL, 0x0, NULL, HFILL }} |
324 | 15 | }; |
325 | | |
326 | 15 | static int *ett[] = { |
327 | 15 | &ett_cesoeth, |
328 | 15 | &ett_cesoeth_cw |
329 | 15 | }; |
330 | | |
331 | 15 | static ei_register_info ei[] = { |
332 | 15 | { &ei_cesoeth_reserved, |
333 | 15 | { "cesoeth.reserved", PI_PROTOCOL, PI_WARN, |
334 | 15 | "Reserved field", EXPFILL }}, |
335 | 15 | { &ei_cesoeth_length, |
336 | 15 | { "cesoeth.length", PI_PROTOCOL, PI_WARN, |
337 | 15 | "Length field", EXPFILL }} |
338 | 15 | }; |
339 | | |
340 | | /* Decode As handling */ |
341 | 15 | static build_valid_func cesoeth_ecid_da_build_value[1] = {cesoeth_ecid_value}; |
342 | 15 | static decode_as_value_t cesoeth_ecid_da_values = {cesoeth_ecid_prompt, 1, cesoeth_ecid_da_build_value}; |
343 | 15 | static decode_as_t cesoeth_ecid_da = {"cesoeth", "cesoeth.ecid", 1, 0, &cesoeth_ecid_da_values, NULL, NULL, |
344 | 15 | decode_as_default_populate_list, decode_as_default_reset, decode_as_default_change, |
345 | 15 | NULL, NULL, NULL}; |
346 | | |
347 | 15 | module_t *cesoeth_module; |
348 | 15 | expert_module_t* expert_cesoeth; |
349 | | |
350 | 15 | proto_cesoeth = proto_register_protocol("Circuit Emulation Service over Ethernet", "CESoETH", "cesoeth"); |
351 | 15 | proto_register_field_array(proto_cesoeth, hf, array_length(hf)); |
352 | 15 | proto_register_subtree_array(ett, array_length(ett)); |
353 | 15 | expert_cesoeth = expert_register_protocol(proto_cesoeth); |
354 | 15 | expert_register_field_array(expert_cesoeth, ei, array_length(ei)); |
355 | | |
356 | 15 | cesoeth_dissector_table = register_dissector_table("cesoeth.ecid", "ECID", |
357 | 15 | proto_cesoeth, FT_UINT24, BASE_HEX); |
358 | 15 | register_decode_as(&cesoeth_ecid_da); |
359 | | |
360 | 15 | cesoeth_module = prefs_register_protocol(proto_cesoeth, NULL); |
361 | | |
362 | 15 | prefs_register_bool_preference(cesoeth_module, "rtp_header", "RTP header in CES payload", |
363 | 15 | "Whether or not the RTP header is present in the CES payload.", &has_rtp_header); |
364 | | |
365 | 15 | prefs_register_bool_preference(cesoeth_module, "rtp_header_heuristic", "Try to find RTP header in CES payload", |
366 | 15 | "Heuristically determine if an RTP header is present in the CES payload.", &heuristic_rtp_header); |
367 | | |
368 | | |
369 | 15 | cesoeth_handle = register_dissector("cesoeth", dissect_cesoeth, proto_cesoeth); |
370 | 15 | } |
371 | | |
372 | | void |
373 | | proto_reg_handoff_cesoeth(void) |
374 | 15 | { |
375 | 15 | dissector_add_uint("ethertype", ETHERTYPE_CESOETH, cesoeth_handle); |
376 | 15 | } |
377 | | |
378 | | /* |
379 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
380 | | * |
381 | | * Local variables: |
382 | | * c-basic-offset: 4 |
383 | | * tab-width: 8 |
384 | | * indent-tabs-mode: nil |
385 | | * End: |
386 | | * |
387 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
388 | | * :indentSize=4:tabSize=8:noTabs=true: |
389 | | */ |