Coverage Report

Created: 2026-11-12 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
 */