Coverage Report

Created: 2025-08-04 07:15

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