Coverage Report

Created: 2026-07-12 07:10

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
15
#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
89
#define LLS_TABLE_TYPE_SIGNED_MULTI_TABLE 0xFE
49
68
#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
35
{
97
35
    proto_item *ti = proto_tree_add_item(tree, hf_lls_table_payload, tvb, offset, len, ENC_NA);
98
99
35
    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
34
    proto_tree *uncompress_tree = proto_item_add_subtree(ti, ett_lls_table_payload);
105
34
    tvbuff_t *uncompress_tvb = tvb_child_uncompress_zlib(tvb, tvb, offset, len);
106
34
    proto_tree *xml_tree = NULL;
107
34
    if (uncompress_tvb) {
108
10
        const char *table_type_short = val_to_str_const(lls_table_id, hf_lls_table_type_short_vals, "Unknown");
109
10
        char *source_name = wmem_strdup_printf(pinfo->pool, "Table ID %u (%s)", lls_table_id, table_type_short);
110
10
        add_new_data_source(pinfo, uncompress_tvb, source_name);
111
10
        unsigned decomp_length = tvb_captured_length(uncompress_tvb);
112
113
10
        proto_item *ti_uncomp = proto_tree_add_item(uncompress_tree, hf_lls_table_payload_uncompressed, uncompress_tvb, 0, decomp_length, ENC_ASCII);
114
10
        proto_item_set_generated(ti_uncomp);
115
116
10
        if (xml_handle) {
117
10
            xml_tree = proto_item_add_subtree(ti_uncomp, ett_lls_table_payload_xml);
118
10
            call_dissector(xml_handle, uncompress_tvb, pinfo, xml_tree);
119
10
        }
120
24
    } else {
121
24
        expert_add_info(pinfo, ti, &ei_lls_table_decompression_failed);
122
24
    }
123
124
34
    if (lls_table_id == LLS_TABLE_TYPE_SLT && xml_tree != NULL) {
125
0
        lls_extract_save_slt_table(pinfo, xml_handle);
126
0
    }
127
34
}
128
129
130
static int
131
dissect_lls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
132
54
{
133
54
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "LLS");
134
135
54
    proto_item *ti = proto_tree_add_item(tree, proto_lls, tvb, 0, -1, ENC_NA);
136
54
    proto_tree *lls_tree = proto_item_add_subtree(ti, ett_lls);
137
138
54
    unsigned offset = 0;
139
140
54
    uint8_t lls_table_id = tvb_get_uint8(tvb, offset);
141
54
    col_set_str(pinfo->cinfo, COL_INFO, val_to_str_const(lls_table_id, hf_lls_table_type_vals, "Unknown"));
142
54
    proto_tree_add_item(lls_tree, hf_lls_table_id, tvb, offset, 1, ENC_BIG_ENDIAN);
143
54
    offset++;
144
145
54
    proto_tree_add_item(lls_tree, hf_lls_group_id, tvb, offset, 1, ENC_BIG_ENDIAN);
146
54
    offset++;
147
148
54
    uint16_t lls_group_count = tvb_get_uint8(tvb, offset) + 1;
149
54
    PROTO_ITEM_SET_GENERATED(
150
54
        proto_tree_add_uint(lls_tree, hf_lls_group_count, tvb, offset, 1, lls_group_count)
151
54
    );
152
54
    offset++;
153
154
54
    proto_tree_add_item(lls_tree, hf_lls_table_version, tvb, offset, 1, ENC_BIG_ENDIAN);
155
54
    offset++;
156
157
54
    if (lls_table_id == LLS_TABLE_TYPE_SIGNED_MULTI_TABLE) {
158
43
        uint8_t smt_payload_count = tvb_get_uint8(tvb, offset);
159
43
        proto_tree_add_item(lls_tree, hf_lls_smt_payload_count, tvb, offset, 1, ENC_BIG_ENDIAN);
160
43
        offset++;
161
162
67
        for(uint8_t i = 0; i < smt_payload_count; i++) {
163
24
            uint16_t smt_entry_payload_length = tvb_get_uint16(tvb, offset + 2, ENC_BIG_ENDIAN);
164
24
            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
24
            proto_tree *smt_entry_tree = proto_item_add_subtree(smt_entry_item, ett_lls_smt_entry);
166
167
24
            uint8_t smt_entry_table_id = tvb_get_uint8(tvb, offset);
168
24
            const char *table_type_short = val_to_str_const(smt_entry_table_id, hf_lls_table_type_short_vals, "Unknown");
169
24
            proto_item_append_text(smt_entry_item, " (%u) Table ID=%u (%s)", i, smt_entry_table_id, table_type_short);
170
24
            col_append_fstr(pinfo->cinfo, COL_INFO, "/%s", table_type_short);
171
24
            proto_tree_add_item(smt_entry_tree, hf_lls_table_id, tvb, offset, 1, ENC_BIG_ENDIAN);
172
24
            offset++;
173
174
24
            proto_tree_add_item(smt_entry_tree, hf_lls_table_version, tvb, offset, 1, ENC_BIG_ENDIAN);
175
24
            offset++;
176
177
24
            proto_tree_add_item(smt_entry_tree, hf_lls_smt_entry_payload_length, tvb, offset, 2, ENC_BIG_ENDIAN);
178
24
            offset += 2;
179
180
24
            dissect_lls_table_payload(smt_entry_table_id, tvb, pinfo, offset, smt_entry_payload_length, smt_entry_tree);
181
24
            offset += smt_entry_payload_length;
182
24
        }
183
184
43
        uint16_t smt_signature_length = tvb_get_uint16(tvb, offset, ENC_BIG_ENDIAN);
185
43
        proto_tree_add_item(lls_tree, hf_lls_smt_signature_length, tvb, offset, 2, ENC_BIG_ENDIAN);
186
43
        offset += 2;
187
188
43
        proto_item *smt_signature_item = proto_tree_add_item(lls_tree, hf_lls_smt_signature, tvb, offset, smt_signature_length, ENC_NA);
189
43
        if (cms_handle) {
190
38
            proto_tree *cms_tree = proto_item_add_subtree(smt_signature_item, ett_lls_smt_signature);
191
38
            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
38
            char *col_info_text = wmem_strdup(pinfo->pool, col_get_text(pinfo->cinfo, COL_INFO));
195
38
            char *col_protocol_text = wmem_strdup(pinfo->pool, col_get_text(pinfo->cinfo, COL_PROTOCOL));
196
197
38
            call_dissector(cms_handle, cms_tvb, pinfo, cms_tree);
198
199
            /* Restore Protocol and Info columns */
200
38
            col_set_str(pinfo->cinfo, COL_INFO, col_info_text);
201
38
            col_set_str(pinfo->cinfo, COL_PROTOCOL, col_protocol_text);
202
38
        }
203
43
    } else {
204
11
        int table_payload_length = tvb_captured_length(tvb) - 4;
205
11
        dissect_lls_table_payload(lls_table_id, tvb, pinfo, offset, table_payload_length, lls_tree);
206
11
    }
207
208
54
    return tvb_captured_length(tvb);
209
54
}
210
211
void
212
proto_register_lls(void)
213
15
{
214
15
    static hf_register_info hf[] = {
215
15
        { &hf_lls_table_id, {
216
15
            "Table ID", "lls.table.id",
217
15
            FT_UINT8, BASE_DEC, VALS(hf_lls_table_type_vals), 0, NULL, HFILL
218
15
        } },
219
15
        { &hf_lls_group_id, {
220
15
            "Group ID", "lls.group.id",
221
15
            FT_UINT8, BASE_DEC, 0, 0, NULL, HFILL
222
15
        } },
223
15
        { &hf_lls_group_count, {
224
15
            "Group Count", "lls.group.count",
225
15
            FT_UINT16, BASE_DEC, 0, 0, NULL, HFILL
226
15
        } },
227
15
        { &hf_lls_table_version, {
228
15
            "Table Version", "lls.table.version",
229
15
            FT_UINT8, BASE_DEC, 0, 0, NULL, HFILL
230
15
        } },
231
15
        { &hf_lls_table_payload, {
232
15
            "Table Payload", "lls.table.payload",
233
15
            FT_NONE, BASE_NONE, 0, 0, NULL, HFILL
234
15
        } },
235
15
        { &hf_lls_table_payload_uncompressed, {
236
15
            "Table Payload Uncompressed", "lls.table.payload.uncompressed",
237
15
            FT_STRING, BASE_NONE, 0, 0, NULL, HFILL
238
15
        } },
239
240
241
15
        { &hf_lls_smt_payload_count, {
242
15
            "Signed Multi Table Payload Count", "lls.smt.payload_count",
243
15
            FT_UINT8, BASE_DEC, 0, 0, NULL, HFILL
244
15
        } },
245
15
        { &hf_lls_smt_entry, {
246
15
            "Signed Multi Table Entry", "lls.smt.entry",
247
15
            FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL
248
15
        } },
249
250
15
        { &hf_lls_smt_entry_payload_length, {
251
15
            "Payload Length", "lls.smt.entry.payload_length",
252
15
            FT_UINT16, BASE_DEC, 0, 0, NULL, HFILL
253
15
        } },
254
255
15
        { &hf_lls_smt_signature_length, {
256
15
            "Signed Multi Table Signature Length", "lls.smt.signature_length",
257
15
            FT_UINT16, BASE_DEC, 0, 0, NULL, HFILL
258
15
        } },
259
15
        { &hf_lls_smt_signature, {
260
15
            "Signed Multi Table Signature", "lls.smt.signature",
261
15
            FT_NONE, BASE_NONE, 0, 0, NULL, HFILL
262
15
        } },
263
15
    };
264
265
15
    static int *ett[] = {
266
15
        &ett_lls,
267
15
        &ett_lls_smt_entry,
268
15
        &ett_lls_table_payload,
269
15
        &ett_lls_table_payload_xml,
270
15
        &ett_lls_smt_signature,
271
15
    };
272
273
15
    static ei_register_info ei[] = {
274
15
        { &ei_lls_table_decompression_failed,
275
15
          { "lls.table.decompression.failed", PI_MALFORMED, PI_ERROR,
276
15
            "LLS table payload decompression failed",
277
15
            EXPFILL }
278
15
        },
279
15
    };
280
281
15
    proto_lls = proto_register_protocol("ATSC3 Low Level Signalling", "LLS", "lls");
282
283
15
    expert_module_t *expert_lls = expert_register_protocol(proto_lls);
284
15
    expert_register_field_array(expert_lls, ei, array_length(ei));
285
286
15
    proto_register_field_array(proto_lls, hf, array_length(hf));
287
15
    proto_register_subtree_array(ett, array_length(ett));
288
15
}
289
290
void
291
proto_reg_handoff_lls(void)
292
15
{
293
15
    lls_handle = create_dissector_handle(dissect_lls, proto_lls);
294
15
    xml_handle = find_dissector_add_dependency("xml", proto_lls);
295
15
    cms_handle = find_dissector_add_dependency("cms", proto_lls);
296
15
    dissector_add_uint_with_preference("udp.port", LLS_PORT, lls_handle);
297
298
15
}
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
 */