2 * Routines for ATSC3 LLS(Low Level Signalling) SLT table dissection
3 * Copyright 2023, Sergey V. Lobanov <sergey@lobanov.in>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
13 * ATSC3 Signaling, Delivery, Synchronization, and Error Protection (A/331)
14 * https://www.atsc.org/atsc-documents/3312017-signaling-delivery-synchronization-error-protection/
17 #include <epan/packet.h>
18 #include <epan/proto_data.h>
20 #include <wsutil/inet_addr.h>
21 #include <wsutil/strtoi.h>
23 #include "packet-lls.h"
24 #include "packet-xml.h"
27 /* Saved SLT Table to use it from another protocols (e.g. ALC/LCT) */
28 wmem_map_t
*lls_slt_table
;
32 lls_slt_key_equal(const void *v
, const void *w
)
34 const lls_slt_key_t
*v1
= (const lls_slt_key_t
*)v
;
35 const lls_slt_key_t
*v2
= (const lls_slt_key_t
*)w
;
37 result
= (v1
->src_ip
== v2
->src_ip
&&
38 v1
->dst_ip
== v2
->dst_ip
&&
39 v1
->dst_port
== v2
->dst_port
);
44 lls_slt_key_hash(const void *v
)
46 const lls_slt_key_t
*key
= (const lls_slt_key_t
*)v
;
47 unsigned hash_val
= key
->src_ip
^ key
->dst_ip
^ (((uint32_t)(key
->dst_port
)) << 16);
53 lls_check_init_slt_table(void) {
54 if(lls_slt_table
== NULL
) {
55 lls_slt_table
= wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), lls_slt_key_hash
, lls_slt_key_equal
);
60 xml_value_to_gchar(xml_frame_t
*xml_frame
, wmem_allocator_t
*scope
) {
62 if (xml_frame
->value
!= NULL
) {
63 unsigned l
= tvb_reported_length(xml_frame
->value
);
64 value
= (char *)wmem_alloc0(scope
, l
+ 1);
65 tvb_memcpy(xml_frame
->value
, value
, 0, l
);
71 lls_extract_save_slt_table(packet_info
*pinfo
, dissector_handle_t xml_handle
)
73 /* Extract data saved by xml */
74 int proto_xml
= dissector_handle_get_protocol_index(xml_handle
);
75 xml_frame_t
*xml_dissector_frame
= (xml_frame_t
*)p_get_proto_data(pinfo
->pool
, pinfo
, proto_xml
, 0);
76 if (xml_dissector_frame
== NULL
) {
80 /* Data from XML dissector */
81 /* Root level, find SLT tag */
82 xml_frame_t
*xml_frame
= xml_dissector_frame
->first_child
;
83 xml_frame_t
*xml_frame_slt
= NULL
;
85 if (xml_frame
->type
== XML_FRAME_TAG
&& g_strcmp0("SLT", xml_frame
->name_orig_case
) == 0) {
86 xml_frame_slt
= xml_frame
;
87 break; /* SLT tag found */
89 xml_frame
= xml_frame
->next_sibling
;
92 if (xml_frame_slt
== NULL
)
96 xml_frame_t
*slt_entry
= xml_frame_slt
->first_child
;
98 if (!(slt_entry
->type
== XML_FRAME_TAG
&& g_strcmp0("Service", slt_entry
->name_orig_case
) == 0)) {
99 slt_entry
= slt_entry
->next_sibling
;
104 xml_frame_t
*service_entry
= slt_entry
->first_child
;
106 lls_slt_key_t slt_key
= {0};
107 lls_slt_value_t slt_val
= {0};
108 slt_val
.major_channel_num
= -1;
109 slt_val
.minor_channel_num
= -1;
110 while (service_entry
) {
111 char *value
= xml_value_to_gchar(service_entry
, pinfo
->pool
);
112 if (service_entry
->type
== XML_FRAME_ATTRIB
&& value
!= NULL
) {
113 if(g_strcmp0("serviceId", service_entry
->name_orig_case
) == 0) {
114 ws_strtou16(value
, NULL
, &slt_val
.service_id
);
115 } else if(g_strcmp0("majorChannelNo", service_entry
->name_orig_case
) == 0) {
116 ws_strtoi32(value
, NULL
, &slt_val
.major_channel_num
);
117 } else if(g_strcmp0("minorChannelNo", service_entry
->name_orig_case
) == 0) {
118 ws_strtoi32(value
, NULL
, &slt_val
.minor_channel_num
);
121 wmem_free(pinfo
->pool
, value
);
123 if (service_entry
->type
== XML_FRAME_TAG
&& g_strcmp0("BroadcastSvcSignaling", service_entry
->name_orig_case
) == 0) {
124 /* Broadcast svc signalling level*/
125 xml_frame_t
*bcast_svc_entry
= service_entry
->first_child
;
127 while (bcast_svc_entry
) {
128 value
= xml_value_to_gchar(bcast_svc_entry
, pinfo
->pool
);
129 if (bcast_svc_entry
->type
== XML_FRAME_ATTRIB
&& value
!= NULL
) {
130 if (g_strcmp0("slsProtocol", bcast_svc_entry
->name_orig_case
) == 0) {
131 ws_strtou8(value
, NULL
, &slt_val
.sls_protocol
);
132 } else if (g_strcmp0("slsDestinationIpAddress", bcast_svc_entry
->name_orig_case
) == 0) {
133 ws_inet_pton4(value
, &slt_key
.dst_ip
);
134 } else if (g_strcmp0("slsSourceIpAddress", bcast_svc_entry
->name_orig_case
) == 0) {
135 ws_inet_pton4(value
, &slt_key
.src_ip
);
136 } else if (g_strcmp0("slsDestinationUdpPort", bcast_svc_entry
->name_orig_case
) == 0) {
137 ws_strtou16(value
, NULL
, &slt_key
.dst_port
);
140 wmem_free(pinfo
->pool
, value
);
141 bcast_svc_entry
= bcast_svc_entry
->next_sibling
;
145 service_entry
= service_entry
->next_sibling
;
147 if (slt_key
.dst_ip
!= 0) {
148 /* Save found service entry to hashmap */
149 lls_slt_key_t
*slt_key_m
= wmem_new(wmem_file_scope(), lls_slt_key_t
);
150 lls_slt_value_t
*slt_val_m
= wmem_new(wmem_file_scope(), lls_slt_value_t
);
151 *slt_key_m
= slt_key
;
152 *slt_val_m
= slt_val
;
153 lls_check_init_slt_table();
154 wmem_map_insert(lls_slt_table
, (void *)slt_key_m
, (void *)slt_val_m
);
156 slt_entry
= slt_entry
->next_sibling
;
160 static lls_slt_value_t
*
161 get_lls_slt_val(packet_info
*pinfo
) {
162 /* This routine is for ATSC3 ALC/LCT packets (ipv4 only protocol by design)
163 so ipv6 is not supported by this test */
164 if (!(pinfo
->net_src
.type
== AT_IPv4
)) {
168 /* No ability to lookup a record */
169 if (lls_slt_table
== NULL
) {
173 /* Prepare for lookup in LLS SLT table */
174 lls_slt_key_t slt_key
;
175 slt_key
.src_ip
= *(uint32_t *)pinfo
->net_src
.data
;
176 slt_key
.dst_ip
= *(uint32_t *)pinfo
->net_dst
.data
;
177 slt_key
.dst_port
= (uint16_t)pinfo
->destport
;
179 /* Try to lookup by src_ip + dst_ip + dst_port */
180 lls_slt_value_t
*slt_val
= (lls_slt_value_t
*)wmem_map_lookup(lls_slt_table
, (const void *)(&slt_key
));
181 if(slt_val
== NULL
) {
182 /* No record in SLT table. src_ip is optional according to A/331 so try to lookup by dst ip + port */
183 slt_key
.src_ip
= 0; /* LLS SLT dissector sets it to 0 if source ip is not specified */
184 slt_val
= (lls_slt_value_t
*)wmem_map_lookup(lls_slt_table
, (const void *)(&slt_key
));
185 if (slt_val
== NULL
) {
186 /* Record not found by dst ip + port */
194 /* Heuristics test. Checks if packet is ALC using LLS SLT table */
196 test_alc_over_slt(packet_info
*pinfo
, tvbuff_t
*tvb _U_
, int offset _U_
, void *data _U_
)
198 lls_slt_value_t
*slt_val
= get_lls_slt_val(pinfo
);
202 if (slt_val
->sls_protocol
== 1) {
203 /* slsProtocol=1 is ALC/LCT ROUTE/DASH */
206 /* ACL/LCT is used only for ROUTE/DASH so return false */
211 /* Returns channel info or NULL if no info in SLT table*/
213 get_slt_channel_info(packet_info
*pinfo
)
215 lls_slt_value_t
*slt_val
= get_lls_slt_val(pinfo
);
219 int32_t major_channel_num
= slt_val
->major_channel_num
;
220 int32_t minor_channel_num
= slt_val
->minor_channel_num
;
222 if (major_channel_num
> 0 && minor_channel_num
> 0) {
223 ret
= wmem_strdup_printf(pinfo
->pool
, "ServiceID: %u Channel: %d.%d", slt_val
->service_id
,
224 major_channel_num
, minor_channel_num
);
226 ret
= wmem_strdup_printf(pinfo
->pool
, "ServiceID: %u", slt_val
->service_id
);