/src/wireshark/epan/dissectors/packet-rmt-alc.c
Line | Count | Source |
1 | | /* packet-rmt-alc.c |
2 | | * Reliable Multicast Transport (RMT) |
3 | | * ALC Protocol Instantiation dissector |
4 | | * Copyright 2005, Stefano Pettini <spettini@users.sourceforge.net> |
5 | | * Copyright 2023, Sergey V. Lobanov <sergey@lobanov.in> |
6 | | * |
7 | | * Asynchronous Layered Coding (ALC): |
8 | | * ---------------------------------- |
9 | | * |
10 | | * A massively scalable reliable content delivery protocol. |
11 | | * Asynchronous Layered Coding combines the Layered Coding Transport |
12 | | * (LCT) building block, a multiple rate congestion control building |
13 | | * block and the Forward Error Correction (FEC) building block to |
14 | | * provide congestion controlled reliable asynchronous delivery of |
15 | | * content to an unlimited number of concurrent receivers from a single |
16 | | * sender. |
17 | | * |
18 | | * References: |
19 | | * RFC 3450, Asynchronous Layered Coding protocol instantiation |
20 | | * |
21 | | * Wireshark - Network traffic analyzer |
22 | | * By Gerald Combs <gerald@wireshark.org> |
23 | | * Copyright 1998 Gerald Combs |
24 | | * |
25 | | * SPDX-License-Identifier: GPL-2.0-or-later |
26 | | */ |
27 | | |
28 | | #include "config.h" |
29 | | |
30 | | #include <epan/packet.h> |
31 | | #include <epan/prefs.h> |
32 | | #include <epan/expert.h> |
33 | | #include <epan/conversation.h> |
34 | | #include <wiretap/wtap.h> |
35 | | #include <wsutil/array.h> |
36 | | |
37 | | #include "packet-rmt-common.h" |
38 | | #include "packet-lls.h" |
39 | | |
40 | | /* Initialize the protocol and registered fields */ |
41 | | /* ============================================= */ |
42 | | static dissector_handle_t alc_handle; |
43 | | |
44 | | void proto_register_alc(void); |
45 | | void proto_reg_handoff_alc(void); |
46 | | |
47 | | static int proto_rmt_alc; |
48 | | |
49 | | static int hf_version; |
50 | | static int hf_atsc3; |
51 | | static int hf_object_start_offset; |
52 | | static int hf_payload; |
53 | | static int hf_uncomp_payload; |
54 | | |
55 | | static int ett_main; |
56 | | static int ett_uncomp_payload; |
57 | | static int ett_uncomp_decode; |
58 | | |
59 | | static expert_field ei_version1_only; |
60 | | |
61 | | static dissector_handle_t xml_handle; |
62 | | static dissector_handle_t rmt_lct_handle; |
63 | | static dissector_handle_t rmt_fec_handle; |
64 | | |
65 | | static dissector_table_t media_type_dissector_table; |
66 | | |
67 | | static bool g_codepoint_as_fec_encoding = true; |
68 | | static int g_ext_192 = LCT_PREFS_EXT_192_FLUTE; |
69 | | static int g_ext_193 = LCT_PREFS_EXT_193_FLUTE; |
70 | | static int g_atsc3_mode = LCT_ATSC3_MODE_AUTO; |
71 | | |
72 | | static void |
73 | | try_decode_payload(tvbuff_t *tvb, packet_info *pinfo, proto_item *tree) |
74 | 0 | { |
75 | 0 | uint32_t b03 = tvb_get_uint32(tvb, 0, ENC_BIG_ENDIAN); |
76 | | /* xml ("<?xm") */ |
77 | 0 | if (b03 == 0x3C3F786D) { |
78 | 0 | call_dissector(xml_handle, tvb, pinfo, tree); |
79 | 0 | } else { |
80 | 0 | uint32_t b47 = tvb_get_uint32(tvb, 4, ENC_BIG_ENDIAN); |
81 | | /* mp4 ("ftyp" or "sidx" or "styp" mp4 box) */ |
82 | 0 | if (b47 == 0x66747970 || b47 == 0x73696478 || b47 == 0x73747970) { |
83 | | /* MP4 dissector removes useful info from Protocol and Info columns so store it */ |
84 | 0 | char *col_info_text = wmem_strdup(pinfo->pool, col_get_text(pinfo->cinfo, COL_INFO)); |
85 | 0 | char *col_protocol_text = wmem_strdup(pinfo->pool, col_get_text(pinfo->cinfo, COL_PROTOCOL)); |
86 | |
|
87 | 0 | int mp4_dis = dissector_try_string_with_data(media_type_dissector_table, "video/mp4", tvb, pinfo, tree, true, NULL); |
88 | 0 | char *col_protocol_text_mp4 = wmem_strdup(pinfo->pool,col_get_text(pinfo->cinfo, COL_PROTOCOL)); |
89 | | |
90 | | /* Restore Protocol and Info columns and add MP4 Protocol Info */ |
91 | 0 | col_set_str(pinfo->cinfo, COL_INFO, col_info_text); |
92 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, col_protocol_text); |
93 | 0 | if (mp4_dis > 0) { |
94 | 0 | col_append_sep_str(pinfo->cinfo, COL_PROTOCOL, "/", col_protocol_text_mp4); |
95 | 0 | } |
96 | 0 | } |
97 | 0 | } |
98 | 0 | } |
99 | | |
100 | | static void |
101 | | try_uncompress(tvbuff_t *tvb, packet_info *pinfo, int offset, /*int len,*/ proto_item *ti) |
102 | 0 | { |
103 | 0 | tvbuff_t *uncompress_tvb = tvb_child_uncompress_zlib(tvb, tvb, offset, tvb_captured_length_remaining(tvb, offset)); |
104 | 0 | if (uncompress_tvb) { |
105 | 0 | add_new_data_source(pinfo, uncompress_tvb, "Uncompressed Payload"); |
106 | |
|
107 | 0 | proto_tree *uncompress_tree = proto_item_add_subtree(ti, ett_uncomp_payload); |
108 | 0 | unsigned decomp_length = tvb_captured_length(uncompress_tvb); |
109 | 0 | proto_item *ti_uncomp = proto_tree_add_item(uncompress_tree, hf_uncomp_payload, uncompress_tvb, 0, decomp_length, ENC_NA); |
110 | 0 | proto_item_set_generated(ti_uncomp); |
111 | |
|
112 | 0 | proto_tree *payload_tree = proto_item_add_subtree(ti_uncomp, ett_uncomp_decode); |
113 | 0 | try_decode_payload(uncompress_tvb, pinfo, payload_tree); |
114 | 0 | } |
115 | 0 | } |
116 | | |
117 | | /* Code to actually dissect the packets */ |
118 | | /* ==================================== */ |
119 | | static int |
120 | | dissect_alc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
121 | 4 | { |
122 | 4 | uint8_t version; |
123 | 4 | lct_data_exchange_t lct; |
124 | 4 | fec_data_exchange_t fec; |
125 | 4 | int len; |
126 | 4 | bool is_atsc3; |
127 | | |
128 | 4 | if (g_atsc3_mode == LCT_ATSC3_MODE_FORCE) { |
129 | 0 | is_atsc3 = true; |
130 | 4 | } else if (g_atsc3_mode == LCT_ATSC3_MODE_DISABLED) { |
131 | 0 | is_atsc3 = false; |
132 | 4 | } else { /* Auto detect mode*/ |
133 | | /* If packet encap is ALP then it is necessary to use ATSC decoding mode*/ |
134 | 4 | is_atsc3 = pinfo->rec->rec_header.packet_header.pkt_encap == WTAP_ENCAP_ATSC_ALP; |
135 | 4 | } |
136 | | |
137 | | /* Offset for subpacket dissection */ |
138 | 4 | unsigned offset = 0; |
139 | | |
140 | | /* Set up structures needed to add the protocol subtree and manage it */ |
141 | 4 | proto_item *ti; |
142 | 4 | proto_tree *alc_tree; |
143 | | |
144 | 4 | tvbuff_t *new_tvb; |
145 | | |
146 | | /* Make entries in Protocol column and Info column on summary display */ |
147 | 4 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "ALC"); |
148 | 4 | col_clear(pinfo->cinfo, COL_INFO); |
149 | | |
150 | | /* ALC header dissection */ |
151 | | /* --------------------- */ |
152 | | |
153 | 4 | version = hi_nibble(tvb_get_uint8(tvb, offset)); |
154 | | |
155 | | /* Create subtree for the ALC protocol */ |
156 | 4 | ti = proto_tree_add_item(tree, proto_rmt_alc, tvb, offset, -1, ENC_NA); |
157 | 4 | alc_tree = proto_item_add_subtree(ti, ett_main); |
158 | | |
159 | | /* Fill the ALC subtree */ |
160 | 4 | ti = proto_tree_add_uint(alc_tree, hf_version, tvb, offset, 1, version); |
161 | 4 | PROTO_ITEM_SET_GENERATED( |
162 | 4 | proto_tree_add_boolean(alc_tree, hf_atsc3, 0, 0, 0, is_atsc3) |
163 | 4 | ); |
164 | | |
165 | | /* This dissector supports only ALCv1 packets. |
166 | | * If version > 1 print only version field and quit. |
167 | | */ |
168 | 4 | if (version != 1) { |
169 | 0 | expert_add_info(pinfo, ti, &ei_version1_only); |
170 | | |
171 | | /* Complete entry in Info column on summary display */ |
172 | 0 | col_add_fstr(pinfo->cinfo, COL_INFO, "Version: %u (not supported)", version); |
173 | 0 | return 0; |
174 | 0 | } |
175 | | |
176 | | /* LCT header dissection */ |
177 | | /* --------------------- */ |
178 | 4 | new_tvb = tvb_new_subset_remaining(tvb,offset); |
179 | | |
180 | 4 | lct.ext_192 = g_ext_192; |
181 | 4 | lct.ext_193 = g_ext_193; |
182 | 4 | lct.codepoint = 0; |
183 | 4 | lct.is_flute = false; |
184 | 4 | lct.is_atsc3 = is_atsc3; |
185 | 4 | lct.is_sp = false; |
186 | 4 | len = call_dissector_with_data(rmt_lct_handle, new_tvb, pinfo, alc_tree, &lct); |
187 | 4 | if (len < 0) |
188 | 0 | return offset; |
189 | | |
190 | 4 | offset += len; |
191 | | |
192 | | /* FEC header dissection */ |
193 | | /* --------------------- */ |
194 | | |
195 | | /* Only if LCT dissector has determined FEC Encoding ID */ |
196 | | /* FEC dissector needs to be called with encoding_id filled */ |
197 | 4 | if (!lct.is_sp && g_codepoint_as_fec_encoding && tvb_reported_length(tvb) > offset) |
198 | 2 | { |
199 | 2 | fec.encoding_id = lct.codepoint; |
200 | | |
201 | 2 | new_tvb = tvb_new_subset_remaining(tvb,offset); |
202 | 2 | len = call_dissector_with_data(rmt_fec_handle, new_tvb, pinfo, alc_tree, &fec); |
203 | 2 | if (len < 0) |
204 | 0 | return offset; |
205 | | |
206 | 2 | offset += len; |
207 | 2 | } |
208 | | |
209 | | /* A/331 specifies start_offset field */ |
210 | 4 | int64_t object_start_offset = -1; |
211 | 4 | if (lct.is_sp) { |
212 | 0 | object_start_offset = tvb_get_uint32(tvb, offset, ENC_BIG_ENDIAN); |
213 | 0 | proto_tree_add_item(alc_tree, hf_object_start_offset, tvb, offset, 4, ENC_BIG_ENDIAN); |
214 | 0 | offset += 4; |
215 | 0 | } |
216 | | |
217 | | /* Add the Payload item */ |
218 | 4 | if (tvb_reported_length(tvb) > offset){ |
219 | 1 | if(lct.is_flute){ |
220 | 0 | new_tvb = tvb_new_subset_remaining(tvb,offset); |
221 | 0 | call_dissector(xml_handle, new_tvb, pinfo, alc_tree); |
222 | 1 | }else{ |
223 | 1 | ti = proto_tree_add_item(alc_tree, hf_payload, tvb, offset, -1, ENC_NA); |
224 | 1 | if (object_start_offset == 0 && |
225 | 0 | tvb_captured_length_remaining(tvb, offset) > 18 && |
226 | 0 | tvb_get_uint16(tvb, offset, ENC_BIG_ENDIAN) == 0x1f8b) { |
227 | | /* gzip is detected */ |
228 | 0 | try_uncompress(tvb, pinfo, offset, ti); |
229 | 1 | } else if (object_start_offset == 0) { |
230 | | /* gzip is not detected */ |
231 | 0 | new_tvb = tvb_new_subset_remaining(tvb, offset); |
232 | 0 | try_decode_payload(new_tvb, pinfo, alc_tree); |
233 | 0 | } |
234 | 1 | } |
235 | 1 | } |
236 | | |
237 | | /* Add Channel info in ATSC3 mode */ |
238 | 4 | if(lct.is_atsc3) { |
239 | 0 | char *channel_info = get_slt_channel_info(pinfo); |
240 | 0 | if (channel_info != NULL) { |
241 | 0 | col_append_sep_str(pinfo->cinfo, COL_INFO, " ", channel_info); |
242 | 0 | wmem_free(pinfo->pool, channel_info); |
243 | 0 | } |
244 | 0 | } |
245 | | |
246 | 4 | return tvb_reported_length(tvb); |
247 | 4 | } |
248 | | |
249 | | |
250 | | static bool |
251 | | dissect_alc_heur_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
252 | 103 | { |
253 | | /* Lookup over ATSC3 SLT Table*/ |
254 | 103 | if (!test_alc_over_slt(pinfo, tvb, 0, data)) |
255 | 103 | return false; |
256 | | |
257 | 0 | conversation_t *conversation = find_or_create_conversation(pinfo); |
258 | 0 | conversation_set_dissector(conversation, alc_handle); |
259 | |
|
260 | 0 | return (dissect_alc(tvb, pinfo, tree, data) != 0); |
261 | 103 | } |
262 | | |
263 | | void proto_register_alc(void) |
264 | 16 | { |
265 | | /* Setup ALC header fields */ |
266 | 16 | static hf_register_info hf_ptr[] = { |
267 | | |
268 | 16 | { &hf_version, |
269 | 16 | { "Version", "alc.version", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, |
270 | | |
271 | 16 | { &hf_atsc3, |
272 | 16 | { "Decode as ATSC3", "alc.atsc3", FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL }}, |
273 | | |
274 | 16 | { &hf_object_start_offset, |
275 | 16 | { "Object Start Offset", "alc.object_start_offset", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, |
276 | | |
277 | 16 | { &hf_payload, |
278 | 16 | { "Payload", "alc.payload", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, |
279 | | |
280 | 16 | { &hf_uncomp_payload, |
281 | 16 | { "Uncompressed Payload", "alc.payload.uncompressed", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, |
282 | 16 | }; |
283 | | |
284 | | /* Setup protocol subtree array */ |
285 | 16 | static int *ett_ptr[] = { |
286 | 16 | &ett_main, |
287 | 16 | &ett_uncomp_payload, |
288 | 16 | &ett_uncomp_decode, |
289 | 16 | }; |
290 | | |
291 | 16 | static ei_register_info ei[] = { |
292 | 16 | { &ei_version1_only, { "alc.version1_only", PI_PROTOCOL, PI_WARN, "Sorry, this dissector supports ALC version 1 only", EXPFILL }}, |
293 | 16 | }; |
294 | | |
295 | 16 | module_t *module; |
296 | 16 | expert_module_t* expert_rmt_alc; |
297 | | |
298 | | /* Register the protocol name and description */ |
299 | 16 | proto_rmt_alc = proto_register_protocol("Asynchronous Layered Coding", "ALC", "alc"); |
300 | 16 | alc_handle = register_dissector("alc", dissect_alc, proto_rmt_alc); |
301 | | |
302 | | /* Register the header fields and subtrees used */ |
303 | 16 | proto_register_field_array(proto_rmt_alc, hf_ptr, array_length(hf_ptr)); |
304 | 16 | proto_register_subtree_array(ett_ptr, array_length(ett_ptr)); |
305 | 16 | expert_rmt_alc = expert_register_protocol(proto_rmt_alc); |
306 | 16 | expert_register_field_array(expert_rmt_alc, ei, array_length(ei)); |
307 | | |
308 | | /* Register preferences */ |
309 | 16 | module = prefs_register_protocol(proto_rmt_alc, NULL); |
310 | | |
311 | 16 | prefs_register_obsolete_preference(module, "default.udp_port.enabled"); |
312 | | |
313 | 16 | prefs_register_bool_preference(module, |
314 | 16 | "lct.codepoint_as_fec_id", |
315 | 16 | "LCT Codepoint as FEC Encoding ID", |
316 | 16 | "Whether the LCT header Codepoint field should be considered the FEC Encoding ID of carried object", |
317 | 16 | &g_codepoint_as_fec_encoding); |
318 | | |
319 | 16 | prefs_register_enum_preference(module, |
320 | 16 | "lct.ext.192", |
321 | 16 | "LCT header extension 192", |
322 | 16 | "How to decode LCT header extension 192", |
323 | 16 | &g_ext_192, |
324 | 16 | enum_lct_ext_192, |
325 | 16 | false); |
326 | | |
327 | 16 | prefs_register_enum_preference(module, |
328 | 16 | "lct.ext.193", |
329 | 16 | "LCT header extension 193", |
330 | 16 | "How to decode LCT header extension 193", |
331 | 16 | &g_ext_193, |
332 | 16 | enum_lct_ext_193, |
333 | 16 | false); |
334 | | |
335 | 16 | prefs_register_enum_preference(module, |
336 | 16 | "lct.atsc3.mode", |
337 | 16 | "ATSC3 Mode", |
338 | 16 | "How to detect ATSC3 data", |
339 | 16 | &g_atsc3_mode, |
340 | 16 | enum_lct_atsc3_mode, |
341 | 16 | false); |
342 | 16 | } |
343 | | |
344 | | void proto_reg_handoff_alc(void) |
345 | 16 | { |
346 | 16 | dissector_add_for_decode_as_with_preference("udp.port", alc_handle); |
347 | 16 | xml_handle = find_dissector_add_dependency("xml", proto_rmt_alc); |
348 | 16 | rmt_lct_handle = find_dissector_add_dependency("rmt-lct", proto_rmt_alc); |
349 | 16 | rmt_fec_handle = find_dissector_add_dependency("rmt-fec", proto_rmt_alc); |
350 | 16 | heur_dissector_add("udp", dissect_alc_heur_udp, "Asynchronous Layered Coding", |
351 | 16 | "alc", proto_rmt_alc, HEURISTIC_ENABLE); |
352 | | |
353 | 16 | media_type_dissector_table = find_dissector_table("media_type"); |
354 | 16 | } |
355 | | |
356 | | /* |
357 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
358 | | * |
359 | | * Local variables: |
360 | | * c-basic-offset: 4 |
361 | | * tab-width: 8 |
362 | | * indent-tabs-mode: nil |
363 | | * End: |
364 | | * |
365 | | * ex: set shiftwidth=4 tabstop=8 expandtab: |
366 | | * :indentSize=4:tabSize=8:noTabs=true: |
367 | | */ |