ALC/LCT, LLS/SLT: Add ATSC3 support

This commit is contained in:
Sergey V. Lobanov 2023-06-11 16:59:45 +00:00 committed by AndersBroman
parent 952a3163c2
commit 04257d928a
8 changed files with 542 additions and 17 deletions

View File

@ -1425,6 +1425,7 @@ set(DISSECTOR_SRC
${CMAKE_CURRENT_SOURCE_DIR}/packet-llc.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-lldp.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-llrp.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-lls-slt.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-lls.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-llt.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-lltd.c

View File

@ -0,0 +1,229 @@
/* packet-lls-slt.c
* Routines for ATSC3 LLS(Low Level Signalling) SLT table 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/
*/
#include <epan/packet.h>
#include <epan/proto_data.h>
#include <wsutil/strtoi.h>
#include "packet-lls.h"
#include "packet-xml.h"
/* Saved SLT Table to use it from another protocols (e.g. ALC/LCT) */
wmem_map_t *lls_slt_table = NULL;
/* Hash functions */
static gint
lls_slt_key_equal(gconstpointer v, gconstpointer w)
{
const lls_slt_key_t *v1 = (const lls_slt_key_t *)v;
const lls_slt_key_t *v2 = (const lls_slt_key_t *)w;
gint result;
result = (v1->src_ip == v2->src_ip &&
v1->dst_ip == v2->dst_ip &&
v1->dst_port == v2->dst_port);
return result;
}
static guint
lls_slt_key_hash(gconstpointer v)
{
const lls_slt_key_t *key = (const lls_slt_key_t *)v;
guint hash_val = key->src_ip ^ key->dst_ip ^ (((guint32)(key->dst_port)) << 16);
return hash_val;
}
/* Init hash table */
static void
lls_check_init_slt_table(void) {
if(lls_slt_table == NULL) {
lls_slt_table = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), lls_slt_key_hash, lls_slt_key_equal);
}
}
static gchar *
xml_value_to_gchar(xml_frame_t *xml_frame, wmem_allocator_t *scope) {
gchar *value = NULL;
if (xml_frame->value != NULL) {
guint l = tvb_reported_length(xml_frame->value);
value = (gchar *)wmem_alloc0(scope, l + 1);
tvb_memcpy(xml_frame->value, value, 0, l);
}
return value;
}
void
lls_extract_save_slt_table(packet_info *pinfo, dissector_handle_t xml_handle)
{
/* Extract data saved by xml */
int proto_xml = dissector_handle_get_protocol_index(xml_handle);
xml_frame_t *xml_dissector_frame = (xml_frame_t *)p_get_proto_data(pinfo->pool, pinfo, proto_xml, 0);
if (xml_dissector_frame == NULL) {
return;
}
/* Data from XML dissector */
/* Root level, find SLT tag */
xml_frame_t *xml_frame = xml_dissector_frame->first_child;
xml_frame_t *xml_frame_slt = NULL;
while (xml_frame) {
if (xml_frame->type == XML_FRAME_TAG && g_strcmp0("SLT", xml_frame->name_orig_case) == 0) {
xml_frame_slt = xml_frame;
break; /* SLT tag found */
}
xml_frame = xml_frame->next_sibling;
}
if (xml_frame_slt == NULL)
return;
/* SLT level*/
xml_frame_t *slt_entry = xml_frame_slt->first_child;
while (slt_entry) {
if (!(slt_entry->type == XML_FRAME_TAG && g_strcmp0("Service", slt_entry->name_orig_case) == 0)) {
slt_entry = slt_entry->next_sibling;
continue;
}
/* Service level */
xml_frame_t *service_entry = slt_entry->first_child;
lls_slt_key_t slt_key = {0};
lls_slt_value_t slt_val = {0};
slt_val.major_channel_num = -1;
slt_val.minor_channel_num = -1;
while (service_entry) {
gchar *value = xml_value_to_gchar(service_entry, pinfo->pool);
if (service_entry->type == XML_FRAME_ATTRIB && value != NULL) {
if(g_strcmp0("serviceId", service_entry->name_orig_case) == 0) {
ws_strtou16(value, NULL, &slt_val.service_id);
} else if(g_strcmp0("majorChannelNo", service_entry->name_orig_case) == 0) {
ws_strtoi32(value, NULL, &slt_val.major_channel_num);
} else if(g_strcmp0("minorChannelNo", service_entry->name_orig_case) == 0) {
ws_strtoi32(value, NULL, &slt_val.minor_channel_num);
}
}
wmem_free(pinfo->pool, value);
if (service_entry->type == XML_FRAME_TAG && g_strcmp0("BroadcastSvcSignaling", service_entry->name_orig_case) == 0) {
/* Broadcast svc signalling level*/
xml_frame_t *bcast_svc_entry = service_entry->first_child;
while (bcast_svc_entry) {
value = xml_value_to_gchar(bcast_svc_entry, pinfo->pool);
if (bcast_svc_entry->type == XML_FRAME_ATTRIB && value != NULL) {
if (g_strcmp0("slsProtocol", bcast_svc_entry->name_orig_case) == 0) {
ws_strtou8(value, NULL, &slt_val.sls_protocol);
} else if (g_strcmp0("slsDestinationIpAddress", bcast_svc_entry->name_orig_case) == 0) {
ws_inet_pton4(value, &slt_key.dst_ip);
} else if (g_strcmp0("slsSourceIpAddress", bcast_svc_entry->name_orig_case) == 0) {
ws_inet_pton4(value, &slt_key.src_ip);
} else if (g_strcmp0("slsDestinationUdpPort", bcast_svc_entry->name_orig_case) == 0) {
ws_strtou16(value, NULL, &slt_key.dst_port);
}
}
wmem_free(pinfo->pool, value);
bcast_svc_entry = bcast_svc_entry->next_sibling;
}
}
service_entry = service_entry->next_sibling;
}
if (slt_key.dst_ip != 0) {
/* Save found service entry to hashmap */
lls_slt_key_t *slt_key_m = wmem_new(wmem_file_scope(), lls_slt_key_t);
lls_slt_value_t *slt_val_m = wmem_new(wmem_file_scope(), lls_slt_value_t);
*slt_key_m = slt_key;
*slt_val_m = slt_val;
lls_check_init_slt_table();
wmem_map_insert(lls_slt_table, (void *)slt_key_m, (void *)slt_val_m);
}
slt_entry = slt_entry->next_sibling;
}
}
static lls_slt_value_t *
get_lls_slt_val(packet_info *pinfo) {
/* This routine is for ATSC3 ALC/LCT packets (ipv4 only protocol by design)
so ipv6 is not supported by this test */
if (!(pinfo->net_src.type == AT_IPv4)) {
return NULL;
}
/* No ability to lookup a record */
if (lls_slt_table == NULL) {
return NULL;
}
/* Prepare for lookup in LLS SLT table */
lls_slt_key_t slt_key;
slt_key.src_ip = *(guint32 *)pinfo->net_src.data;
slt_key.dst_ip = *(guint32 *)pinfo->net_dst.data;
slt_key.dst_port = (guint16)pinfo->destport;
/* Try to lookup by src_ip + dst_ip + dst_port */
lls_slt_value_t *slt_val = (lls_slt_value_t *)wmem_map_lookup(lls_slt_table, (const void *)(&slt_key));
if(slt_val == NULL) {
/* No record in SLT table. src_ip is optional according to A/331 so try to lookup by dst ip + port */
slt_key.src_ip = 0; /* LLS SLT dissector sets it to 0 if source ip is not specified */
slt_val = (lls_slt_value_t *)wmem_map_lookup(lls_slt_table, (const void *)(&slt_key));
if (slt_val == NULL) {
/* Record not found by dst ip + port */
return NULL;
}
}
return slt_val;
}
/* Heuristics test. Checks if packet is ALC using LLS SLT table */
gboolean
test_alc_over_slt(packet_info *pinfo, tvbuff_t *tvb _U_, int offset _U_, void *data _U_)
{
lls_slt_value_t *slt_val = get_lls_slt_val(pinfo);
if (slt_val == NULL)
return FALSE;
if (slt_val->sls_protocol == 1) {
/* slsProtocol=1 is ALC/LCT ROUTE/DASH */
return TRUE;
} else {
/* ACL/LCT is used only for ROUTE/DASH so return false */
return FALSE;
}
}
/* Returns channel info or NULL if no info in SLT table*/
gchar *
get_slt_channel_info(packet_info *pinfo)
{
lls_slt_value_t *slt_val = get_lls_slt_val(pinfo);
if (slt_val == NULL)
return NULL;
gint32 major_channel_num = slt_val->major_channel_num;
gint32 minor_channel_num = slt_val->minor_channel_num;
gchar *ret;
if (major_channel_num > 0 && minor_channel_num > 0) {
ret = wmem_strdup_printf(pinfo->pool, "ServiceID: %u Channel: %d.%d", slt_val->service_id,
major_channel_num, minor_channel_num);
} else {
ret = wmem_strdup_printf(pinfo->pool, "ServiceID: %u", slt_val->service_id);
}
return ret;
}

View File

@ -25,6 +25,8 @@
#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);
@ -45,6 +47,7 @@ static expert_field ei_lls_table_decompression_failed = EI_INIT;
static int hf_lls_table_id = -1;
#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)" },
@ -101,6 +104,7 @@ dissect_lls_table_payload(guint8 lls_table_id, tvbuff_t *tvb, packet_info *pinfo
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);
@ -111,12 +115,16 @@ dissect_lls_table_payload(guint8 lls_table_id, tvbuff_t *tvb, packet_info *pinfo
proto_item_set_generated(ti_uncomp);
if (xml_handle) {
proto_tree *xml_tree = proto_item_add_subtree(ti_uncomp, ett_lls_table_payload_xml);
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);
}
}

View File

@ -0,0 +1,36 @@
/* packet-lls.h
* 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
*/
#include <epan/packet.h>
#ifndef __PACKET_LLS_H__
#define __PACKET_LLS_H__
typedef struct {
guint32 src_ip;
guint32 dst_ip;
guint16 dst_port;
} lls_slt_key_t;
typedef struct {
guint8 service_category;
guint8 sls_protocol;
guint16 service_id;
gint32 major_channel_num;
gint32 minor_channel_num;
} lls_slt_value_t;
/* SLT Table Routines */
void lls_extract_save_slt_table(packet_info *pinfo, dissector_handle_t xml_handle);
gboolean test_alc_over_slt(packet_info *pinfo, tvbuff_t *tvb, int offset, void *data);
gchar *get_slt_channel_info(packet_info *pinfo);
#endif

View File

@ -2,6 +2,7 @@
* Reliable Multicast Transport (RMT)
* ALC Protocol Instantiation dissector
* Copyright 2005, Stefano Pettini <spettini@users.sourceforge.net>
* Copyright 2023, Sergey V. Lobanov <sergey@lobanov.in>
*
* Asynchronous Layered Coding (ALC):
* ----------------------------------
@ -29,11 +30,14 @@
#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/expert.h>
#include <epan/conversation.h>
#include "packet-rmt-common.h"
#include "packet-lls.h"
/* Initialize the protocol and registered fields */
/* ============================================= */
static dissector_handle_t alc_handle;
void proto_register_alc(void);
void proto_reg_handoff_alc(void);
@ -41,9 +45,14 @@ void proto_reg_handoff_alc(void);
static int proto_rmt_alc = -1;
static int hf_version = -1;
static int hf_atsc3 = -1;
static int hf_object_start_offset = -1;
static int hf_payload = -1;
static int hf_uncomp_payload = -1;
static int ett_main = -1;
static int ett_uncomp_payload = -1;
static int ett_uncomp_decode = -1;
static expert_field ei_version1_only = EI_INIT;
@ -51,9 +60,57 @@ static dissector_handle_t xml_handle;
static dissector_handle_t rmt_lct_handle;
static dissector_handle_t rmt_fec_handle;
static dissector_table_t media_type_dissector_table;
static gboolean g_codepoint_as_fec_encoding = TRUE;
static gint g_ext_192 = LCT_PREFS_EXT_192_FLUTE;
static gint g_ext_193 = LCT_PREFS_EXT_193_FLUTE;
static gint g_atsc3_mode = LCT_ATSC3_MODE_AUTO;
static void
try_decode_payload(tvbuff_t *tvb, packet_info *pinfo, proto_item *tree)
{
guint32 b03 = tvb_get_guint32(tvb, 0, ENC_BIG_ENDIAN);
/* xml ("<?xm") */
if (b03 == 0x3C3F786D) {
call_dissector(xml_handle, tvb, pinfo, tree);
} else {
guint32 b47 = tvb_get_guint32(tvb, 4, ENC_BIG_ENDIAN);
/* mp4 ("ftyp" or "sidx" or "styp" mp4 box) */
if (b47 == 0x66747970 || b47 == 0x73696478 || b47 == 0x73747970) {
/* MP4 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));
int mp4_dis = dissector_try_string(media_type_dissector_table, "video/mp4", tvb, pinfo, tree, NULL);
gchar *col_protocol_text_mp4 = wmem_strdup(pinfo->pool,col_get_text(pinfo->cinfo, COL_PROTOCOL));
/* Restore Protocol and Info columns and add MP4 Protocol Info */
col_set_str(pinfo->cinfo, COL_INFO, col_info_text);
col_set_str(pinfo->cinfo, COL_PROTOCOL, col_protocol_text);
if (mp4_dis > 0) {
col_append_sep_str(pinfo->cinfo, COL_PROTOCOL, "/", col_protocol_text_mp4);
}
}
}
}
static void
try_uncompress(tvbuff_t *tvb, packet_info *pinfo, gint offset, /*gint len,*/ proto_item *ti)
{
tvbuff_t *uncompress_tvb = tvb_uncompress(tvb, offset, tvb_captured_length(tvb) - offset);
if (uncompress_tvb) {
add_new_data_source(pinfo, uncompress_tvb, "Uncompressed Payload");
proto_tree *uncompress_tree = proto_item_add_subtree(ti, ett_uncomp_payload);
guint decomp_length = tvb_captured_length(uncompress_tvb);
proto_item *ti_uncomp = proto_tree_add_item(uncompress_tree, hf_uncomp_payload, uncompress_tvb, 0, decomp_length, ENC_ASCII);
proto_item_set_generated(ti_uncomp);
proto_tree *payload_tree = proto_item_add_subtree(ti_uncomp, ett_uncomp_decode);
try_decode_payload(uncompress_tvb, pinfo, payload_tree);
}
}
/* Code to actually dissect the packets */
/* ==================================== */
@ -64,6 +121,16 @@ dissect_alc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
lct_data_exchange_t lct;
fec_data_exchange_t fec;
int len;
gboolean is_atsc3;
if (g_atsc3_mode == LCT_ATSC3_MODE_FORCE) {
is_atsc3 = TRUE;
} else if (g_atsc3_mode == LCT_ATSC3_MODE_DISABLED) {
is_atsc3 = FALSE;
} else { /* Auto detect mode*/
/* If packet encap is ALP then it is necessary to use ATSC decoding mode*/
is_atsc3 = pinfo->rec->rec_header.packet_header.pkt_encap == WTAP_ENCAP_ATSC_ALP;
}
/* Offset for subpacket dissection */
guint offset = 0;
@ -89,6 +156,9 @@ dissect_alc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
/* Fill the ALC subtree */
ti = proto_tree_add_uint(alc_tree, hf_version, tvb, offset, 1, version);
PROTO_ITEM_SET_GENERATED(
proto_tree_add_boolean(alc_tree, hf_atsc3, 0, 0, 0, is_atsc3)
);
/* This dissector supports only ALCv1 packets.
* If version > 1 print only version field and quit.
@ -109,6 +179,8 @@ dissect_alc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
lct.ext_193 = g_ext_193;
lct.codepoint = 0;
lct.is_flute = FALSE;
lct.is_atsc3 = is_atsc3;
lct.is_sp = FALSE;
len = call_dissector_with_data(rmt_lct_handle, new_tvb, pinfo, alc_tree, &lct);
if (len < 0)
return offset;
@ -120,7 +192,7 @@ dissect_alc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
/* Only if LCT dissector has determined FEC Encoding ID */
/* FEC dissector needs to be called with encoding_id filled */
if (g_codepoint_as_fec_encoding && tvb_reported_length(tvb) > offset)
if (!lct.is_sp && g_codepoint_as_fec_encoding && tvb_reported_length(tvb) > offset)
{
fec.encoding_id = lct.codepoint;
@ -132,19 +204,60 @@ dissect_alc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
offset += len;
}
/* A/331 specifies start_offset field */
gint64 object_start_offset = -1;
if (lct.is_sp) {
object_start_offset = tvb_get_guint32(tvb, offset, 4);
proto_tree_add_item(alc_tree, hf_object_start_offset, tvb, offset, 4, ENC_BIG_ENDIAN);
offset += 4;
}
/* Add the Payload item */
if (tvb_reported_length(tvb) > offset){
if(lct.is_flute){
new_tvb = tvb_new_subset_remaining(tvb,offset);
call_dissector(xml_handle, new_tvb, pinfo, alc_tree);
}else{
proto_tree_add_item(alc_tree, hf_payload, tvb, offset, -1, ENC_NA);
ti = proto_tree_add_item(alc_tree, hf_payload, tvb, offset, -1, ENC_NA);
if (object_start_offset == 0 &&
tvb_captured_length_remaining(tvb, offset) > 18 &&
tvb_get_guint16(tvb, offset, ENC_BIG_ENDIAN) == 0x1f8b) {
/* gzip is detected */
try_uncompress(tvb, pinfo, offset, ti);
} else if (object_start_offset == 0) {
/* gzip is not detected */
new_tvb = tvb_new_subset_remaining(tvb, offset);
try_decode_payload(new_tvb, pinfo, alc_tree);
}
}
}
/* Add Channel info in ATSC3 mode */
if(lct.is_atsc3) {
gchar *channel_info = get_slt_channel_info(pinfo);
if (channel_info != NULL) {
col_append_sep_str(pinfo->cinfo, COL_INFO, " ", channel_info);
wmem_free(pinfo->pool, channel_info);
}
}
return tvb_reported_length(tvb);
}
static gboolean
dissect_alc_heur_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
/* Lookup over ATSC3 SLT Table*/
if (!test_alc_over_slt(pinfo, tvb, 0, data))
return FALSE;
conversation_t *conversation = find_or_create_conversation(pinfo);
conversation_set_dissector(conversation, alc_handle);
return (dissect_alc(tvb, pinfo, tree, data) != 0);
}
void proto_register_alc(void)
{
/* Setup ALC header fields */
@ -153,13 +266,24 @@ void proto_register_alc(void)
{ &hf_version,
{ "Version", "alc.version", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_atsc3,
{ "Decode as ATSC3", "alc.atsc3", FT_BOOLEAN, 0, NULL, 0x0, NULL, HFILL }},
{ &hf_object_start_offset,
{ "Object Start Offset", "alc.object_start_offset", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_payload,
{ "Payload", "alc.payload", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}
{ "Payload", "alc.payload", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_uncomp_payload,
{ "Uncompressed Payload", "alc.payload.uncompressed", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
};
/* Setup protocol subtree array */
static gint *ett_ptr[] = {
&ett_main,
&ett_uncomp_payload,
&ett_uncomp_decode,
};
static ei_register_info ei[] = {
@ -205,17 +329,27 @@ void proto_register_alc(void)
&g_ext_193,
enum_lct_ext_193,
FALSE);
prefs_register_enum_preference(module,
"lct.atsc3.mode",
"ATSC3 Mode",
"How to detect ATSC3 data",
&g_atsc3_mode,
enum_lct_atsc3_mode,
FALSE);
}
void proto_reg_handoff_alc(void)
{
dissector_handle_t handle;
handle = create_dissector_handle(dissect_alc, proto_rmt_alc);
dissector_add_for_decode_as_with_preference("udp.port", handle);
alc_handle = create_dissector_handle(dissect_alc, proto_rmt_alc);
dissector_add_for_decode_as_with_preference("udp.port", alc_handle);
xml_handle = find_dissector_add_dependency("xml", proto_rmt_alc);
rmt_lct_handle = find_dissector_add_dependency("rmt-lct", proto_rmt_alc);
rmt_fec_handle = find_dissector_add_dependency("rmt-fec", proto_rmt_alc);
heur_dissector_add("udp", dissect_alc_heur_udp, "Asynchronous Layered Coding",
"alc", proto_rmt_alc, HEURISTIC_ENABLE);
media_type_dissector_table = find_dissector_table("media_type");
}
/*

View File

@ -23,9 +23,13 @@
#define LCT_PREFS_EXT_193_NONE 0
#define LCT_PREFS_EXT_193_FLUTE 1
#define LCT_ATSC3_MODE_DISABLED 0
#define LCT_ATSC3_MODE_AUTO 1
#define LCT_ATSC3_MODE_FORCE 2
extern const enum_val_t enum_lct_ext_192[];
extern const enum_val_t enum_lct_ext_193[];
extern const enum_val_t enum_lct_atsc3_mode[];
/* String tables external references */
extern const value_string string_fec_encoding_id[];
@ -38,10 +42,12 @@ typedef struct lct_data_exchange
/* inputs */
gint ext_192;
gint ext_193;
gboolean is_atsc3;
/* outputs */
guint8 codepoint;
gboolean is_flute;
gboolean is_sp; /* is Source Packet? Source Packet Indicator is defined in RFC 5775 */
} lct_data_exchange_t;

View File

@ -2,6 +2,7 @@
* Reliable Multicast Transport (RMT)
* LCT Building Block dissector
* Copyright 2005, Stefano Pettini <spettini@users.sourceforge.net>
* Copyright 2023, Sergey V. Lobanov <sergey@lobanov.in>
*
* Layered Coding Transport (LCT):
* -------------------------------
@ -16,6 +17,14 @@
*
* References:
* RFC 3451, Layered Coding Transport (LCT) Building Block
* RFC 5651, Layered Coding Transport (LCT) Building Block
* RFC 5775, Asynchronous Layered Coding (ALC) Protocol Instantiation
*
* ATSC3 Signaling, Delivery, Synchronization, and Error Protection (A/331)
* https://www.atsc.org/atsc-documents/3312017-signaling-delivery-synchronization-error-protection/
*
* IANA Layered Coding Transport (LCT) Header Extension Types
* https://www.iana.org/assignments/lct-header-extensions/lct-header-extensions.txt
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
@ -36,12 +45,16 @@
#define LCT_ERT_FLAG 0x0004
#define LCT_CLOSE_SESSION_FLAG 0x0002
#define LCT_CLOSE_OBJECT_FLAG 0x0001
#define LCT_PSI 0x0300
#define LCT_PSI_MSB 0x0200
void proto_register_rmt_lct(void);
static int proto_rmt_lct = -1;
static int hf_version = -1;
static int hf_psi = -1;
static int hf_spi = -1;
static int hf_fsize_header = -1;
static int hf_fsize_cci = -1;
static int hf_fsize_tsi = -1;
@ -53,6 +66,7 @@ static int hf_flags_close_session = -1;
static int hf_flags_close_object = -1;
static int hf_hlen = -1;
static int hf_codepoint = -1;
static int hf_codepoint_atsc3 = -1;
static int hf_cci = -1;
static int hf_tsi16 = -1;
static int hf_tsi32 = -1;
@ -72,6 +86,8 @@ static int hf_send_rate = -1;
static int hf_cenc = -1;
static int hf_flute_version = -1;
static int hf_fdt_instance_id = -1;
static int hf_ext_tol_48_transfer_len = -1;
static int hf_ext_tol_24_transfer_len = -1;
/* Generated from convert_proto_tree_add_text.pl */
static int hf_cc_rate = -1;
static int hf_cc_rtt = -1;
@ -84,6 +100,7 @@ static int ett_fsize = -1;
static int ett_flags = -1;
static int ett_ext = -1;
static int ett_ext_ext = -1;
static int ett_psi = -1;
/* Enumerated data types for LCT preferences */
const enum_val_t enum_lct_ext_192[] =
@ -100,14 +117,40 @@ const enum_val_t enum_lct_ext_193[] =
{ NULL, NULL, 0 }
};
const enum_val_t enum_lct_atsc3_mode[] =
{
{ "disabled", "Do not decode as ATSC3 data", LCT_ATSC3_MODE_DISABLED },
{ "auto", "Auto Detect (if encap is ALP)", LCT_ATSC3_MODE_AUTO },
{ "force", "Force to decode as ATSC3 data", LCT_ATSC3_MODE_FORCE },
{ NULL, NULL, 0 }
};
static const value_string hec_type_vals[] = {
{ 0, "EXT_NOP, No-Operation" },
{ 1, "EXT_AUTH, Packet authentication" },
{ 2, "EXT_CC, Congestion Control Feedback" },
{ 2, "EXT_TIME" },
{ 64, "EXT_FTI, FEC Object Transmission Information" },
{ 65, "DVB-IPTV CDS Completion Poll Request LCT" },
{ 66, "EXT_ROUTE_PRESENTATION_TIME" },
{ 67, "EXT_TOL, Transport Object Length (48-bit version)" },
{ 128, "EXT_RATE, Send Rate" },
{ 192, "EXT_FDT, FDT Instance Header" },
{ 193, "EXT_CENC, FDT Instance Content Encoding" },
{ 194, "EXT_TOL, Transport Object Length (24-bit version)" },
{ 0, NULL }
};
static const value_string cp_type_vals[] = {
{ 1, "NRT, File Mode" },
{ 2, "NRT, Entity Mode" },
{ 3, "NRT, Unsigned Package Mode" },
{ 4, "NRT, Signed Package Mode" },
{ 5, "New IS, timeline changed" },
{ 6, "New IS, timeline continued" },
{ 7, "Redundant IS" },
{ 8, "Media Segment, File Mode" },
{ 9, "Media Segment, Entity Mode" },
{ 0, NULL }
};
@ -209,7 +252,12 @@ int lct_ext_decode(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, guint of
case 64: /* EXT_FTI */
fec_decode_ext_fti(tvb, pinfo, ext_tree, offset,
(data_exchange == NULL) ? 0 : data_exchange->codepoint);
(data_exchange == NULL) ? 0 :
data_exchange->is_sp ? 0 :data_exchange->codepoint);
break;
case 67: /* EXT_TOL_48 */
proto_tree_add_item(ext_tree, hf_ext_tol_48_transfer_len, tvb, offset+1, 6, ENC_BIG_ENDIAN);
break;
case 128: /* EXT_RATE */
@ -232,6 +280,10 @@ int lct_ext_decode(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, guint of
proto_tree_add_item(ext_tree, hf_cenc, tvb, offset+3, 1, ENC_BIG_ENDIAN);
}
break;
case 194: /* EXT_TOL_24 */
proto_tree_add_item(ext_tree, hf_ext_tol_24_transfer_len, tvb, offset+1, 3, ENC_BIG_ENDIAN);
break;
}
offset += length;
@ -327,14 +379,28 @@ dissect_lct(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
/* LCT version number (4 bits) */
proto_tree_add_item(lct_tree, hf_version, tvb, offset, 2, ENC_BIG_ENDIAN);
ti = proto_tree_add_item(lct_tree, hf_psi, tvb, offset, 2, ENC_BIG_ENDIAN);
if (data_exchange->is_atsc3) {
guint16 psi_msb = tvb_get_guint16(tvb, offset, ENC_BIG_ENDIAN) & LCT_PSI_MSB;
data_exchange->is_sp = !!psi_msb;
proto_tree *lct_psi_tree = proto_item_add_subtree(ti, ett_psi);
proto_tree_add_item(lct_psi_tree, hf_spi, tvb, offset, 2, ENC_BIG_ENDIAN);
}
ti = proto_tree_add_item(lct_tree, hf_fsize_header, tvb, offset, 2, ENC_BIG_ENDIAN);
lct_fsize_tree = proto_item_add_subtree(ti, ett_fsize);
/* Fill the LCT fsize subtree */
proto_tree_add_uint(lct_fsize_tree, hf_fsize_cci, tvb, offset, 2, cci_size);
proto_tree_add_uint(lct_fsize_tree, hf_fsize_tsi, tvb, offset, 2, tsi_size);
proto_tree_add_uint(lct_fsize_tree, hf_fsize_toi, tvb, offset, 2, toi_size);
PROTO_ITEM_SET_GENERATED(
proto_tree_add_uint(lct_fsize_tree, hf_fsize_cci, tvb, offset, 1, cci_size)
);
PROTO_ITEM_SET_GENERATED(
proto_tree_add_uint(lct_fsize_tree, hf_fsize_tsi, tvb, offset, 2, tsi_size)
);
PROTO_ITEM_SET_GENERATED(
proto_tree_add_uint(lct_fsize_tree, hf_fsize_toi, tvb, offset, 2, toi_size)
);
ti = proto_tree_add_item(lct_tree, hf_flags_header, tvb, offset, 2, ENC_BIG_ENDIAN);
lct_flags_tree = proto_item_add_subtree(ti, ett_flags);
@ -346,7 +412,16 @@ dissect_lct(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
proto_tree_add_item(lct_flags_tree, hf_flags_close_object, tvb, offset, 2, ENC_BIG_ENDIAN);
proto_tree_add_uint(lct_tree, hf_hlen, tvb, offset+2, 1, hlen);
proto_tree_add_item(lct_tree, hf_codepoint, tvb, offset+3, 1, ENC_BIG_ENDIAN);
if (data_exchange->is_atsc3) {
if(data_exchange->codepoint < 128) {
proto_tree_add_item(lct_tree, hf_codepoint_atsc3, tvb, offset+3, 1, ENC_BIG_ENDIAN);
} else {
proto_tree_add_uint_format_value(lct_tree, hf_codepoint_atsc3, tvb, offset+3, 1,
data_exchange->codepoint, "Defined by SLS (%u)", data_exchange->codepoint);
}
} else {
proto_tree_add_item(lct_tree, hf_codepoint, tvb, offset+3, 1, ENC_BIG_ENDIAN);
}
}
@ -445,6 +520,16 @@ dissect_lct(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
if (buffer16 & LCT_CLOSE_OBJECT_FLAG)
col_append_sep_str(pinfo->cinfo, COL_INFO, " ", "Close object");
if (data_exchange->is_atsc3) {
if (data_exchange->is_sp) {
/* According to A/331:2022-11 A.3.4 Usage of ALC and LCT */
col_append_sep_str(pinfo->cinfo, COL_INFO, " ", "(Source)");
} else {
/* According to A/331:2022-11 A.4.2.4 Repair Packet Structure */
col_append_sep_str(pinfo->cinfo, COL_INFO, " ", " (Repair)");
}
}
/* Sender Current Time (SCT) */
if (buffer16 & LCT_SCT_FLAG) {
lct_timestamp_parse(tvb_get_ntohl(tvb, offset), &tmp_time);
@ -475,9 +560,19 @@ proto_register_rmt_lct(void)
FT_UINT16, BASE_DEC, NULL, 0xF000,
NULL, HFILL }
},
{ &hf_psi,
{ "Protocol-Specific Indication", "rmt-lct.psi",
FT_UINT16, BASE_HEX, NULL, LCT_PSI,
NULL, HFILL }
},
{ &hf_spi,
{ "Source Packet Indicator", "rmt-lct.spi",
FT_BOOLEAN, 16, NULL, LCT_PSI_MSB,
NULL, HFILL }
},
{ &hf_fsize_header,
{ "Field size flags", "rmt-lct.fsize",
FT_UINT16, BASE_HEX, NULL, 0x0FD0,
FT_UINT16, BASE_HEX, NULL, 0x0FC0,
NULL, HFILL }
},
{ &hf_fsize_cci,
@ -530,6 +625,11 @@ proto_register_rmt_lct(void)
FT_UINT8, BASE_DEC, NULL, 0x0,
NULL, HFILL }
},
{ &hf_codepoint_atsc3,
{ "Codepoint", "rmt-lct.codepoint",
FT_UINT8, BASE_DEC, VALS(cp_type_vals), 0x0,
NULL, HFILL }
},
{ &hf_cci,
{ "Congestion Control Information", "rmt-lct.cci",
FT_BYTES, BASE_NONE, NULL, 0x0,
@ -625,6 +725,16 @@ proto_register_rmt_lct(void)
FT_UINT32, BASE_DEC, NULL, 0x000FFFFF,
NULL, HFILL }
},
{ &hf_ext_tol_48_transfer_len,
{ "EXT_TOL_48 Tranfer Length", "rmt-lct.ext_tol_tranfer_len",
FT_UINT48, BASE_DEC, NULL, 0,
NULL, HFILL }
},
{ &hf_ext_tol_24_transfer_len,
{ "EXT_TOL_24 Tranfer Length", "rmt-lct.ext_tol_tranfer_len",
FT_UINT24, BASE_DEC, NULL, 0,
NULL, HFILL }
},
{ &hf_cc_sequence,
{ "CC Sequence", "rmt-lct.cc_sequence",
FT_UINT16, BASE_DEC, NULL, 0x0,
@ -658,7 +768,8 @@ proto_register_rmt_lct(void)
&ett_fsize,
&ett_flags,
&ett_ext,
&ett_ext_ext
&ett_ext_ext,
&ett_psi,
};
/* Register the protocol name and description */

View File

@ -375,7 +375,7 @@ dissect_xml(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
while(tvbparse_get(tt, want)) ;
/* Save XML structure in case it is useful for the caller (only XMPP for now) */
/* Save XML structure in case it is useful for the caller */
p_add_proto_data(pinfo->pool, pinfo, xml_ns.hf_tag, 0, current_frame);
return tvb_captured_length(tvb);