Coverage Report

Created: 2026-07-12 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-cesoeth.c
Line
Count
Source
1
/* packet-cesoeth.c
2
 * Dissection of Circuit Emulation Service over Ethernet (MEF 8)
3
 * www.mplify.net
4
 *
5
 * Copyright 2018, AimValley B.V.
6
 * Jaap Keuter <jaap.keuter@aimvalley.com>
7
 *
8
 * Wireshark - Network traffic analyzer
9
 * By Gerald Combs <gerald@wireshark.org>
10
 * Copyright 1998 Gerald Combs
11
 *
12
 * SPDX-License-Identifier: GPL-2.0-or-later
13
 */
14
15
#include "config.h"
16
17
#include <epan/packet.h>
18
#include <epan/prefs.h>
19
#include <epan/decode_as.h>
20
#include <epan/proto_data.h>
21
#include <epan/expert.h>
22
#include <epan/etypes.h>
23
#include "packet-rtp.h"
24
25
void proto_register_cesoeth(void);
26
void proto_reg_handoff_cesoeth(void);
27
28
static dissector_handle_t cesoeth_handle;
29
30
static dissector_table_t cesoeth_dissector_table;
31
32
static int proto_cesoeth;
33
static int hf_cesoeth_pw_ecid;
34
static int hf_cesoeth_pw_res;
35
static int hf_cesoeth_cw;
36
static int hf_cesoeth_cw_reserved1;
37
static int hf_cesoeth_cw_l;
38
static int hf_cesoeth_cw_r;
39
static int hf_cesoeth_cw_l0_m;
40
static int hf_cesoeth_cw_l1_m;
41
static int hf_cesoeth_cw_frg;
42
static int hf_cesoeth_cw_len;
43
static int hf_cesoeth_cw_seq;
44
static int hf_cesoeth_padding;
45
46
static int ett_cesoeth;
47
static int ett_cesoeth_cw;
48
49
static expert_field ei_cesoeth_reserved;
50
static expert_field ei_cesoeth_length;
51
52
static int* const cesoeth_l0_cw[] =
53
{
54
    &hf_cesoeth_cw_reserved1,
55
    &hf_cesoeth_cw_l,
56
    &hf_cesoeth_cw_r,
57
    &hf_cesoeth_cw_l0_m,
58
    &hf_cesoeth_cw_frg,
59
    &hf_cesoeth_cw_len,
60
    &hf_cesoeth_cw_seq,
61
    NULL
62
};
63
64
static int* const cesoeth_l1_cw[] =
65
{
66
    &hf_cesoeth_cw_reserved1,
67
    &hf_cesoeth_cw_l,
68
    &hf_cesoeth_cw_r,
69
    &hf_cesoeth_cw_l1_m,
70
    &hf_cesoeth_cw_frg,
71
    &hf_cesoeth_cw_len,
72
    &hf_cesoeth_cw_seq,
73
    NULL
74
};
75
76
static const value_string frg_names[] =
77
{
78
    { 0, "No fragmentation" },
79
    { 1, "First fragment" },
80
    { 2, "Last fragment" },
81
    { 3, "Intermediate fragment" },
82
    { 0, NULL }
83
};
84
85
static const value_string l0_m_names[] =
86
{
87
    { 0, "No local TDM defect" },
88
    { 1, "Reserved" },
89
    { 2, "RDI on TDM input" },
90
    { 3, "Non-TDM data" },
91
    { 0, NULL }
92
};
93
94
static const value_string l1_m_names[] =
95
{
96
    { 0, "TDM defect" },
97
    { 1, "Reserved" },
98
    { 2, "Reserved" },
99
    { 3, "Reserved" },
100
    { 0, NULL }
101
};
102
103
/* Preferences */
104
static bool has_rtp_header;
105
static bool heuristic_rtp_header = true;
106
107
static void cesoeth_ecid_prompt(packet_info *pinfo, char* result)
108
0
{
109
0
    uint32_t *ecid = (uint32_t *)p_get_proto_data(pinfo->pool, pinfo, proto_cesoeth, pinfo->curr_layer_num);
110
0
    snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "ECID 0x%05x as", *ecid);
111
0
}
112
113
static void *cesoeth_ecid_value(packet_info *pinfo)
114
0
{
115
0
    uint32_t *ecid = (uint32_t *)p_get_proto_data(pinfo->pool, pinfo, proto_cesoeth, pinfo->curr_layer_num);
116
0
    return GUINT_TO_POINTER(*ecid);
117
0
}
118
119
120
static int
121
dissect_cesoeth(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
122
36
{
123
36
    proto_tree  *cesoeth_tree;
124
36
    proto_item  *cesoeth_ti;
125
36
    proto_item  *bitmask_ti;
126
36
    int         offset = 0;
127
36
    uint32_t    *ecid = wmem_new(pinfo->pool, uint32_t);
128
36
    uint32_t    reserved;
129
36
    bool        l_bit, r_bit;
130
36
    uint8_t     m_bits, frg;
131
36
    int         cw_len, padding_len, tail_len, payload_len;
132
36
    uint16_t    sn;
133
36
    tvbuff_t    *next_tvb;
134
135
36
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "CESoETH");
136
36
    col_clear(pinfo->cinfo, COL_INFO);
137
138
36
    cesoeth_ti = proto_tree_add_item(tree, proto_cesoeth, tvb, 0, -1, ENC_NA);
139
36
    cesoeth_tree = proto_item_add_subtree(cesoeth_ti, ett_cesoeth);
140
141
36
    proto_tree_add_item_ret_uint(cesoeth_tree, hf_cesoeth_pw_ecid, tvb, offset, 4, ENC_BIG_ENDIAN, ecid);
142
36
    col_append_fstr(pinfo->cinfo, COL_INFO, "ECID: 0x%05x", *ecid);
143
36
    p_add_proto_data(pinfo->pool, pinfo, proto_cesoeth, pinfo->curr_layer_num, ecid);
144
36
    bitmask_ti = proto_tree_add_item_ret_uint(cesoeth_tree, hf_cesoeth_pw_res, tvb, offset, 4, ENC_BIG_ENDIAN, &reserved);
145
36
    if (reserved != 0x102)
146
36
        expert_add_info_format(pinfo, bitmask_ti, &ei_cesoeth_reserved, "Reserved field must be 0x102");
147
36
    offset += 4;
148
149
36
    next_tvb = tvb_new_subset_length(tvb, offset, tvb_reported_length_remaining(tvb, offset));
150
36
    payload_len = dissector_try_uint(cesoeth_dissector_table, *ecid, next_tvb, pinfo, tree);
151
36
    if (payload_len)
152
0
        return payload_len;
153
154
    /*
155
     * CES header control word
156
     *
157
     * bits  name       description
158
     * 31-28 reserved   set to 0
159
     *  27   L-bit      set to 1 to indicate local TDM failure
160
     *  26   R-bit      set to 1 to indicate remote loss of frame
161
     * 25-24 M-bits     modifier bits
162
     * 23-22 FRG bits   fragmentation bits
163
     * 21-16 length     length (0 if no padding applied)
164
     * 15-0  sequence   sequence number
165
     */
166
167
36
    l_bit  = (tvb_get_uint8(tvb, offset) & 0x08) ? true : false;
168
36
    r_bit  = (tvb_get_uint8(tvb, offset) & 0x04) ? true : false;
169
36
    m_bits = (tvb_get_uint8(tvb, offset) & 0x03);
170
36
    frg    = tvb_get_bits8(tvb, 40, 2);
171
36
    cw_len = tvb_get_bits8(tvb, 42, 6);
172
36
    sn     = tvb_get_ntohs(tvb, offset + 2);
173
174
36
    col_append_fstr(pinfo->cinfo, COL_INFO, ", SN: %u", sn);
175
176
36
    if (l_bit)
177
18
    {
178
18
        bitmask_ti = proto_tree_add_bitmask(cesoeth_tree, tvb, offset, hf_cesoeth_cw, ett_cesoeth_cw, cesoeth_l1_cw, ENC_BIG_ENDIAN);
179
18
        col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "%s", val_to_str_const(m_bits, l1_m_names, "Unknown"));
180
18
    } else {
181
18
        bitmask_ti = proto_tree_add_bitmask(cesoeth_tree, tvb, offset, hf_cesoeth_cw, ett_cesoeth_cw, cesoeth_l0_cw, ENC_BIG_ENDIAN);
182
18
        if (m_bits)
183
6
            col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "%s", val_to_str_const(m_bits, l0_m_names, "Unknown"));
184
18
    }
185
186
36
    if (cw_len >= 42)
187
28
    {
188
        /*
189
         * Now we have to go spelunking in the bitmask tree for the length item
190
         * in order to add an expert item to it.
191
         */
192
28
        proto_tree *bm_tree = proto_item_get_subtree(bitmask_ti);
193
28
        if (bm_tree) {
194
28
            proto_item *pi;
195
196
766
            for (pi = tree->first_child; pi; pi = pi->next)
197
738
            {
198
738
                field_info *fi;
199
738
                fi = PITEM_FINFO(pi);
200
738
                if (fi && (fi->hfinfo->id == hf_cesoeth_cw_len))
201
0
                    break;
202
738
            }
203
204
28
            expert_add_info_format(pinfo, pi, &ei_cesoeth_length, "Length can not be 42 or larger");
205
28
        }
206
207
28
        cw_len = 0; /* Put a stop to this madness */
208
28
    }
209
210
36
    if (r_bit)
211
9
        col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "Remote loss of frame");
212
213
36
    if (frg)
214
13
        col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "%s", val_to_str_const(frg, frg_names, "Unknown"));
215
216
36
    offset += 4;
217
218
    /*
219
     * When L is set the TDM payload MAY be missing.
220
     * But also when snap value is used on capture.
221
     * Will the optional RTP header be there when the TDM payload is missing? I guess so.
222
     * Length includes the size of the CW (being 4), the optional RTP header (being 12) and
223
     * the TDM payload, as long as it doesn't exceed 42 octets. Length > 0 indicates padding,
224
     * which must NOT be passed to the RTP dissector, it's not RTP padding.
225
     */
226
227
36
    padding_len = (cw_len > 0) ? (42 - cw_len) : 0;
228
36
    tail_len = tvb_reported_length_remaining(tvb, offset);
229
36
    payload_len = tail_len - padding_len;
230
231
36
    if (payload_len > 0)
232
35
    {
233
35
        next_tvb = tvb_new_subset_length(tvb, offset, payload_len);
234
235
35
        if ((has_rtp_header) ||
236
35
            ((heuristic_rtp_header) &&
237
                /* Check for RTP version 2, the other fields must be zero */
238
35
                (tvb_get_uint8(tvb, offset) == 0x80) &&
239
                /* Check the marker is zero. Unfortunately PT is not always from the dynamic range */
240
1
                ((tvb_get_uint8(tvb, offset + 1) & 0x80) == 0) &&
241
                /* The sequence numbers from cw and RTP header must match */
242
1
                (tvb_get_ntohs(tvb, offset + 2) == sn)))
243
0
        {
244
0
            struct _rtp_info rtp_info;
245
246
0
            int rtp_header_len = dissect_rtp_shim_header(tvb, offset, pinfo, cesoeth_tree, &rtp_info);
247
248
0
            if (rtp_header_len > 0)
249
0
            {
250
0
                col_set_str(pinfo->cinfo, COL_PROTOCOL, "CESoETH (w RTP)");
251
0
                col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "RTP PT: %u, SSRC: 0x%X, Seq: %u, Time=%u",
252
0
                                    rtp_info.info_payload_type,
253
0
                                    rtp_info.info_sync_src,
254
0
                                    rtp_info.info_seq_num,
255
0
                                    rtp_info.info_timestamp
256
0
                                );
257
258
0
                next_tvb = tvb_new_subset_length(tvb, offset + rtp_header_len, payload_len - rtp_header_len);
259
0
            }
260
0
            else
261
0
            {
262
0
                col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "RTP header missing");
263
0
            }
264
0
        }
265
266
35
        call_data_dissector(next_tvb, pinfo, tree);
267
268
35
        offset += payload_len;
269
35
    }
270
271
36
    if (padding_len > 0)
272
6
    {
273
6
        proto_tree_add_item(cesoeth_tree, hf_cesoeth_padding, tvb, offset, padding_len, ENC_NA);
274
275
6
        offset += padding_len;
276
6
    }
277
278
36
    return offset;
279
36
}
280
281
void
282
proto_register_cesoeth(void)
283
15
{
284
15
    static hf_register_info hf[] = {
285
15
        { &hf_cesoeth_pw_ecid,
286
15
            { "ECID", "cesoeth.ecid", FT_UINT32, BASE_HEX,
287
15
            NULL, 0xFFFFF000, NULL, HFILL }},
288
15
        { &hf_cesoeth_pw_res,
289
15
            { "Reserved", "cesoeth.res", FT_UINT32, BASE_HEX,
290
15
            NULL, 0x00000FFF, "Reserved (0x102)", HFILL }},
291
292
15
        { &hf_cesoeth_cw,
293
15
            { "Control word", "cesoeth.cw", FT_UINT32, BASE_HEX,
294
15
            NULL, 0x0, NULL, HFILL }},
295
296
15
        { &hf_cesoeth_cw_reserved1,
297
15
            { "Reserved", "cesoeth.cw.reserved", FT_UINT32, BASE_HEX,
298
15
            NULL, 0xF0000000, NULL, HFILL }},
299
15
        { &hf_cesoeth_cw_l,
300
15
            { "L-bit", "cesoeth.cw.l", FT_BOOLEAN, 32,
301
15
            NULL, 0x08000000, "Local TDM failure", HFILL }},
302
15
        { &hf_cesoeth_cw_r,
303
15
            { "R-bit", "cesoeth.cw.r", FT_BOOLEAN, 32,
304
15
            NULL, 0x04000000, "Remote Loss of Frames indication", HFILL }},
305
15
        { &hf_cesoeth_cw_l0_m,
306
15
            { "M-bits", "cesoeth.cw.m", FT_UINT32, BASE_HEX,
307
15
            VALS(l0_m_names), 0x03000000, "Modifier bits", HFILL }},
308
15
        { &hf_cesoeth_cw_l1_m,
309
15
            { "M-bits", "cesoeth.cw.m", FT_UINT32, BASE_HEX,
310
15
            VALS(l1_m_names), 0x03000000, "Modifier bits", HFILL }},
311
15
        { &hf_cesoeth_cw_frg,
312
15
            { "Frg", "cesoeth.cw.frg", FT_UINT32, BASE_HEX,
313
15
            VALS(frg_names), 0x00C00000, "Fragmentation bits", HFILL }},
314
15
        { &hf_cesoeth_cw_len,
315
15
            { "Len", "cesoeth.cw.len", FT_UINT32, BASE_DEC,
316
15
            NULL, 0x003F0000, "Length", HFILL }},
317
15
        { &hf_cesoeth_cw_seq,
318
15
            { "SN", "cesoeth.cw.sn", FT_UINT32, BASE_DEC,
319
15
            NULL, 0x0000FFFF, "Sequence number", HFILL }},
320
321
15
        { &hf_cesoeth_padding,
322
15
            { "Padding", "cesoeth.padding", FT_BYTES, BASE_NONE,
323
15
            NULL, 0x0, NULL, HFILL }}
324
15
    };
325
326
15
    static int *ett[] = {
327
15
        &ett_cesoeth,
328
15
        &ett_cesoeth_cw
329
15
    };
330
331
15
    static ei_register_info ei[] = {
332
15
        { &ei_cesoeth_reserved,
333
15
            { "cesoeth.reserved", PI_PROTOCOL, PI_WARN,
334
15
              "Reserved field", EXPFILL }},
335
15
        { &ei_cesoeth_length,
336
15
            { "cesoeth.length", PI_PROTOCOL, PI_WARN,
337
15
              "Length field", EXPFILL }}
338
15
    };
339
340
    /* Decode As handling */
341
15
    static build_valid_func cesoeth_ecid_da_build_value[1] = {cesoeth_ecid_value};
342
15
    static decode_as_value_t cesoeth_ecid_da_values = {cesoeth_ecid_prompt, 1, cesoeth_ecid_da_build_value};
343
15
    static decode_as_t cesoeth_ecid_da = {"cesoeth", "cesoeth.ecid", 1, 0, &cesoeth_ecid_da_values, NULL, NULL,
344
15
                                          decode_as_default_populate_list, decode_as_default_reset, decode_as_default_change,
345
15
                                          NULL, NULL, NULL};
346
347
15
    module_t *cesoeth_module;
348
15
    expert_module_t* expert_cesoeth;
349
350
15
    proto_cesoeth = proto_register_protocol("Circuit Emulation Service over Ethernet", "CESoETH", "cesoeth");
351
15
    proto_register_field_array(proto_cesoeth, hf, array_length(hf));
352
15
    proto_register_subtree_array(ett, array_length(ett));
353
15
    expert_cesoeth = expert_register_protocol(proto_cesoeth);
354
15
    expert_register_field_array(expert_cesoeth, ei, array_length(ei));
355
356
15
    cesoeth_dissector_table = register_dissector_table("cesoeth.ecid", "ECID",
357
15
                                                       proto_cesoeth, FT_UINT24, BASE_HEX);
358
15
    register_decode_as(&cesoeth_ecid_da);
359
360
15
    cesoeth_module = prefs_register_protocol(proto_cesoeth, NULL);
361
362
15
    prefs_register_bool_preference(cesoeth_module, "rtp_header", "RTP header in CES payload",
363
15
                                   "Whether or not the RTP header is present in the CES payload.", &has_rtp_header);
364
365
15
    prefs_register_bool_preference(cesoeth_module, "rtp_header_heuristic", "Try to find RTP header in CES payload",
366
15
                                   "Heuristically determine if an RTP header is present in the CES payload.", &heuristic_rtp_header);
367
368
369
15
    cesoeth_handle = register_dissector("cesoeth", dissect_cesoeth, proto_cesoeth);
370
15
}
371
372
void
373
proto_reg_handoff_cesoeth(void)
374
15
{
375
15
    dissector_add_uint("ethertype", ETHERTYPE_CESOETH, cesoeth_handle);
376
15
}
377
378
/*
379
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
380
 *
381
 * Local variables:
382
 * c-basic-offset: 4
383
 * tab-width: 8
384
 * indent-tabs-mode: nil
385
 * End:
386
 *
387
 * vi: set shiftwidth=4 tabstop=8 expandtab:
388
 * :indentSize=4:tabSize=8:noTabs=true:
389
 */