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