1 /* packet-rtps-processed.c
2 * Dissector for the Real-Time Publish-Subscribe (RTPS) Processed Protocol.
4 * (c) 2020 Copyright, Real-Time Innovations, Inc.
5 * Real-Time Innovations, Inc.
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <gerald@wireshark.org>
11 * Copyright 1998 Gerald Combs
13 * SPDX-License-Identifier: GPL-2.0-or-later
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.
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.
26 * The processed dissector is called by the transport dissector. It should never
27 * be called directly by Wireshark without going through the transport
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
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).
39 * - Inbound traffic: A list of decrypted RTPS submessages (the protected
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.
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.
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.
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"
67 #define PARAM_ID_ADVANCED_FRAME0 0x000C1
68 #define PARAM_ID_ADVANCED_FRAME1 0x000C2
70 void proto_reg_handoff_rtps_processed(void);
71 void proto_register_rtps_processed(void);
72 static int dissect_rtps_processed(
77 static void get_new_colinfo_w_submessages(
80 const char *submessages
);
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
;
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
;
94 /* Used for caching a handle to the RTPS dissector */
95 static dissector_handle_t rtps_handle
;
97 /* ========================================================================== */
99 /* ========================================================================== */
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).
108 static int dissect_rtps_processed(
114 proto_tree
*rtpsproc_tree_general
= NULL
;
115 proto_tree
*rtpsproc_tree_security
= NULL
;
116 proto_item
*rtpsproc_ti
= NULL
;
118 uint16_t param_length
;
120 int offset_version
= 4; /* 'R', 'T', 'P', 'S' */
121 tvbuff_t
*rtps_payload
= NULL
;
122 tvbuff_t
*message_payload
= NULL
;
123 struct rtpsvt_data
*transport_data
= (struct rtpsvt_data
*) data
;
124 const char *title_security
;
125 uint16_t rtps_version
= 0x0203;
126 uint16_t rtps_vendor_id
= 0x0101;
129 if (transport_data
== NULL
) {
130 /* Reject the packet if no transport information */
133 param_length
= transport_data
->rtps_length
;
134 title_security
= transport_data
->direction
== 1
135 ? "RTPS Security decoding"
136 : "RTPS Security pre-encoding";
138 /* ***************************** MAIN ***********************************/
140 * The contents passed to the rtpsproc dissector must start with the RTPS
143 rtps_version
= tvb_get_uint16(
145 offset
+ offset_version
,
147 rtps_vendor_id
= tvb_get_uint16(
149 offset
+ offset_version
+ 2,
151 guid
.host_id
= tvb_get_ntohl(tvb
, offset
+ offset_version
+ 4);
152 guid
.app_id
= tvb_get_ntohl(tvb
, offset
+ offset_version
+ 8);
153 guid
.instance_id
= tvb_get_ntohl(tvb
, offset
+ offset_version
+ 12);
154 guid
.fields_present
= GUID_HAS_HOST_ID
| GUID_HAS_APP_ID
| GUID_HAS_INSTANCE_ID
;
155 rtps_payload
= tvb_new_subset_length(tvb
, offset
, param_length
);
156 if (rtps_handle
!= NULL
) {
157 call_dissector(rtps_handle
, rtps_payload
, pinfo
, tree
);
159 offset
+= param_length
;
161 /* *********** Add subtree used for the fields of our rtpsproc_tree *******/
162 rtpsproc_ti
= proto_tree_add_item(
169 rtpsproc_tree_general
= proto_item_add_subtree(rtpsproc_ti
, ett_rtpsproc
);
171 /* *************************** ADVANCED 0 *******************************/
172 param_id
= tvb_get_uint16(tvb
, offset
, ENC_BIG_ENDIAN
);
173 if (param_id
== PARAM_ID_ADVANCED_FRAME0
) {
174 proto_tree
*rtpsproc_tree_frame0
= NULL
;
175 param_length
= tvb_get_uint16(tvb
, offset
+ 2, ENC_BIG_ENDIAN
);
177 rtpsproc_tree_security
= proto_tree_add_subtree_format(
178 rtpsproc_tree_general
,
182 ett_rtpsproc_security
,
187 rtpsproc_tree_frame0
= proto_tree_add_subtree_format(
188 rtpsproc_tree_security
,
192 ett_rtpsproc_advanced_frame0
,
198 rtpsproc_tree_frame0
,
199 hf_rtpsproc_param_id
,
207 rtpsproc_tree_frame0
,
208 hf_rtpsproc_param_length
,
215 message_payload
= tvb_new_subset_length(tvb
, offset
, param_length
);
216 if (rtps_handle
!= NULL
) {
221 rtpsproc_tree_frame0
);
223 offset
+= param_length
;
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.
232 /* *************************** ADVANCED 1 *******************************/
233 param_id
= tvb_get_uint16(tvb
, offset
, ENC_BIG_ENDIAN
);
234 if (param_id
== PARAM_ID_ADVANCED_FRAME1
) {
235 proto_tree
*rtpsproc_tree_frame1
= NULL
;
236 const char *title
= transport_data
->direction
238 : "RTPS and Submessage level (no protection)";
239 param_length
= tvb_get_uint16(tvb
, offset
+ 2, ENC_BIG_ENDIAN
);
241 if (rtpsproc_tree_security
== NULL
) {
242 rtpsproc_tree_security
= proto_tree_add_subtree_format(
243 rtpsproc_tree_general
,
247 ett_rtpsproc_security
,
253 rtpsproc_tree_frame1
= proto_tree_add_subtree_format(
254 rtpsproc_tree_security
,
258 ett_rtpsproc_advanced_frame1
,
264 rtpsproc_tree_frame1
,
265 hf_rtpsproc_param_id
,
273 rtpsproc_tree_frame1
,
274 hf_rtpsproc_param_length
,
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.
287 if (transport_data
->direction
) {
288 tvbuff_t
*rtps_submessages
= NULL
;
289 wmem_strbuf_t
*info_w_encrypted
= NULL
; /* Current info */
290 wmem_strbuf_t
*info_w_decrypted
= NULL
; /* New info */
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
299 const char *colinfo
= col_get_text(pinfo
->cinfo
, COL_INFO
);
301 info_w_encrypted
= wmem_strbuf_new(
304 col_clear(pinfo
->cinfo
, COL_INFO
);
307 /* Dissect the submessages using the RTPS dissector */
308 rtps_submessages
= tvb_new_subset_length(tvb
, offset
, param_length
);
309 dissect_rtps_submessages(
313 rtpsproc_tree_frame1
,
317 false /* dissect_rtps_submessages. */);
320 * Get the decrypted submessages and update the column information.
323 const char *colinfo
= col_get_text(pinfo
->cinfo
, COL_INFO
);
324 info_w_decrypted
= wmem_strbuf_new(pinfo
->pool
, "");
326 get_new_colinfo_w_submessages(
327 info_w_decrypted
, /* out */
328 info_w_encrypted
, /* in */
330 col_clear(pinfo
->cinfo
, COL_INFO
);
334 wmem_strbuf_get_str(info_w_decrypted
));
338 message_payload
= tvb_new_subset_length(tvb
, offset
, param_length
);
339 if (rtps_handle
!= NULL
) {
344 rtpsproc_tree_frame1
);
348 return tvb_captured_length(tvb
);
351 /* ========================================================================== */
353 /* ========================================================================== */
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.
359 void proto_reg_handoff_rtps_processed(void)
361 rtps_handle
= find_dissector("rtps");
364 static void get_new_colinfo_w_submessages(
366 wmem_strbuf_t
*frame
,
367 const char *submessages
)
369 const char *pattern
= "SEC_PREFIX, SEC_BODY, SEC_POSTFIX";
370 const char *frame_str
= wmem_strbuf_get_str(frame
);
371 size_t idx
= 0; /* index for iterating frame_str */
372 char *submessages_dup
= g_strdup(submessages
);
373 /* First decrypted submessage in submessages list */
374 char *submessage_current
= strtok(submessages_dup
, ", ");
375 /* First encrypted submessage. Found by searching the RTPS colinfo */
376 char *encrypted_current
= strstr(&frame_str
[idx
], pattern
);
378 while (encrypted_current
!= NULL
) {
379 /* Copy the RTPS frame up to the newly found encrypted submessage */
380 size_t length_to_copy
= encrypted_current
- &frame_str
[idx
];
381 wmem_strbuf_append_len(out
, &frame_str
[idx
], length_to_copy
);
383 /* Copy the decrypted contents that replace the encrypted submessage */
384 wmem_strbuf_append(out
, submessage_current
);
386 /* Advance the index and continue searching */
387 idx
+= length_to_copy
+ strlen(pattern
);
388 encrypted_current
= strstr(&frame_str
[idx
], pattern
);
390 /* Copy the remaining from the RTPS frame */
391 wmem_strbuf_append(out
, &frame_str
[idx
]);
394 /* ========================================================================== */
395 /* Protocol registration */
396 /* ========================================================================== */
398 proto_register_rtps_processed(void)
400 static hf_register_info hf
[] = {
402 &hf_rtpsproc_param_id
,
404 "Parameter Identifier", "rtpsproc.param.id",
405 FT_UINT16
, BASE_DEC
, NULL
, 0, NULL
, HFILL
409 &hf_rtpsproc_param_length
,
411 "Parameter Length", "rtpsproc.param.length",
412 FT_UINT16
, BASE_DEC
, NULL
, 0, NULL
, HFILL
416 static int *ett
[] = {
418 &ett_rtpsproc_security
,
419 &ett_rtpsproc_advanced_frame0
,
420 &ett_rtpsproc_advanced_frame1
423 /* Register the protocol name and description */
424 rtpsproc_tree
= proto_register_protocol("Real-Time Publish-Subscribe Wire Protocol (processed)", "RTPS-PROC", "rtpsproc");
426 /* Required function calls to register the header fields and subtrees */
427 rtpsproc_hf
= proto_registrar_get_nth(rtpsproc_tree
);
428 proto_register_field_array(rtpsproc_tree
, hf
, array_length(hf
));
429 proto_register_subtree_array(ett
, array_length(ett
));
431 register_dissector("rtpsproc", dissect_rtps_processed
, rtpsproc_tree
);
435 // * Editor modelines - https://www.wireshark.org/tools/modelines.html
437 // * Local variables:
438 // * c-basic-offset: 4
440 // * indent-tabs-mode: nil
443 // * vi: set shiftwidth=4 tabstop=8 expandtab:
444 // * :indentSize=4:tabSize=8:noTabs=true: