/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 | | */ |