312 lines
11 KiB
C
312 lines
11 KiB
C
/* packet-lls.c
|
|
* Routines for ATSC3 LLS(Low Level Signalling) dissection
|
|
* Copyright 2023, Sergey V. Lobanov <sergey@lobanov.in>
|
|
*
|
|
* Wireshark - Network traffic analyzer
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
* Copyright 1998 Gerald Combs
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
/*
|
|
* ATSC3 Signaling, Delivery, Synchronization, and Error Protection (A/331)
|
|
* https://www.atsc.org/atsc-documents/3312017-signaling-delivery-synchronization-error-protection/
|
|
*
|
|
* ATSC3 Security and Service Protection (A/360)
|
|
* https://www.atsc.org/atsc-documents/3602018-atsc-3-0-security-service-protection/
|
|
*
|
|
* ATSC Code Point Registry
|
|
* https://www.atsc.org/documents/code-point-registry/
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <epan/expert.h>
|
|
#include <epan/packet.h>
|
|
|
|
#include "packet-lls.h"
|
|
|
|
#define LLS_PORT 4937 // IANA Registered (atsc-mh-ssc)
|
|
|
|
void proto_reg_handoff_lls(void);
|
|
void proto_register_lls(void);
|
|
|
|
static int proto_lls;
|
|
static gint ett_lls;
|
|
static gint ett_lls_smt_entry;
|
|
static gint ett_lls_smt_signature;
|
|
static gint ett_lls_table_payload;
|
|
static gint ett_lls_table_payload_xml;
|
|
|
|
static dissector_handle_t lls_handle;
|
|
static dissector_handle_t xml_handle;
|
|
static dissector_handle_t cms_handle;
|
|
|
|
static expert_field ei_lls_table_decompression_failed;
|
|
|
|
static int hf_lls_table_id;
|
|
#define LLS_TABLE_TYPE_SIGNED_MULTI_TABLE 0xFE
|
|
#define LLS_TABLE_TYPE_SLT 0x01
|
|
static const value_string hf_lls_table_type_vals[] = {
|
|
{ 0x01, "SLT (Service List Table)" },
|
|
{ 0x02, "RRT (Rating Region Table)" },
|
|
{ 0x03, "System Time" },
|
|
{ 0x04, "AEAT (Advanced Emergency Information Table)" },
|
|
{ 0x05, "On Screen Message Notification" },
|
|
{ 0x06, "CDT (Certification Data Table)" },
|
|
{ 0x07, "DRCT (Dedicated Return Channel Table)" },
|
|
{ 0x80, "VIT (Version Information Table)" },
|
|
{ 0x81, "CPT (Content Protection Table)" },
|
|
{ 0x82, "CAP (Common Alerting Protocol)" },
|
|
{ 0xFE, "Signed Multi Table" },
|
|
{ 0xFF, "User Defined" },
|
|
{ 0x00, NULL }
|
|
};
|
|
static const value_string hf_lls_table_type_short_vals[] = {
|
|
{ 0x01, "SLT" },
|
|
{ 0x02, "RRT" },
|
|
{ 0x03, "ST" },
|
|
{ 0x04, "AEAT" },
|
|
{ 0x05, "OSMN" },
|
|
{ 0x06, "CDT" },
|
|
{ 0x07, "DRCT" },
|
|
{ 0x80, "VIT" },
|
|
{ 0x81, "CPT" },
|
|
{ 0x82, "CAP" },
|
|
{ 0xFE, "SMT" },
|
|
{ 0xFF, "USD" },
|
|
{ 0x00, NULL }
|
|
};
|
|
|
|
static int hf_lls_group_id;
|
|
static int hf_lls_group_count;
|
|
static int hf_lls_table_version;
|
|
static int hf_lls_table_payload;
|
|
static int hf_lls_table_payload_uncompressed;
|
|
|
|
static int hf_lls_smt_payload_count;
|
|
static int hf_lls_smt_entry;
|
|
static int hf_lls_smt_entry_payload_length;
|
|
static int hf_lls_smt_signature_length;
|
|
static int hf_lls_smt_signature;
|
|
|
|
|
|
static void
|
|
dissect_lls_table_payload(guint8 lls_table_id, tvbuff_t *tvb, packet_info *pinfo, gint offset, gint len, proto_tree *tree)
|
|
{
|
|
proto_item *ti = proto_tree_add_item(tree, hf_lls_table_payload, tvb, offset, len, ENC_NA);
|
|
|
|
if (lls_table_id == LLS_TABLE_TYPE_SIGNED_MULTI_TABLE) {
|
|
/* Nested SignedMultiTable decoding is not specified in the standard */
|
|
return;
|
|
}
|
|
|
|
proto_tree *uncompress_tree = proto_item_add_subtree(ti, ett_lls_table_payload);
|
|
tvbuff_t *uncompress_tvb = tvb_uncompress(tvb, offset, len);
|
|
proto_tree *xml_tree = NULL;
|
|
if (uncompress_tvb) {
|
|
const gchar *table_type_short = val_to_str_const(lls_table_id, hf_lls_table_type_short_vals, "Unknown");
|
|
gchar *source_name = wmem_strdup_printf(pinfo->pool, "Table ID %u (%s)", lls_table_id, table_type_short);
|
|
add_new_data_source(pinfo, uncompress_tvb, source_name);
|
|
guint decomp_length = tvb_captured_length(uncompress_tvb);
|
|
|
|
proto_item *ti_uncomp = proto_tree_add_item(uncompress_tree, hf_lls_table_payload_uncompressed, uncompress_tvb, 0, decomp_length, ENC_ASCII);
|
|
proto_item_set_generated(ti_uncomp);
|
|
|
|
if (xml_handle) {
|
|
xml_tree = proto_item_add_subtree(ti_uncomp, ett_lls_table_payload_xml);
|
|
call_dissector(xml_handle, uncompress_tvb, pinfo, xml_tree);
|
|
}
|
|
} else {
|
|
expert_add_info(pinfo, ti, &ei_lls_table_decompression_failed);
|
|
}
|
|
|
|
if (lls_table_id == LLS_TABLE_TYPE_SLT && xml_tree != NULL) {
|
|
lls_extract_save_slt_table(pinfo, xml_handle);
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
dissect_lls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
|
|
{
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "LLS");
|
|
|
|
proto_item *ti = proto_tree_add_item(tree, proto_lls, tvb, 0, -1, ENC_NA);
|
|
proto_tree *lls_tree = proto_item_add_subtree(ti, ett_lls);
|
|
|
|
gint offset = 0;
|
|
|
|
guint8 lls_table_id = tvb_get_guint8(tvb, offset);
|
|
col_set_str(pinfo->cinfo, COL_INFO, val_to_str_const(lls_table_id, hf_lls_table_type_vals, "Unknown"));
|
|
proto_tree_add_item(lls_tree, hf_lls_table_id, tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
offset++;
|
|
|
|
proto_tree_add_item(lls_tree, hf_lls_group_id, tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
offset++;
|
|
|
|
guint16 lls_group_count = tvb_get_guint8(tvb, offset) + 1;
|
|
PROTO_ITEM_SET_GENERATED(
|
|
proto_tree_add_uint(lls_tree, hf_lls_group_count, tvb, offset, 1, lls_group_count)
|
|
);
|
|
offset++;
|
|
|
|
proto_tree_add_item(lls_tree, hf_lls_table_version, tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
offset++;
|
|
|
|
if (lls_table_id == LLS_TABLE_TYPE_SIGNED_MULTI_TABLE) {
|
|
guint8 smt_payload_count = tvb_get_guint8(tvb, offset);
|
|
proto_tree_add_item(lls_tree, hf_lls_smt_payload_count, tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
offset++;
|
|
|
|
for(guint8 i = 0; i < smt_payload_count; i++) {
|
|
guint16 smt_entry_payload_length = tvb_get_guint16(tvb, offset + 2, ENC_BIG_ENDIAN);
|
|
proto_item *smt_entry_item = proto_tree_add_item(lls_tree, hf_lls_smt_entry, tvb, offset, smt_entry_payload_length + 4, ENC_NA);
|
|
proto_tree *smt_entry_tree = proto_item_add_subtree(smt_entry_item, ett_lls_smt_entry);
|
|
|
|
guint8 smt_entry_table_id = tvb_get_guint8(tvb, offset);
|
|
const gchar *table_type_short = val_to_str_const(smt_entry_table_id, hf_lls_table_type_short_vals, "Unknown");
|
|
proto_item_append_text(smt_entry_item, " (%u) Table ID=%u (%s)", i, smt_entry_table_id, table_type_short);
|
|
col_append_fstr(pinfo->cinfo, COL_INFO, "/%s", table_type_short);
|
|
proto_tree_add_item(smt_entry_tree, hf_lls_table_id, tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
offset++;
|
|
|
|
proto_tree_add_item(smt_entry_tree, hf_lls_table_version, tvb, offset, 1, ENC_BIG_ENDIAN);
|
|
offset++;
|
|
|
|
proto_tree_add_item(smt_entry_tree, hf_lls_smt_entry_payload_length, tvb, offset, 2, ENC_BIG_ENDIAN);
|
|
offset += 2;
|
|
|
|
dissect_lls_table_payload(smt_entry_table_id, tvb, pinfo, offset, smt_entry_payload_length, smt_entry_tree);
|
|
offset += smt_entry_payload_length;
|
|
}
|
|
|
|
guint16 smt_signature_length = tvb_get_guint16(tvb, offset, ENC_BIG_ENDIAN);
|
|
proto_tree_add_item(lls_tree, hf_lls_smt_signature_length, tvb, offset, 2, ENC_BIG_ENDIAN);
|
|
offset += 2;
|
|
|
|
proto_item *smt_signature_item = proto_tree_add_item(lls_tree, hf_lls_smt_signature, tvb, offset, smt_signature_length, ENC_NA);
|
|
if (cms_handle) {
|
|
proto_tree *cms_tree = proto_item_add_subtree(smt_signature_item, ett_lls_smt_signature);
|
|
tvbuff_t *cms_tvb = tvb_new_subset_length(tvb, offset, smt_signature_length);
|
|
|
|
/* CMS dissector removes useful info from Protocol and Info columns so store it */
|
|
gchar *col_info_text = wmem_strdup(pinfo->pool, col_get_text(pinfo->cinfo, COL_INFO));
|
|
gchar *col_protocol_text = wmem_strdup(pinfo->pool, col_get_text(pinfo->cinfo, COL_PROTOCOL));
|
|
|
|
call_dissector(cms_handle, cms_tvb, pinfo, cms_tree);
|
|
|
|
/* Restore Protocol and Info columns */
|
|
col_set_str(pinfo->cinfo, COL_INFO, col_info_text);
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, col_protocol_text);
|
|
}
|
|
} else {
|
|
gint table_payload_length = tvb_captured_length(tvb) - 4;
|
|
dissect_lls_table_payload(lls_table_id, tvb, pinfo, offset, table_payload_length, lls_tree);
|
|
}
|
|
|
|
return tvb_captured_length(tvb);
|
|
}
|
|
|
|
void
|
|
proto_register_lls(void)
|
|
{
|
|
static hf_register_info hf[] = {
|
|
{ &hf_lls_table_id, {
|
|
"Table ID", "lls.table.id",
|
|
FT_UINT8, BASE_DEC, VALS(hf_lls_table_type_vals), 0, NULL, HFILL
|
|
} },
|
|
{ &hf_lls_group_id, {
|
|
"Group ID", "lls.group.id",
|
|
FT_UINT8, BASE_DEC, 0, 0, NULL, HFILL
|
|
} },
|
|
{ &hf_lls_group_count, {
|
|
"Group Count", "lls.group.count",
|
|
FT_UINT16, BASE_DEC, 0, 0, NULL, HFILL
|
|
} },
|
|
{ &hf_lls_table_version, {
|
|
"Table Version", "lls.table.version",
|
|
FT_UINT8, BASE_DEC, 0, 0, NULL, HFILL
|
|
} },
|
|
{ &hf_lls_table_payload, {
|
|
"Table Payload", "lls.table.payload",
|
|
FT_NONE, BASE_NONE, 0, 0, NULL, HFILL
|
|
} },
|
|
{ &hf_lls_table_payload_uncompressed, {
|
|
"Table Payload Uncompressed", "lls.table.payload.uncompressed",
|
|
FT_STRING, BASE_NONE, 0, 0, NULL, HFILL
|
|
} },
|
|
|
|
|
|
{ &hf_lls_smt_payload_count, {
|
|
"Signed Multi Table Payload Count", "lls.smt.payload_count",
|
|
FT_UINT8, BASE_DEC, 0, 0, NULL, HFILL
|
|
} },
|
|
{ &hf_lls_smt_entry, {
|
|
"Signed Multi Table Entry", "lls.smt.entry",
|
|
FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL
|
|
} },
|
|
|
|
{ &hf_lls_smt_entry_payload_length, {
|
|
"Payload Length", "lls.smt.entry.payload_length",
|
|
FT_UINT16, BASE_DEC, 0, 0, NULL, HFILL
|
|
} },
|
|
|
|
{ &hf_lls_smt_signature_length, {
|
|
"Signed Multi Table Signature Length", "lls.smt.signature_length",
|
|
FT_UINT16, BASE_DEC, 0, 0, NULL, HFILL
|
|
} },
|
|
{ &hf_lls_smt_signature, {
|
|
"Signed Multi Table Signature", "lls.smt.signature",
|
|
FT_NONE, BASE_NONE, 0, 0, NULL, HFILL
|
|
} },
|
|
};
|
|
|
|
static gint *ett[] = {
|
|
&ett_lls,
|
|
&ett_lls_smt_entry,
|
|
&ett_lls_table_payload,
|
|
&ett_lls_table_payload_xml,
|
|
&ett_lls_smt_signature,
|
|
};
|
|
|
|
static ei_register_info ei[] = {
|
|
{ &ei_lls_table_decompression_failed,
|
|
{ "lls.table.decompression.failed", PI_MALFORMED, PI_ERROR,
|
|
"LLS table payload decompression failed",
|
|
EXPFILL }
|
|
},
|
|
};
|
|
|
|
proto_lls = proto_register_protocol("ATSC3 Low Level Signalling", "LLS", "lls");
|
|
|
|
expert_module_t *expert_lls = expert_register_protocol(proto_lls);
|
|
expert_register_field_array(expert_lls, ei, array_length(ei));
|
|
|
|
proto_register_field_array(proto_lls, hf, array_length(hf));
|
|
proto_register_subtree_array(ett, array_length(ett));
|
|
}
|
|
|
|
void
|
|
proto_reg_handoff_lls(void)
|
|
{
|
|
lls_handle = create_dissector_handle(dissect_lls, proto_lls);
|
|
xml_handle = find_dissector_add_dependency("xml", proto_lls);
|
|
cms_handle = find_dissector_add_dependency("cms", proto_lls);
|
|
dissector_add_uint_with_preference("udp.port", LLS_PORT, lls_handle);
|
|
|
|
}
|
|
|
|
/*
|
|
* Editor modelines - https://www.wireshark.org/tools/modelines.html
|
|
*
|
|
* Local variables:
|
|
* c-basic-offset: 4
|
|
* tab-width: 8
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*
|
|
* vi: set shiftwidth=4 tabstop=8 expandtab:
|
|
* :indentSize=4:tabSize=8:noTabs=true:
|
|
*/
|