Coverage Report

Created: 2025-12-27 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-lls.c
Line
Count
Source
1
/* packet-lls.c
2
 * Routines for ATSC3 LLS(Low Level Signalling) dissection
3
 * Copyright 2023, Sergey V. Lobanov <sergey@lobanov.in>
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
/*
13
 * ATSC3 Signaling, Delivery, Synchronization, and Error Protection (A/331)
14
 * https://www.atsc.org/atsc-documents/3312017-signaling-delivery-synchronization-error-protection/
15
 *
16
 * ATSC3 Security and Service Protection (A/360)
17
 * https://www.atsc.org/atsc-documents/3602018-atsc-3-0-security-service-protection/
18
 *
19
 * ATSC Code Point Registry
20
 * https://www.atsc.org/documents/code-point-registry/
21
 */
22
23
#include <config.h>
24
#include <epan/expert.h>
25
#include <epan/packet.h>
26
27
#include "packet-lls.h"
28
29
14
#define LLS_PORT 4937 // IANA Registered (atsc-mh-ssc)
30
31
void proto_reg_handoff_lls(void);
32
void proto_register_lls(void);
33
34
static int proto_lls;
35
static int ett_lls;
36
static int ett_lls_smt_entry;
37
static int ett_lls_smt_signature;
38
static int ett_lls_table_payload;
39
static int ett_lls_table_payload_xml;
40
41
static dissector_handle_t lls_handle;
42
static dissector_handle_t xml_handle;
43
static dissector_handle_t cms_handle;
44
45
static expert_field ei_lls_table_decompression_failed;
46
47
static int hf_lls_table_id;
48
206
#define LLS_TABLE_TYPE_SIGNED_MULTI_TABLE 0xFE
49
266
#define LLS_TABLE_TYPE_SLT                0x01
50
static const value_string hf_lls_table_type_vals[] = {
51
    { 0x01, "SLT (Service List Table)" },
52
    { 0x02, "RRT (Rating Region Table)" },
53
    { 0x03, "System Time" },
54
    { 0x04, "AEAT (Advanced Emergency Information Table)" },
55
    { 0x05, "On Screen Message Notification" },
56
    { 0x06, "CDT (Certification Data Table)" },
57
    { 0x07, "DRCT (Dedicated Return Channel Table)" },
58
    { 0x80, "VIT (Version Information Table)" },
59
    { 0x81, "CPT (Content Protection Table)" },
60
    { 0x82, "CAP (Common Alerting Protocol)" },
61
    { 0xFE, "Signed Multi Table" },
62
    { 0xFF, "User Defined" },
63
    { 0x00, NULL }
64
};
65
static const value_string hf_lls_table_type_short_vals[] = {
66
    { 0x01, "SLT" },
67
    { 0x02, "RRT" },
68
    { 0x03, "ST" },
69
    { 0x04, "AEAT" },
70
    { 0x05, "OSMN" },
71
    { 0x06, "CDT" },
72
    { 0x07, "DRCT" },
73
    { 0x80, "VIT" },
74
    { 0x81, "CPT" },
75
    { 0x82, "CAP" },
76
    { 0xFE, "SMT" },
77
    { 0xFF, "USD" },
78
    { 0x00, NULL }
79
};
80
81
static int hf_lls_group_id;
82
static int hf_lls_group_count;
83
static int hf_lls_table_version;
84
static int hf_lls_table_payload;
85
static int hf_lls_table_payload_uncompressed;
86
87
static int hf_lls_smt_payload_count;
88
static int hf_lls_smt_entry;
89
static int hf_lls_smt_entry_payload_length;
90
static int hf_lls_smt_signature_length;
91
static int hf_lls_smt_signature;
92
93
94
static void
95
dissect_lls_table_payload(uint8_t lls_table_id, tvbuff_t *tvb, packet_info *pinfo, int offset, int len, proto_tree *tree)
96
134
{
97
134
    proto_item *ti = proto_tree_add_item(tree, hf_lls_table_payload, tvb, offset, len, ENC_NA);
98
99
134
    if (lls_table_id == LLS_TABLE_TYPE_SIGNED_MULTI_TABLE) {
100
        /* Nested SignedMultiTable decoding is not specified in the standard */
101
1
        return;
102
1
    }
103
104
133
    proto_tree *uncompress_tree = proto_item_add_subtree(ti, ett_lls_table_payload);
105
133
    tvbuff_t *uncompress_tvb = tvb_uncompress_zlib(tvb, offset, len);
106
133
    proto_tree *xml_tree = NULL;
107
133
    if (uncompress_tvb) {
108
12
        const char *table_type_short = val_to_str_const(lls_table_id, hf_lls_table_type_short_vals, "Unknown");
109
12
        char *source_name = wmem_strdup_printf(pinfo->pool, "Table ID %u (%s)", lls_table_id, table_type_short);
110
12
        add_new_data_source(pinfo, uncompress_tvb, source_name);
111
12
        unsigned decomp_length = tvb_captured_length(uncompress_tvb);
112
113
12
        proto_item *ti_uncomp = proto_tree_add_item(uncompress_tree, hf_lls_table_payload_uncompressed, uncompress_tvb, 0, decomp_length, ENC_ASCII);
114
12
        proto_item_set_generated(ti_uncomp);
115
116
12
        if (xml_handle) {
117
12
            xml_tree = proto_item_add_subtree(ti_uncomp, ett_lls_table_payload_xml);
118
12
            call_dissector(xml_handle, uncompress_tvb, pinfo, xml_tree);
119
12
        }
120
121
    } else {
121
121
        expert_add_info(pinfo, ti, &ei_lls_table_decompression_failed);
122
121
    }
123
124
133
    if (lls_table_id == LLS_TABLE_TYPE_SLT && xml_tree != NULL) {
125
2
        lls_extract_save_slt_table(pinfo, xml_handle);
126
2
    }
127
133
}
128
129
130
static int
131
dissect_lls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
132
72
{
133
72
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "LLS");
134
135
72
    proto_item *ti = proto_tree_add_item(tree, proto_lls, tvb, 0, -1, ENC_NA);
136
72
    proto_tree *lls_tree = proto_item_add_subtree(ti, ett_lls);
137
138
72
    int offset = 0;
139
140
72
    uint8_t lls_table_id = tvb_get_uint8(tvb, offset);
141
72
    col_set_str(pinfo->cinfo, COL_INFO, val_to_str_const(lls_table_id, hf_lls_table_type_vals, "Unknown"));
142
72
    proto_tree_add_item(lls_tree, hf_lls_table_id, tvb, offset, 1, ENC_BIG_ENDIAN);
143
72
    offset++;
144
145
72
    proto_tree_add_item(lls_tree, hf_lls_group_id, tvb, offset, 1, ENC_BIG_ENDIAN);
146
72
    offset++;
147
148
72
    uint16_t lls_group_count = tvb_get_uint8(tvb, offset) + 1;
149
72
    PROTO_ITEM_SET_GENERATED(
150
72
        proto_tree_add_uint(lls_tree, hf_lls_group_count, tvb, offset, 1, lls_group_count)
151
72
    );
152
72
    offset++;
153
154
72
    proto_tree_add_item(lls_tree, hf_lls_table_version, tvb, offset, 1, ENC_BIG_ENDIAN);
155
72
    offset++;
156
157
72
    if (lls_table_id == LLS_TABLE_TYPE_SIGNED_MULTI_TABLE) {
158
56
        uint8_t smt_payload_count = tvb_get_uint8(tvb, offset);
159
56
        proto_tree_add_item(lls_tree, hf_lls_smt_payload_count, tvb, offset, 1, ENC_BIG_ENDIAN);
160
56
        offset++;
161
162
175
        for(uint8_t i = 0; i < smt_payload_count; i++) {
163
119
            uint16_t smt_entry_payload_length = tvb_get_uint16(tvb, offset + 2, ENC_BIG_ENDIAN);
164
119
            proto_item *smt_entry_item = proto_tree_add_item(lls_tree, hf_lls_smt_entry, tvb, offset, smt_entry_payload_length + 4, ENC_NA);
165
119
            proto_tree *smt_entry_tree = proto_item_add_subtree(smt_entry_item, ett_lls_smt_entry);
166
167
119
            uint8_t smt_entry_table_id = tvb_get_uint8(tvb, offset);
168
119
            const char *table_type_short = val_to_str_const(smt_entry_table_id, hf_lls_table_type_short_vals, "Unknown");
169
119
            proto_item_append_text(smt_entry_item, " (%u) Table ID=%u (%s)", i, smt_entry_table_id, table_type_short);
170
119
            col_append_fstr(pinfo->cinfo, COL_INFO, "/%s", table_type_short);
171
119
            proto_tree_add_item(smt_entry_tree, hf_lls_table_id, tvb, offset, 1, ENC_BIG_ENDIAN);
172
119
            offset++;
173
174
119
            proto_tree_add_item(smt_entry_tree, hf_lls_table_version, tvb, offset, 1, ENC_BIG_ENDIAN);
175
119
            offset++;
176
177
119
            proto_tree_add_item(smt_entry_tree, hf_lls_smt_entry_payload_length, tvb, offset, 2, ENC_BIG_ENDIAN);
178
119
            offset += 2;
179
180
119
            dissect_lls_table_payload(smt_entry_table_id, tvb, pinfo, offset, smt_entry_payload_length, smt_entry_tree);
181
119
            offset += smt_entry_payload_length;
182
119
        }
183
184
56
        uint16_t smt_signature_length = tvb_get_uint16(tvb, offset, ENC_BIG_ENDIAN);
185
56
        proto_tree_add_item(lls_tree, hf_lls_smt_signature_length, tvb, offset, 2, ENC_BIG_ENDIAN);
186
56
        offset += 2;
187
188
56
        proto_item *smt_signature_item = proto_tree_add_item(lls_tree, hf_lls_smt_signature, tvb, offset, smt_signature_length, ENC_NA);
189
56
        if (cms_handle) {
190
46
            proto_tree *cms_tree = proto_item_add_subtree(smt_signature_item, ett_lls_smt_signature);
191
46
            tvbuff_t *cms_tvb = tvb_new_subset_length(tvb, offset, smt_signature_length);
192
193
            /* CMS dissector removes useful info from Protocol and Info columns so store it */
194
46
            char *col_info_text = wmem_strdup(pinfo->pool, col_get_text(pinfo->cinfo, COL_INFO));
195
46
            char *col_protocol_text = wmem_strdup(pinfo->pool, col_get_text(pinfo->cinfo, COL_PROTOCOL));
196
197
46
            call_dissector(cms_handle, cms_tvb, pinfo, cms_tree);
198
199
            /* Restore Protocol and Info columns */
200
46
            col_set_str(pinfo->cinfo, COL_INFO, col_info_text);
201
46
            col_set_str(pinfo->cinfo, COL_PROTOCOL, col_protocol_text);
202
46
        }
203
56
    } else {
204
16
        int table_payload_length = tvb_captured_length(tvb) - 4;
205
16
        dissect_lls_table_payload(lls_table_id, tvb, pinfo, offset, table_payload_length, lls_tree);
206
16
    }
207
208
72
    return tvb_captured_length(tvb);
209
72
}
210
211
void
212
proto_register_lls(void)
213
14
{
214
14
    static hf_register_info hf[] = {
215
14
        { &hf_lls_table_id, {
216
14
            "Table ID", "lls.table.id",
217
14
            FT_UINT8, BASE_DEC, VALS(hf_lls_table_type_vals), 0, NULL, HFILL
218
14
        } },
219
14
        { &hf_lls_group_id, {
220
14
            "Group ID", "lls.group.id",
221
14
            FT_UINT8, BASE_DEC, 0, 0, NULL, HFILL
222
14
        } },
223
14
        { &hf_lls_group_count, {
224
14
            "Group Count", "lls.group.count",
225
14
            FT_UINT16, BASE_DEC, 0, 0, NULL, HFILL
226
14
        } },
227
14
        { &hf_lls_table_version, {
228
14
            "Table Version", "lls.table.version",
229
14
            FT_UINT8, BASE_DEC, 0, 0, NULL, HFILL
230
14
        } },
231
14
        { &hf_lls_table_payload, {
232
14
            "Table Payload", "lls.table.payload",
233
14
            FT_NONE, BASE_NONE, 0, 0, NULL, HFILL
234
14
        } },
235
14
        { &hf_lls_table_payload_uncompressed, {
236
14
            "Table Payload Uncompressed", "lls.table.payload.uncompressed",
237
14
            FT_STRING, BASE_NONE, 0, 0, NULL, HFILL
238
14
        } },
239
240
241
14
        { &hf_lls_smt_payload_count, {
242
14
            "Signed Multi Table Payload Count", "lls.smt.payload_count",
243
14
            FT_UINT8, BASE_DEC, 0, 0, NULL, HFILL
244
14
        } },
245
14
        { &hf_lls_smt_entry, {
246
14
            "Signed Multi Table Entry", "lls.smt.entry",
247
14
            FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL
248
14
        } },
249
250
14
        { &hf_lls_smt_entry_payload_length, {
251
14
            "Payload Length", "lls.smt.entry.payload_length",
252
14
            FT_UINT16, BASE_DEC, 0, 0, NULL, HFILL
253
14
        } },
254
255
14
        { &hf_lls_smt_signature_length, {
256
14
            "Signed Multi Table Signature Length", "lls.smt.signature_length",
257
14
            FT_UINT16, BASE_DEC, 0, 0, NULL, HFILL
258
14
        } },
259
14
        { &hf_lls_smt_signature, {
260
14
            "Signed Multi Table Signature", "lls.smt.signature",
261
14
            FT_NONE, BASE_NONE, 0, 0, NULL, HFILL
262
14
        } },
263
14
    };
264
265
14
    static int *ett[] = {
266
14
        &ett_lls,
267
14
        &ett_lls_smt_entry,
268
14
        &ett_lls_table_payload,
269
14
        &ett_lls_table_payload_xml,
270
14
        &ett_lls_smt_signature,
271
14
    };
272
273
14
    static ei_register_info ei[] = {
274
14
        { &ei_lls_table_decompression_failed,
275
14
          { "lls.table.decompression.failed", PI_MALFORMED, PI_ERROR,
276
14
            "LLS table payload decompression failed",
277
14
            EXPFILL }
278
14
        },
279
14
    };
280
281
14
    proto_lls = proto_register_protocol("ATSC3 Low Level Signalling", "LLS", "lls");
282
283
14
    expert_module_t *expert_lls = expert_register_protocol(proto_lls);
284
14
    expert_register_field_array(expert_lls, ei, array_length(ei));
285
286
14
    proto_register_field_array(proto_lls, hf, array_length(hf));
287
14
    proto_register_subtree_array(ett, array_length(ett));
288
14
}
289
290
void
291
proto_reg_handoff_lls(void)
292
14
{
293
14
    lls_handle = create_dissector_handle(dissect_lls, proto_lls);
294
14
    xml_handle = find_dissector_add_dependency("xml", proto_lls);
295
14
    cms_handle = find_dissector_add_dependency("cms", proto_lls);
296
14
    dissector_add_uint_with_preference("udp.port", LLS_PORT, lls_handle);
297
298
14
}
299
300
/*
301
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
302
 *
303
 * Local variables:
304
 * c-basic-offset: 4
305
 * tab-width: 8
306
 * indent-tabs-mode: nil
307
 * End:
308
 *
309
 * vi: set shiftwidth=4 tabstop=8 expandtab:
310
 * :indentSize=4:tabSize=8:noTabs=true:
311
 */