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-rtps-processed.c
Line
Count
Source
1
/* packet-rtps-processed.c
2
 * Dissector for the Real-Time Publish-Subscribe (RTPS) Processed Protocol.
3
 *
4
 * (c) 2020 Copyright, Real-Time Innovations, Inc.
5
 * Real-Time Innovations, Inc.
6
 * 232 East Java Drive
7
 * Sunnyvale, CA 94089
8
 *
9
 * Wireshark - Network traffic analyzer
10
 * By Gerald Combs <gerald@wireshark.org>
11
 * Copyright 1998 Gerald Combs
12
 *
13
 * SPDX-License-Identifier: GPL-2.0-or-later
14
 *
15
 * -----------------------------------------------------------------------------
16
 * RTI Connext DDS can capture RTPS-related traffic by using the Network Capture
17
 * Utility. The generated .pcap capture files will follow a format that
18
 * defines how information must be saved, and then parsed.
19
 *
20
 * The format is divided into two layers/protocols: virtual transport
21
 * (packet-rtps-virtual-transport.c) and processed (packet-rtps-processed.c).
22
 * This file is about the processed dissector. For a general introduction and
23
 * information about the virtual transport dissector, read the documentation at
24
 * the beginning of packet-rtps-virtual-transport.c.
25
 *
26
 * The processed dissector is called by the transport dissector. It should never
27
 * be called directly by Wireshark without going through the transport
28
 * dissector first.
29
 *
30
 * The advanced information contains one parameter that it is really important
31
 * (and compulsory). This parameter is the "main frame", i.e. the frame that
32
 * would usually be captured over the wire. This frame is encrypted if security
33
 * applies.
34
 *
35
 * Then we have two optional fields: advanced frame0 and frame1.
36
 *   - frame0: Contains the RTPS frame with submessage protection (but
37
 *             decrypted at the RTPS level).
38
 *   - frame1:
39
 *     - Inbound traffic: A list of decrypted RTPS submessages (the protected
40
 *                        ones from frame0).
41
 *     - Outbound traffic: The RTPS message before any kind of protection.
42
 * The contents encrypted at RTPS message level can be found in the main frame.
43
 *
44
 * We can see there is a difference between frame1 (the parameter containing the
45
 * decrypted RTPS submessages): inbound traffic has a list of submessages (no
46
 * RTPS header) but outbound traffic has a RTPS message. The reason behind
47
 * this is related to how RTI Connext DDS handles protected inbound traffic.
48
 *
49
 * An alternative would be to build the RTPS message from frame0 and frame1 and
50
 * then pass it to the RTPS dissector. This solution would be cleaner but would
51
 * require to keep a buffer and information between parameters.
52
 * The current solution is kept for the moment.
53
 */
54
55
#include "config.h"
56
57
#include <epan/packet.h>
58
#include <epan/expert.h>
59
#include <epan/prefs.h>
60
#include <epan/addr_resolv.h>
61
#include <epan/wmem_scopes.h>
62
#include <epan/conversation.h>
63
#include "packet-tcp.h"
64
#include "packet-rtps.h"
65
66
67
0
#define PARAM_ID_ADVANCED_FRAME0               0x000C1
68
0
#define PARAM_ID_ADVANCED_FRAME1               0x000C2
69
70
void proto_reg_handoff_rtps_processed(void);
71
void proto_register_rtps_processed(void);
72
static int dissect_rtps_processed(
73
        tvbuff_t *tvb,
74
        packet_info *pinfo,
75
        proto_tree *tree,
76
        void *data);
77
static void get_new_colinfo_w_submessages(
78
        wmem_strbuf_t *out,
79
        wmem_strbuf_t *frame,
80
        const char *submessages);
81
82
/* Subtree pointers */
83
static int rtpsproc_tree = -1;
84
static int ett_rtpsproc;
85
static int ett_rtpsproc_security;
86
static int ett_rtpsproc_advanced_frame0;
87
static int ett_rtpsproc_advanced_frame1;
88
89
/* Initialize the protocol and registered fields */
90
static header_field_info *rtpsproc_hf;
91
static int hf_rtpsproc_param_id;
92
static int hf_rtpsproc_param_length;
93
94
/* Used for caching a handle to the RTPS dissector */
95
static dissector_handle_t rtps_handle;
96
97
/* ========================================================================== */
98
/*                                 Dissector                                  */
99
/* ========================================================================== */
100
/*
101
 * Parameters must be in the right order or dissector will fail.
102
 * This was done instead of looping for all parameters (like in
103
 * packet-rtps-virtual-transport.c) because:
104
 *   - The number of parameters is small.
105
 *   - This way we can skip creating some headings if they are not needed (by
106
 *     using zeros instead).
107
 */
108
static int dissect_rtps_processed(
109
        tvbuff_t *tvb,
110
        packet_info *pinfo,
111
        proto_tree *tree,
112
        void *data)
113
0
{
114
0
    proto_tree *rtpsproc_tree_general = NULL;
115
0
    proto_tree *rtpsproc_tree_security = NULL;
116
0
    proto_item *rtpsproc_ti = NULL;
117
0
    uint16_t param_id;
118
0
    uint16_t param_length;
119
0
    unsigned offset = 0;
120
0
    int offset_version = 4; /* 'R', 'T', 'P', 'S' */
121
0
    tvbuff_t *rtps_payload = NULL;
122
0
    tvbuff_t *message_payload = NULL;
123
0
    struct rtpsvt_data *transport_data = (struct rtpsvt_data *) data;
124
0
    const char *title_security;
125
0
    uint16_t rtps_version = 0x0203;
126
0
    uint16_t rtps_vendor_id = 0x0101;
127
0
    endpoint_guid guid;
128
129
0
    if (transport_data == NULL) {
130
        /* Reject the packet if no transport information */
131
0
        return 0;
132
0
    }
133
0
    param_length = transport_data->rtps_length;
134
0
    title_security = transport_data->direction == 1
135
0
                        ? "RTPS Security decoding"
136
0
                        : "RTPS Security pre-encoding";
137
138
    /* *****************************  MAIN  ***********************************/
139
    /*
140
     * The contents passed to the rtpsproc dissector must start with the RTPS
141
     * frame.
142
     */
143
0
    rtps_version = tvb_get_uint16(
144
0
            tvb,
145
0
            offset + offset_version,
146
0
            ENC_BIG_ENDIAN);
147
0
    rtps_vendor_id = tvb_get_uint16(
148
0
            tvb,
149
0
            offset + offset_version + 2,
150
0
            ENC_BIG_ENDIAN);
151
0
    guid.host_id = tvb_get_ntohl(tvb, offset + offset_version + 4);
152
0
    guid.app_id = tvb_get_ntohl(tvb, offset + offset_version + 8);
153
0
    guid.instance_id = tvb_get_ntohl(tvb, offset + offset_version + 12);
154
0
    guid.fields_present = GUID_HAS_HOST_ID | GUID_HAS_APP_ID | GUID_HAS_INSTANCE_ID;
155
0
    rtps_payload = tvb_new_subset_length(tvb, offset, param_length);
156
0
    if (rtps_handle != NULL) {
157
0
        call_dissector(rtps_handle, rtps_payload, pinfo, tree);
158
0
    }
159
0
    offset += param_length;
160
161
    /* *********** Add subtree used for the fields of our rtpsproc_tree *******/
162
0
    rtpsproc_ti = proto_tree_add_item(
163
0
            tree,
164
0
            rtpsproc_tree,
165
0
            tvb,
166
0
            offset,
167
0
            -1,
168
0
            ENC_BIG_ENDIAN);
169
0
    rtpsproc_tree_general = proto_item_add_subtree(rtpsproc_ti, ett_rtpsproc);
170
171
    /* ***************************  ADVANCED 0  *******************************/
172
0
    param_id = tvb_get_uint16(tvb, offset, ENC_BIG_ENDIAN);
173
0
    if (param_id == PARAM_ID_ADVANCED_FRAME0) {
174
0
        proto_tree *rtpsproc_tree_frame0  = NULL;
175
0
        param_length = tvb_get_uint16(tvb, offset + 2, ENC_BIG_ENDIAN);
176
177
0
        rtpsproc_tree_security = proto_tree_add_subtree_format(
178
0
                rtpsproc_tree_general,
179
0
                tvb,
180
0
                offset,
181
0
                0,
182
0
                ett_rtpsproc_security,
183
0
                NULL,
184
0
                "%s",
185
0
                title_security);
186
187
0
        rtpsproc_tree_frame0 = proto_tree_add_subtree_format(
188
0
                rtpsproc_tree_security,
189
0
                tvb,
190
0
                offset,
191
0
                0,
192
0
                ett_rtpsproc_advanced_frame0,
193
0
                NULL,
194
0
                "%s",
195
0
                "RTPS level");
196
197
0
        proto_tree_add_uint(
198
0
                rtpsproc_tree_frame0,
199
0
                hf_rtpsproc_param_id,
200
0
                tvb,
201
0
                offset,
202
0
                2, /* length */
203
0
                param_id);
204
0
        offset += 2;
205
206
0
        proto_tree_add_uint(
207
0
                rtpsproc_tree_frame0,
208
0
                hf_rtpsproc_param_length,
209
0
                tvb,
210
0
                offset,
211
0
                2, /* length */
212
0
                param_length);
213
0
        offset += 2;
214
215
0
        message_payload = tvb_new_subset_length(tvb, offset, param_length);
216
0
        if (rtps_handle != NULL) {
217
0
            call_dissector(
218
0
                    rtps_handle,
219
0
                    message_payload,
220
0
                    pinfo,
221
0
                    rtpsproc_tree_frame0);
222
0
        }
223
0
        offset += param_length;
224
0
    } else {
225
        /*
226
         * If there is no security information, param_id is zeroed.
227
         * In that case the length is also zero, so we move 4 Bytes in total.
228
         */
229
0
        offset += 4;
230
0
    }
231
232
    /* ***************************  ADVANCED 1  *******************************/
233
0
    param_id = tvb_get_uint16(tvb, offset, ENC_BIG_ENDIAN);
234
0
    if (param_id == PARAM_ID_ADVANCED_FRAME1) {
235
0
        proto_tree *rtpsproc_tree_frame1  = NULL;
236
0
        const char *title = transport_data->direction
237
0
                ? "Submessage level"
238
0
                : "RTPS and Submessage level (no protection)";
239
0
        param_length = tvb_get_uint16(tvb, offset + 2, ENC_BIG_ENDIAN);
240
241
0
        if (rtpsproc_tree_security == NULL) {
242
0
            rtpsproc_tree_security = proto_tree_add_subtree_format(
243
0
                    rtpsproc_tree_general,
244
0
                    tvb,
245
0
                    offset,
246
0
                    0,
247
0
                    ett_rtpsproc_security,
248
0
                    NULL,
249
0
                    "%s",
250
0
                    title_security);
251
0
        }
252
253
0
        rtpsproc_tree_frame1 = proto_tree_add_subtree_format(
254
0
                rtpsproc_tree_security,
255
0
                tvb,
256
0
                offset,
257
0
                0,
258
0
                ett_rtpsproc_advanced_frame1,
259
0
                NULL,
260
0
                "%s",
261
0
                title);
262
263
0
        proto_tree_add_uint(
264
0
                rtpsproc_tree_frame1,
265
0
                hf_rtpsproc_param_id,
266
0
                tvb,
267
0
                offset,
268
0
                2, /* length */
269
0
                param_id);
270
0
        offset += 2;
271
272
0
        proto_tree_add_uint(
273
0
                rtpsproc_tree_frame1,
274
0
                hf_rtpsproc_param_length,
275
0
                tvb,
276
0
                offset,
277
0
                2, /* length */
278
0
                param_length);
279
0
        offset += 2;
280
281
        /*
282
         * Depending on the direction we have:
283
         *   - Inbound: List of decrypted submessages.
284
         *   - Outbound: The RTPS message before any kind of protection.
285
         * So, we handle them differently.
286
         */
287
0
        if (transport_data->direction) {
288
0
            tvbuff_t *rtps_submessages = NULL;
289
0
            wmem_strbuf_t *info_w_encrypted = NULL; /* Current info */
290
0
            wmem_strbuf_t *info_w_decrypted = NULL; /* New info */
291
292
            /*
293
             * Get the current column info. This has the RTPS frames with the
294
             * encrypted submessages. We are going to update the text so that
295
             * it has the decrypted information, which is more useful to the
296
             * user.
297
             */
298
0
            if (pinfo->cinfo) {
299
0
                const char *colinfo = col_get_text(pinfo->cinfo, COL_INFO);
300
0
                if (colinfo) {
301
0
                    info_w_encrypted = wmem_strbuf_new(
302
0
                            pinfo->pool,
303
0
                            colinfo);
304
0
                    col_clear(pinfo->cinfo, COL_INFO);
305
0
                }
306
0
            }
307
            /* Dissect the submessages using the RTPS dissector */
308
0
            rtps_submessages = tvb_new_subset_length(tvb, offset, param_length);
309
0
            dissect_rtps_submessages(
310
0
                    rtps_submessages,
311
0
                    0, /* offset */
312
0
                    pinfo,
313
0
                    rtpsproc_tree_frame1,
314
0
                    rtps_version,
315
0
                    rtps_vendor_id,
316
0
                    &guid,
317
0
                    false /* dissect_rtps_submessages. */);
318
319
            /*
320
             * Get the decrypted submessages and update the column information.
321
             */
322
0
            if (pinfo->cinfo) {
323
0
                const char *colinfo = col_get_text(pinfo->cinfo, COL_INFO);
324
0
                info_w_decrypted = wmem_strbuf_new(pinfo->pool, "");
325
0
                if (colinfo) {
326
0
                    get_new_colinfo_w_submessages(
327
0
                            info_w_decrypted, /* out */
328
0
                            info_w_encrypted, /* in */
329
0
                            colinfo); /* in */
330
0
                    col_clear(pinfo->cinfo, COL_INFO);
331
0
                    col_set_str(
332
0
                            pinfo->cinfo,
333
0
                            COL_INFO,
334
0
                            wmem_strbuf_get_str(info_w_decrypted));
335
0
                }
336
0
            }
337
0
        } else {
338
0
            message_payload = tvb_new_subset_length(tvb, offset, param_length);
339
0
            if (rtps_handle != NULL) {
340
0
                call_dissector(
341
0
                        rtps_handle,
342
0
                        message_payload,
343
0
                        pinfo,
344
0
                        rtpsproc_tree_frame1);
345
0
            }
346
0
        }
347
0
    }
348
0
    return tvb_captured_length(tvb);
349
0
}
350
351
/* ========================================================================== */
352
/*                                 Other                                      */
353
/* ========================================================================== */
354
355
/*
356
 * This function is called at startup and caches the handle for the register.
357
 * That way we don't have to find the dissector for each packet.
358
 */
359
void proto_reg_handoff_rtps_processed(void)
360
15
{
361
15
    rtps_handle = find_dissector("rtps");
362
15
}
363
364
static void get_new_colinfo_w_submessages(
365
        wmem_strbuf_t *out,
366
        wmem_strbuf_t *frame,
367
        const char *submessages)
368
0
{
369
0
    const char *pattern = "SEC_PREFIX, SEC_BODY, SEC_POSTFIX";
370
0
    const char *frame_str = wmem_strbuf_get_str(frame);
371
0
    size_t idx = 0; /* index for iterating frame_str */
372
0
    char *submessages_dup = g_strdup(submessages);
373
    /* First decrypted submessage in submessages list */
374
0
    char *submessage_current = strtok(submessages_dup, ", ");
375
    /* First encrypted submessage. Found by searching the RTPS colinfo */
376
0
    char *encrypted_current = strstr(&frame_str[idx], pattern);
377
378
0
    while (encrypted_current != NULL) {
379
        /* Copy the RTPS frame up to the newly found encrypted submessage */
380
0
        size_t length_to_copy = encrypted_current - &frame_str[idx];
381
0
        wmem_strbuf_append_len(out, &frame_str[idx], length_to_copy);
382
383
        /* Copy the decrypted contents that replace the encrypted submessage */
384
0
        wmem_strbuf_append(out, submessage_current);
385
386
        /* Advance the index and continue searching */
387
0
        idx += length_to_copy + strlen(pattern);
388
0
        encrypted_current = strstr(&frame_str[idx], pattern);
389
0
    }
390
    /* Copy the remaining from the RTPS frame */
391
0
    wmem_strbuf_append(out, &frame_str[idx]);
392
0
    g_free(submessages_dup);
393
0
}
394
395
/* ========================================================================== */
396
/*                            Protocol registration                           */
397
/* ========================================================================== */
398
void
399
proto_register_rtps_processed(void)
400
15
{
401
15
    static hf_register_info hf[] = {
402
15
        {
403
15
            &hf_rtpsproc_param_id,
404
15
            {
405
15
                "Parameter Identifier", "rtpsproc.param.id",
406
15
                FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL
407
15
            },
408
15
        },
409
15
        {
410
15
            &hf_rtpsproc_param_length,
411
15
            {
412
15
                "Parameter Length", "rtpsproc.param.length",
413
15
                FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL
414
15
            }
415
15
        },
416
15
    };
417
15
    static int *ett[] = {
418
15
        &ett_rtpsproc,
419
15
        &ett_rtpsproc_security,
420
15
        &ett_rtpsproc_advanced_frame0,
421
15
        &ett_rtpsproc_advanced_frame1
422
15
    };
423
424
  /* Register the protocol name and description */
425
15
    rtpsproc_tree = proto_register_protocol("Real-Time Publish-Subscribe Wire Protocol (processed)", "RTPS-PROC", "rtpsproc");
426
427
    /* Required function calls to register the header fields and subtrees */
428
15
    rtpsproc_hf = proto_registrar_get_nth(rtpsproc_tree);
429
15
    proto_register_field_array(rtpsproc_tree, hf, array_length(hf));
430
15
    proto_register_subtree_array(ett, array_length(ett));
431
432
15
    register_dissector("rtpsproc", dissect_rtps_processed, rtpsproc_tree);
433
15
}
434
435
// /*
436
//  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
437
//  *
438
//  * Local variables:
439
//  * c-basic-offset: 4
440
//  * tab-width: 8
441
//  * indent-tabs-mode: nil
442
//  * End:
443
//  *
444
//  * vi: set shiftwidth=4 tabstop=8 expandtab:
445
//  * :indentSize=4:tabSize=8:noTabs=true:
446
//  */