/src/wireshark/epan/dissectors/packet-lls-slt.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* packet-lls-slt.c |
2 | | * Routines for ATSC3 LLS(Low Level Signalling) SLT table 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 | | |
17 | | #include <epan/packet.h> |
18 | | #include <epan/proto_data.h> |
19 | | |
20 | | #include <wsutil/inet_addr.h> |
21 | | #include <wsutil/strtoi.h> |
22 | | |
23 | | #include "packet-lls.h" |
24 | | #include "packet-xml.h" |
25 | | |
26 | | |
27 | | /* Saved SLT Table to use it from another protocols (e.g. ALC/LCT) */ |
28 | | wmem_map_t *lls_slt_table; |
29 | | |
30 | | /* Hash functions */ |
31 | | static int |
32 | | lls_slt_key_equal(const void *v, const void *w) |
33 | 0 | { |
34 | 0 | const lls_slt_key_t *v1 = (const lls_slt_key_t *)v; |
35 | 0 | const lls_slt_key_t *v2 = (const lls_slt_key_t *)w; |
36 | 0 | int result; |
37 | 0 | result = (v1->src_ip == v2->src_ip && |
38 | 0 | v1->dst_ip == v2->dst_ip && |
39 | 0 | v1->dst_port == v2->dst_port); |
40 | 0 | return result; |
41 | 0 | } |
42 | | |
43 | | static unsigned |
44 | | lls_slt_key_hash(const void *v) |
45 | 0 | { |
46 | 0 | const lls_slt_key_t *key = (const lls_slt_key_t *)v; |
47 | 0 | unsigned hash_val = key->src_ip ^ key->dst_ip ^ (((uint32_t)(key->dst_port)) << 16); |
48 | 0 | return hash_val; |
49 | 0 | } |
50 | | |
51 | | /* Init hash table */ |
52 | | static void |
53 | 0 | lls_check_init_slt_table(void) { |
54 | 0 | if(lls_slt_table == NULL) { |
55 | 0 | lls_slt_table = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), lls_slt_key_hash, lls_slt_key_equal); |
56 | 0 | } |
57 | 0 | } |
58 | | |
59 | | static char * |
60 | 0 | xml_value_to_gchar(xml_frame_t *xml_frame, wmem_allocator_t *scope) { |
61 | 0 | char *value = NULL; |
62 | 0 | if (xml_frame->value != NULL) { |
63 | 0 | unsigned l = tvb_reported_length(xml_frame->value); |
64 | 0 | value = (char *)wmem_alloc0(scope, l + 1); |
65 | 0 | tvb_memcpy(xml_frame->value, value, 0, l); |
66 | 0 | } |
67 | 0 | return value; |
68 | 0 | } |
69 | | |
70 | | void |
71 | | lls_extract_save_slt_table(packet_info *pinfo, dissector_handle_t xml_handle) |
72 | 1 | { |
73 | | /* Extract data saved by xml */ |
74 | 1 | int proto_xml = dissector_handle_get_protocol_index(xml_handle); |
75 | 1 | xml_frame_t *xml_dissector_frame = (xml_frame_t *)p_get_proto_data(pinfo->pool, pinfo, proto_xml, 0); |
76 | 1 | if (xml_dissector_frame == NULL) { |
77 | 0 | return; |
78 | 0 | } |
79 | | |
80 | | /* Data from XML dissector */ |
81 | | /* Root level, find SLT tag */ |
82 | 1 | xml_frame_t *xml_frame = xml_dissector_frame->first_child; |
83 | 1 | xml_frame_t *xml_frame_slt = NULL; |
84 | 2 | while (xml_frame) { |
85 | 1 | if (xml_frame->type == XML_FRAME_TAG && g_strcmp0("SLT", xml_frame->name_orig_case) == 0) { |
86 | 0 | xml_frame_slt = xml_frame; |
87 | 0 | break; /* SLT tag found */ |
88 | 0 | } |
89 | 1 | xml_frame = xml_frame->next_sibling; |
90 | 1 | } |
91 | | |
92 | 1 | if (xml_frame_slt == NULL) |
93 | 1 | return; |
94 | | |
95 | | /* SLT level*/ |
96 | 0 | xml_frame_t *slt_entry = xml_frame_slt->first_child; |
97 | 0 | while (slt_entry) { |
98 | 0 | if (!(slt_entry->type == XML_FRAME_TAG && g_strcmp0("Service", slt_entry->name_orig_case) == 0)) { |
99 | 0 | slt_entry = slt_entry->next_sibling; |
100 | 0 | continue; |
101 | 0 | } |
102 | | |
103 | | /* Service level */ |
104 | 0 | xml_frame_t *service_entry = slt_entry->first_child; |
105 | |
|
106 | 0 | lls_slt_key_t slt_key = {0}; |
107 | 0 | lls_slt_value_t slt_val = {0}; |
108 | 0 | slt_val.major_channel_num = -1; |
109 | 0 | slt_val.minor_channel_num = -1; |
110 | 0 | while (service_entry) { |
111 | 0 | char *value = xml_value_to_gchar(service_entry, pinfo->pool); |
112 | 0 | if (service_entry->type == XML_FRAME_ATTRIB && value != NULL) { |
113 | 0 | if(g_strcmp0("serviceId", service_entry->name_orig_case) == 0) { |
114 | 0 | ws_strtou16(value, NULL, &slt_val.service_id); |
115 | 0 | } else if(g_strcmp0("majorChannelNo", service_entry->name_orig_case) == 0) { |
116 | 0 | ws_strtoi32(value, NULL, &slt_val.major_channel_num); |
117 | 0 | } else if(g_strcmp0("minorChannelNo", service_entry->name_orig_case) == 0) { |
118 | 0 | ws_strtoi32(value, NULL, &slt_val.minor_channel_num); |
119 | 0 | } |
120 | 0 | } |
121 | 0 | wmem_free(pinfo->pool, value); |
122 | |
|
123 | 0 | if (service_entry->type == XML_FRAME_TAG && g_strcmp0("BroadcastSvcSignaling", service_entry->name_orig_case) == 0) { |
124 | | /* Broadcast svc signalling level*/ |
125 | 0 | xml_frame_t *bcast_svc_entry = service_entry->first_child; |
126 | |
|
127 | 0 | while (bcast_svc_entry) { |
128 | 0 | value = xml_value_to_gchar(bcast_svc_entry, pinfo->pool); |
129 | 0 | if (bcast_svc_entry->type == XML_FRAME_ATTRIB && value != NULL) { |
130 | 0 | if (g_strcmp0("slsProtocol", bcast_svc_entry->name_orig_case) == 0) { |
131 | 0 | ws_strtou8(value, NULL, &slt_val.sls_protocol); |
132 | 0 | } else if (g_strcmp0("slsDestinationIpAddress", bcast_svc_entry->name_orig_case) == 0) { |
133 | 0 | ws_inet_pton4(value, &slt_key.dst_ip); |
134 | 0 | } else if (g_strcmp0("slsSourceIpAddress", bcast_svc_entry->name_orig_case) == 0) { |
135 | 0 | ws_inet_pton4(value, &slt_key.src_ip); |
136 | 0 | } else if (g_strcmp0("slsDestinationUdpPort", bcast_svc_entry->name_orig_case) == 0) { |
137 | 0 | ws_strtou16(value, NULL, &slt_key.dst_port); |
138 | 0 | } |
139 | 0 | } |
140 | 0 | wmem_free(pinfo->pool, value); |
141 | 0 | bcast_svc_entry = bcast_svc_entry->next_sibling; |
142 | 0 | } |
143 | 0 | } |
144 | |
|
145 | 0 | service_entry = service_entry->next_sibling; |
146 | 0 | } |
147 | 0 | if (slt_key.dst_ip != 0) { |
148 | | /* Save found service entry to hashmap */ |
149 | 0 | lls_slt_key_t *slt_key_m = wmem_new(wmem_file_scope(), lls_slt_key_t); |
150 | 0 | lls_slt_value_t *slt_val_m = wmem_new(wmem_file_scope(), lls_slt_value_t); |
151 | 0 | *slt_key_m = slt_key; |
152 | 0 | *slt_val_m = slt_val; |
153 | 0 | lls_check_init_slt_table(); |
154 | 0 | wmem_map_insert(lls_slt_table, (void *)slt_key_m, (void *)slt_val_m); |
155 | 0 | } |
156 | 0 | slt_entry = slt_entry->next_sibling; |
157 | 0 | } |
158 | 0 | } |
159 | | |
160 | | static lls_slt_value_t * |
161 | 1.41k | 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 | 1.41k | if (!(pinfo->net_src.type == AT_IPv4)) { |
165 | 1.41k | return NULL; |
166 | 1.41k | } |
167 | | |
168 | | /* No ability to lookup a record */ |
169 | 0 | if (lls_slt_table == NULL) { |
170 | 0 | return NULL; |
171 | 0 | } |
172 | | |
173 | | /* Prepare for lookup in LLS SLT table */ |
174 | 0 | lls_slt_key_t slt_key; |
175 | 0 | slt_key.src_ip = *(uint32_t *)pinfo->net_src.data; |
176 | 0 | slt_key.dst_ip = *(uint32_t *)pinfo->net_dst.data; |
177 | 0 | slt_key.dst_port = (uint16_t)pinfo->destport; |
178 | | |
179 | | /* Try to lookup by src_ip + dst_ip + dst_port */ |
180 | 0 | lls_slt_value_t *slt_val = (lls_slt_value_t *)wmem_map_lookup(lls_slt_table, (const void *)(&slt_key)); |
181 | 0 | 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 | 0 | slt_key.src_ip = 0; /* LLS SLT dissector sets it to 0 if source ip is not specified */ |
184 | 0 | slt_val = (lls_slt_value_t *)wmem_map_lookup(lls_slt_table, (const void *)(&slt_key)); |
185 | 0 | if (slt_val == NULL) { |
186 | | /* Record not found by dst ip + port */ |
187 | 0 | return NULL; |
188 | 0 | } |
189 | 0 | } |
190 | | |
191 | 0 | return slt_val; |
192 | 0 | } |
193 | | |
194 | | /* Heuristics test. Checks if packet is ALC using LLS SLT table */ |
195 | | bool |
196 | | test_alc_over_slt(packet_info *pinfo, tvbuff_t *tvb _U_, int offset _U_, void *data _U_) |
197 | 1.41k | { |
198 | 1.41k | lls_slt_value_t *slt_val = get_lls_slt_val(pinfo); |
199 | 1.41k | if (slt_val == NULL) |
200 | 1.41k | return false; |
201 | | |
202 | 0 | if (slt_val->sls_protocol == 1) { |
203 | | /* slsProtocol=1 is ALC/LCT ROUTE/DASH */ |
204 | 0 | return true; |
205 | 0 | } else { |
206 | | /* ACL/LCT is used only for ROUTE/DASH so return false */ |
207 | 0 | return false; |
208 | 0 | } |
209 | 0 | } |
210 | | |
211 | | /* Returns channel info or NULL if no info in SLT table*/ |
212 | | char * |
213 | | get_slt_channel_info(packet_info *pinfo) |
214 | 0 | { |
215 | 0 | lls_slt_value_t *slt_val = get_lls_slt_val(pinfo); |
216 | 0 | if (slt_val == NULL) |
217 | 0 | return NULL; |
218 | | |
219 | 0 | int32_t major_channel_num = slt_val->major_channel_num; |
220 | 0 | int32_t minor_channel_num = slt_val->minor_channel_num; |
221 | 0 | char *ret; |
222 | 0 | if (major_channel_num > 0 && minor_channel_num > 0) { |
223 | 0 | ret = wmem_strdup_printf(pinfo->pool, "ServiceID: %u Channel: %d.%d", slt_val->service_id, |
224 | 0 | major_channel_num, minor_channel_num); |
225 | 0 | } else { |
226 | 0 | ret = wmem_strdup_printf(pinfo->pool, "ServiceID: %u", slt_val->service_id); |
227 | 0 | } |
228 | |
|
229 | 0 | return ret; |
230 | 0 | } |