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-tcpros.c
Line
Count
Source
1
/* packet-tcpros.c
2
 * Routines for Robot Operating System TCP protocol (TCPROS)
3
 * Copyright 2015, Guillaume Autran  (see AUTHORS file)
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
#include "config.h"
13
14
#include <epan/packet.h>
15
#include <epan/etypes.h>
16
#include <epan/prefs.h>
17
18
#include "packet-tcp.h"
19
20
21
0
#define SIZE_OF_LENGTH_FIELD 4
22
0
#define SIZE_OF_LENGTH_STAMP (4 + 4)
23
24
void proto_register_tcpros(void);
25
void proto_reg_handoff_tcpros(void);
26
27
28
static int proto_tcpros;
29
static dissector_handle_t tcpros_handle;
30
31
/** desegmentation of TCPROS over TCP */
32
static bool tcpros_desegment = true;
33
34
35
static int hf_tcpros_connection_header;
36
static int hf_tcpros_connection_header_length;
37
static int hf_tcpros_connection_header_content;
38
static int hf_tcpros_connection_header_field;
39
static int hf_tcpros_connection_header_field_length;
40
static int hf_tcpros_connection_header_field_data;
41
static int hf_tcpros_connection_header_field_name;
42
static int hf_tcpros_connection_header_field_value;
43
static int hf_tcpros_clock;
44
static int hf_tcpros_clock_length;
45
static int hf_tcpros_message;
46
static int hf_tcpros_message_length;
47
static int hf_tcpros_message_body;
48
static int hf_tcpros_message_header;
49
static int hf_tcpros_message_header_seq;
50
static int hf_tcpros_message_header_stamp;
51
static int hf_tcpros_message_header_stamp_sec;
52
static int hf_tcpros_message_header_stamp_nsec;
53
static int hf_tcpros_message_header_frame;
54
static int hf_tcpros_message_header_frame_length;
55
static int hf_tcpros_message_header_frame_value;
56
static int hf_tcpros_message_payload;
57
58
static int ett_tcpros;
59
60
/**
61
 * This is the ROS connection header dissector. The general packet format is described
62
 * here: http://wiki.ros.org/ROS/TCPROS
63
 * In short, a connection header looks like such: '4-byte length + [4-byte field length + "field=value" ]*'
64
 */
65
static unsigned
66
dissect_ros_connection_header_field(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, unsigned offset)
67
0
{
68
0
  proto_item *ti;
69
0
  proto_tree *field_tree;
70
71
0
  uint32_t fLen = 0;
72
0
  unsigned   sep, ret = 0;
73
74
  /** Do we have enough for a length field? (ie: 4 bytes) */
75
0
  if( tvb_reported_length_remaining(tvb, offset) > SIZE_OF_LENGTH_FIELD ) {
76
    /** Get the length of the next field */
77
0
    fLen = tvb_get_letohl(tvb, offset);
78
79
    /** Display the field as a utf-8 string */
80
0
    ti = proto_tree_add_item(tree, hf_tcpros_connection_header_field, tvb, offset, SIZE_OF_LENGTH_FIELD, ENC_UTF_8|ENC_LITTLE_ENDIAN);
81
0
    field_tree = proto_item_add_subtree(ti, ett_tcpros);
82
83
84
0
    proto_tree_add_item(field_tree, hf_tcpros_connection_header_field_length, tvb, offset, SIZE_OF_LENGTH_FIELD, ENC_LITTLE_ENDIAN);
85
0
    offset += SIZE_OF_LENGTH_FIELD;
86
0
    ti = proto_tree_add_item(field_tree, hf_tcpros_connection_header_field_data, tvb, offset, fLen, ENC_UTF_8);
87
88
    /** Look for the '=' separator */
89
0
    bool sep_found = tvb_find_uint8_length(tvb, offset, fLen, '=', &sep);
90
0
    sep = sep - offset;
91
92
    /** If we find a separator, then split field name and value */
93
0
    if((sep_found == false) ||( sep > 0 )) {
94
0
      const char* field;
95
0
      field_tree = proto_item_add_subtree(ti, ett_tcpros);
96
0
      proto_tree_add_item_ret_string(field_tree, hf_tcpros_connection_header_field_name, tvb, offset, sep, ENC_UTF_8|ENC_NA, pinfo->pool, (const uint8_t**)&field);
97
0
      proto_tree_add_item(field_tree, hf_tcpros_connection_header_field_value, tvb, offset+sep+1, fLen - sep - 1, ENC_UTF_8);
98
99
0
      col_append_str(pinfo->cinfo, COL_INFO, field);
100
0
    }
101
0
    ret = fLen + SIZE_OF_LENGTH_FIELD;
102
0
  }
103
104
0
  return ret;
105
0
}
106
107
static unsigned
108
dissect_ros_connection_header(tvbuff_t *tvb, proto_tree *root_tree, packet_info *pinfo, unsigned offset)
109
0
{
110
0
  proto_item *ti;
111
0
  proto_tree *sub_tree;
112
113
0
  unsigned consumed_len = 0;
114
0
  uint32_t header_len = tvb_get_letohl(tvb, offset);
115
116
0
  col_append_str(pinfo->cinfo, COL_INFO, "[ROS Conn] Metadata: [");
117
118
  /** We got a connection header */
119
0
  ti = proto_tree_add_item(root_tree, hf_tcpros_connection_header, tvb, offset, SIZE_OF_LENGTH_FIELD, ENC_NA|ENC_LITTLE_ENDIAN);
120
0
  sub_tree = proto_item_add_subtree(ti, ett_tcpros);
121
122
0
  proto_tree_add_item(sub_tree, hf_tcpros_connection_header_length, tvb, offset + consumed_len, SIZE_OF_LENGTH_FIELD, ENC_LITTLE_ENDIAN);
123
0
  consumed_len += SIZE_OF_LENGTH_FIELD;
124
125
0
  ti = proto_tree_add_item(sub_tree, hf_tcpros_connection_header_content, tvb, offset + consumed_len, header_len, ENC_NA);
126
0
  sub_tree = proto_item_add_subtree(ti, ett_tcpros);
127
128
0
  header_len += SIZE_OF_LENGTH_FIELD;
129
130
0
  while( consumed_len < header_len ) {
131
0
    unsigned len = dissect_ros_connection_header_field(tvb, sub_tree, pinfo, offset + consumed_len);
132
0
    consumed_len += len;
133
0
    if( len == 0 ) {
134
0
      break;
135
0
    }
136
0
    if( consumed_len < header_len ) {
137
0
      col_append_str(pinfo->cinfo, COL_INFO, ",");
138
0
    }
139
0
  }
140
0
  col_append_str(pinfo->cinfo, COL_INFO, "]");
141
142
0
  return consumed_len;
143
0
}
144
145
146
static unsigned
147
dissect_ros_message_header_stamp(tvbuff_t *tvb, proto_tree *root_tree, packet_info *pinfo, unsigned offset)
148
0
{
149
0
  proto_item *ti;
150
0
  proto_tree *sub_tree;
151
152
0
  unsigned consumed_len = 0;
153
0
  uint32_t sec, nsec;
154
155
0
  ti = proto_tree_add_item(root_tree, hf_tcpros_message_header_stamp, tvb, offset + consumed_len, SIZE_OF_LENGTH_STAMP, ENC_LITTLE_ENDIAN);
156
0
  sub_tree = proto_item_add_subtree(ti, ett_tcpros);
157
158
  /** Seconds */
159
0
  proto_tree_add_item_ret_uint(sub_tree, hf_tcpros_message_header_stamp_sec, tvb, offset + consumed_len, SIZE_OF_LENGTH_FIELD, ENC_LITTLE_ENDIAN, &sec);
160
0
  consumed_len += SIZE_OF_LENGTH_FIELD;
161
162
  /** Nano seconds */
163
0
  proto_tree_add_item_ret_uint(sub_tree, hf_tcpros_message_header_stamp_nsec, tvb, offset + consumed_len, SIZE_OF_LENGTH_FIELD, ENC_LITTLE_ENDIAN, &nsec);
164
0
  consumed_len += SIZE_OF_LENGTH_FIELD;
165
166
  /** Info */
167
0
  col_append_fstr(pinfo->cinfo, COL_INFO, "Timestamp: %d.%09d ", sec, nsec);
168
169
0
  return consumed_len;
170
0
}
171
172
static unsigned
173
dissect_ros_clock(tvbuff_t *tvb, proto_tree *root_tree, packet_info *pinfo, unsigned offset)
174
0
{
175
0
  proto_item *ti;
176
0
  proto_tree *sub_tree;
177
178
0
  unsigned consumed_len = 0;
179
180
0
  col_append_str(pinfo->cinfo, COL_INFO, "[ROS Clock] ");
181
182
  /** We got a ROS Clock msg */
183
0
  ti = proto_tree_add_item(root_tree, hf_tcpros_clock, tvb, offset, SIZE_OF_LENGTH_FIELD, ENC_LITTLE_ENDIAN);
184
0
  sub_tree = proto_item_add_subtree(ti, ett_tcpros);
185
186
0
  proto_tree_add_item(sub_tree, hf_tcpros_clock_length, tvb, offset, SIZE_OF_LENGTH_FIELD, ENC_LITTLE_ENDIAN);
187
0
  consumed_len += SIZE_OF_LENGTH_FIELD;
188
189
0
  consumed_len += dissect_ros_message_header_stamp(tvb, sub_tree, pinfo, offset + consumed_len);
190
191
0
  return consumed_len;
192
0
}
193
194
static unsigned
195
dissect_ros_message_header(tvbuff_t *tvb, proto_tree *root_tree, packet_info *pinfo, unsigned offset)
196
0
{
197
0
  proto_item *ti;
198
0
  proto_tree *sub_tree;
199
200
0
  unsigned consumed_len = 0;
201
0
  uint32_t frame_id_len;
202
0
  uint32_t seq;
203
0
  unsigned header_len;
204
0
  const uint8_t* frame_str;
205
206
0
  frame_id_len = tvb_get_letohl(tvb, offset + consumed_len + SIZE_OF_LENGTH_FIELD + SIZE_OF_LENGTH_STAMP);
207
0
  header_len = SIZE_OF_LENGTH_FIELD + SIZE_OF_LENGTH_STAMP + SIZE_OF_LENGTH_FIELD + frame_id_len;
208
209
  /** Header */
210
0
  ti = proto_tree_add_item(root_tree, hf_tcpros_message_header, tvb, offset + consumed_len, header_len, ENC_NA);
211
0
  sub_tree = proto_item_add_subtree(ti, ett_tcpros);
212
213
  /** Sequence number */
214
0
  proto_tree_add_item_ret_uint(sub_tree, hf_tcpros_message_header_seq, tvb, offset + consumed_len, SIZE_OF_LENGTH_FIELD, ENC_LITTLE_ENDIAN, &seq);
215
0
  consumed_len += SIZE_OF_LENGTH_FIELD;
216
0
  col_append_fstr(pinfo->cinfo, COL_INFO, "Seq: %d ", seq);
217
218
  /** Timestamp */
219
0
  consumed_len += dissect_ros_message_header_stamp(tvb, sub_tree, pinfo, offset + consumed_len);
220
221
  /** Frame ID */
222
0
  ti = proto_tree_add_item(sub_tree, hf_tcpros_message_header_frame, tvb, offset + consumed_len, SIZE_OF_LENGTH_FIELD, ENC_UTF_8|ENC_LITTLE_ENDIAN);
223
0
  sub_tree = proto_item_add_subtree(ti, ett_tcpros);
224
225
0
  proto_tree_add_item(sub_tree, hf_tcpros_message_header_frame_length, tvb, offset + consumed_len, SIZE_OF_LENGTH_FIELD, ENC_LITTLE_ENDIAN);
226
0
  consumed_len += SIZE_OF_LENGTH_FIELD;
227
228
0
  proto_tree_add_item_ret_string(sub_tree, hf_tcpros_message_header_frame_value, tvb, offset + consumed_len, frame_id_len, ENC_UTF_8|ENC_NA, pinfo->pool, &frame_str);
229
0
  col_append_fstr(pinfo->cinfo, COL_INFO, "Frame ID: '%s' ", frame_str);
230
0
  consumed_len += frame_id_len;
231
232
0
  return consumed_len;
233
0
}
234
235
236
/**
237
 * This is the ROS message dissector. A ROS message contains two parts: a msg header; a msg payload.
238
 * Because the packet is all in binary format, we don't really know the payload format (we don't know the payload type either).
239
 * However, every packet has the same header as defined here: http://docs.ros.org/api/std_msgs/html/msg/Header.html
240
 * So, we can break this one down and display it.
241
 */
242
static unsigned
243
dissect_ros_message(tvbuff_t *tvb, proto_tree *root_tree, packet_info *pinfo, unsigned offset)
244
0
{
245
0
  proto_item *ti;
246
0
  proto_tree *sub_tree;
247
248
0
  unsigned consumed_len = 0;
249
0
  uint32_t total_len = tvb_get_letohl(tvb, offset);
250
0
  unsigned payload_len;
251
252
0
  col_append_str(pinfo->cinfo, COL_INFO, "[ROS Msg] ");
253
254
  /** We got a ROS msg */
255
0
  ti = proto_tree_add_item(root_tree, hf_tcpros_message, tvb, offset + consumed_len, SIZE_OF_LENGTH_FIELD, ENC_NA | ENC_LITTLE_ENDIAN);
256
0
  sub_tree = proto_item_add_subtree(ti, ett_tcpros);
257
258
0
  proto_tree_add_item(sub_tree, hf_tcpros_message_length, tvb, offset + consumed_len, SIZE_OF_LENGTH_FIELD, ENC_LITTLE_ENDIAN);
259
0
  consumed_len += SIZE_OF_LENGTH_FIELD;
260
261
  /** Body */
262
0
  ti = proto_tree_add_item(sub_tree, hf_tcpros_message_body, tvb, offset + consumed_len, total_len, ENC_NA);
263
0
  sub_tree = proto_item_add_subtree(ti, ett_tcpros);
264
265
  /** Body.Header */
266
0
  consumed_len += dissect_ros_message_header(tvb, sub_tree, pinfo, offset + consumed_len);
267
268
  /** Body.Payload */
269
0
  payload_len = (total_len + SIZE_OF_LENGTH_FIELD) - consumed_len;
270
0
  proto_tree_add_item(sub_tree, hf_tcpros_message_payload, tvb, offset + consumed_len, payload_len, ENC_NA);
271
0
  consumed_len += payload_len;
272
273
274
0
  return consumed_len;
275
0
}
276
277
278
/**
279
 * This is the poor man's way to differentiate between a connection header packet and a message packet.
280
 */
281
static bool
282
is_connection_header(tvbuff_t *tvb, packet_info *pinfo _U_ , unsigned offset)
283
0
{
284
0
  bool is_con_header = false;
285
0
  uint32_t len1 = tvb_get_letohl(tvb, offset);
286
0
  uint32_t len2 = tvb_get_letohl(tvb, offset + SIZE_OF_LENGTH_FIELD);
287
288
289
0
  if( len1 > len2 ) {
290
0
    is_con_header = true;
291
0
  }
292
293
294
0
  return is_con_header;
295
0
}
296
297
static bool
298
is_rosheaderfield(tvbuff_t *tvb, packet_info *pinfo _U_ , unsigned offset)
299
0
{
300
  /** ROS Header Field:
301
      4-byte len + string */
302
0
  unsigned available = tvb_reported_length_remaining(tvb, offset);
303
0
  uint32_t string_len = 0;
304
0
  uint32_t i;
305
306
0
  if( available < 4 )
307
0
    return false;
308
309
0
  string_len = tvb_get_letohl(tvb, offset);
310
311
  /** If we don't have enough data for the whole string, assume its not */
312
0
  if( available < (string_len + 4) )
313
0
    return false;
314
  /** Check for a valid ascii character and not nil */
315
0
  for( i = 0; i < string_len; i++ ) {
316
0
    int8_t ch = tvb_get_uint8(tvb, offset + 4 + i);
317
0
    if( !g_ascii_isalnum(ch) || 0x00 == ch )
318
0
      return false;
319
0
  }
320
321
  /** Assume it is */
322
0
  return true;
323
0
}
324
325
static bool
326
is_rosconnection_header(tvbuff_t *tvb, packet_info *pinfo _U_ , unsigned offset)
327
0
{
328
  /** ROS Connection Headers: http://wiki.ros.org/ROS/Connection%20Header
329
      4-byte length + [4-byte length + string] */
330
0
  unsigned available = tvb_reported_length_remaining(tvb, offset);
331
0
  uint32_t msg_len = 0;
332
333
0
  if( available < 8+1 )
334
0
    return false;
335
336
0
  msg_len = tvb_get_letohl(tvb, offset);
337
0
  if( msg_len < 4+1 )
338
0
    return false;
339
340
  /** Check first header field */
341
0
  if( !is_rosheaderfield(tvb, pinfo, offset + 4) )
342
0
    return false;
343
344
345
0
  return true;
346
0
}
347
348
static bool
349
is_rosclock(tvbuff_t *tvb, packet_info *pinfo _U_ , unsigned offset)
350
0
{
351
  /** ROS Clock message: http://docs.ros.org/api/rosgraph_msgs/html/msg/Clock.html
352
      4-byte length + 8-byte timestamp == 12 bytes exactly */
353
0
  unsigned available = tvb_reported_length_remaining(tvb, offset);
354
0
  if( available != 12 )
355
0
    return false;
356
357
0
  if( tvb_get_letohl(tvb, offset) != 8 )
358
0
    return false;
359
360
  /** This is highly likely a clock message. */
361
0
  return true;
362
0
}
363
364
static bool
365
is_rosmsg(tvbuff_t *tvb, packet_info *pinfo _U_ , unsigned offset)
366
0
{
367
  /** Most ROS messages start with a header: http://docs.ros.org/jade/api/std_msgs/html/msg/Header.html
368
      4-byte size + 4-byte sequence id + 8-byte timestamp + 4-byte frame id length + frame id */
369
0
  unsigned available = tvb_reported_length_remaining(tvb, offset);
370
0
  uint32_t string_len = 0;
371
0
  uint32_t msg_len = 0;
372
373
0
  if( available < 20 )
374
0
    return false;
375
376
0
  msg_len = tvb_get_letohl(tvb, offset);
377
0
  if( msg_len < 16 )
378
0
    return false;
379
380
  /** Check to see if the frame id length is reasonable */
381
0
  string_len = tvb_get_letohl(tvb, offset + 4 + 4 + 8);
382
0
  if( string_len > (msg_len - (4 + 8 + 4)) )
383
0
    return false;
384
385
  /** If we don't have enough data for the whole string, assume its not */
386
0
  if( (unsigned)available < (string_len + 4) )
387
0
    return false;
388
389
  /** This is highly likely a ROS message. */
390
0
  return true;
391
0
}
392
393
static void
394
dissect_ros_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, bool is_tcp _U_ )
395
0
{
396
0
  proto_item *ti;
397
0
  proto_tree *root_tree;
398
399
0
  unsigned offset;
400
401
402
  /** Clear out stuff in the info column */
403
0
  col_clear(pinfo->cinfo, COL_INFO);
404
405
0
  ti = proto_tree_add_item(tree, proto_tcpros, tvb, 0, -1, ENC_NA);
406
0
  root_tree = proto_item_add_subtree(ti, ett_tcpros);
407
408
0
  offset = 0;
409
410
0
  while(offset < tvb_reported_length(tvb)) {
411
0
    int available = tvb_reported_length_remaining(tvb, offset);
412
413
0
    if( (available < SIZE_OF_LENGTH_FIELD) || ((unsigned)available < tvb_get_letohl(tvb, offset)) ) {
414
      /** we ran out of data: ask for more */
415
0
      pinfo->desegment_offset = offset;
416
0
      pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
417
0
      return;
418
0
    }
419
    /** There are two types of packet: Connection Headers and ROS Message. Which one is it? */
420
0
    if( is_rosclock(tvb, pinfo, offset) ) {
421
      /** This is a ROS Clock message. */
422
0
      offset += dissect_ros_clock(tvb, root_tree, pinfo, offset);
423
0
    } else if( is_rosmsg(tvb, pinfo, offset) ) {
424
      /** We have a ROS message. */
425
0
      offset += dissect_ros_message(tvb, root_tree, pinfo, offset);
426
0
    } else if( is_rosconnection_header(tvb, pinfo, offset) ) {
427
      /** Check for a connection header */
428
0
      offset += dissect_ros_connection_header(tvb, root_tree, pinfo, offset);
429
0
    } else if( is_connection_header(tvb, pinfo, offset) ) {
430
      /** We have a ROS connection header. */
431
0
      offset += dissect_ros_connection_header(tvb, root_tree, pinfo, offset);
432
0
    } else {
433
      /** We have a ROS message. */
434
0
      offset += dissect_ros_message(tvb, root_tree, pinfo, offset);
435
0
    }
436
437
0
  }
438
0
}
439
440
441
static unsigned
442
get_tcpros_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
443
0
{
444
0
  uint32_t plen;
445
446
  /*
447
   * Get the length of the TCPROS packet.
448
   */
449
0
  plen = tvb_get_letohl(tvb, offset);
450
451
  /*
452
   * That length doesn't include the length field itself; add that in.
453
   */
454
0
  return plen + SIZE_OF_LENGTH_FIELD;
455
0
}
456
457
458
static int
459
dissect_tcpros_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
460
0
{
461
0
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "TCPROS");
462
463
0
  dissect_ros_common(tvb, pinfo, tree, true);
464
0
  return tvb_reported_length(tvb);
465
0
}
466
467
static int
468
dissect_tcpros(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
469
0
{
470
0
  tcp_dissect_pdus(tvb, pinfo, tree, tcpros_desegment, SIZE_OF_LENGTH_FIELD, get_tcpros_pdu_len,
471
0
       dissect_tcpros_pdu, data);
472
0
  return tvb_reported_length(tvb);
473
0
}
474
475
476
void
477
proto_register_tcpros(void)
478
15
{
479
15
  static hf_register_info hf[] = {
480
15
    { &hf_tcpros_connection_header,             { "ROS Connection", "tcpros.header",
481
15
                    FT_UINT_BYTES, BASE_NONE, NULL, 0,
482
15
                    "Message Header", HFILL } },
483
15
    { &hf_tcpros_connection_header_length,      { "Header Length", "tcpros.header_length",
484
15
                    FT_UINT32, BASE_DEC, NULL, 0,
485
15
                    "Message Header Length", HFILL } },
486
15
    { &hf_tcpros_connection_header_content,     { "Header Content", "tcpros.header_content",
487
15
                    FT_BYTES, BASE_NONE, NULL, 0,
488
15
                    "Message Header Content", HFILL } },
489
490
15
    { &hf_tcpros_connection_header_field,       { "Field", "tcpros.header_field",
491
15
                    FT_UINT_STRING, BASE_NONE, NULL, 0,
492
15
                    "Message Header Field", HFILL } },
493
15
    { &hf_tcpros_connection_header_field_length, { "Field Length", "tcpros.header_field_length",
494
15
                     FT_UINT32, BASE_DEC, NULL, 0,
495
15
                     "Message Header Field Length", HFILL } },
496
15
    { &hf_tcpros_connection_header_field_data, { "Field Content", "tcpros.header_field_data",
497
15
                   FT_STRING, BASE_NONE, NULL, 0,
498
15
                   "Message Header Field Content", HFILL } },
499
15
    { &hf_tcpros_connection_header_field_name,  { "Name", "tcpros.header_field_name",
500
15
                    FT_STRING, BASE_NONE, NULL, 0,
501
15
                    "Message Header Field Name", HFILL } },
502
15
    { &hf_tcpros_connection_header_field_value, { "Value", "tcpros.header_field_value",
503
15
                    FT_STRING, BASE_NONE, NULL, 0,
504
15
                    "Message Header Field Value", HFILL } },
505
506
15
    { &hf_tcpros_clock,                         { "ROS Clock", "tcpros.clock",
507
15
                    FT_UINT_BYTES, BASE_NONE, NULL, 0,
508
15
                    "ROS Clock Packet", HFILL } },
509
15
    { &hf_tcpros_clock_length,                  { "Clock Length", "tcpros.clock.length",
510
15
                    FT_UINT32, BASE_DEC, NULL, 0,
511
15
                    "ROS Clock Packet length", HFILL } },
512
513
15
    { &hf_tcpros_message,                       { "ROS Message", "tcpros.message",
514
15
                    FT_UINT_BYTES, BASE_NONE, NULL, 0,
515
15
                    "ROS Message Packet", HFILL } },
516
15
    { &hf_tcpros_message_length,                { "Message Length", "tcpros.message.length",
517
15
                    FT_UINT32, BASE_DEC, NULL, 0,
518
15
                    "ROS Message Packet length", HFILL } },
519
15
    { &hf_tcpros_message_body,                   { "Message Content", "tcpros.message.body",
520
15
                     FT_BYTES, BASE_NONE, NULL, 0,
521
15
                     "ROS Message Packet Body", HFILL } },
522
523
15
    { &hf_tcpros_message_header,                { "Header", "tcpros.message.header",
524
15
                    FT_BYTES, BASE_NONE, NULL, 0,
525
15
                    "ROS Message Header", HFILL } },
526
15
    { &hf_tcpros_message_header_seq,            { "Sequence ID", "tcpros.message.header.seq",
527
15
                    FT_UINT32, BASE_DEC, NULL, 0,
528
15
                    "ROS Message Header Sequence", HFILL } },
529
15
    { &hf_tcpros_message_header_stamp,          { "Timestamp", "tcpros.message.header.stamp",
530
15
                    FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0,
531
15
                    "ROS Message Header Stamp", HFILL } },
532
15
    { &hf_tcpros_message_header_stamp_sec,      { "Seconds", "tcpros.message.header.stamp.sec",
533
15
                    FT_UINT32, BASE_DEC, NULL, 0,
534
15
                    "ROS Message Header Stamp Sec", HFILL } },
535
15
    { &hf_tcpros_message_header_stamp_nsec,     { "Nanoseconds", "tcpros.message.header.stamp.nsec",
536
15
                    FT_UINT32, BASE_DEC, NULL, 0,
537
15
                    "ROS Message Header Stamp NSec", HFILL } },
538
539
15
    { &hf_tcpros_message_header_frame,          { "Frame ID", "tcpros.message.header.frame",
540
15
                    FT_UINT_STRING, BASE_NONE, NULL, 0,
541
15
                    "ROS Message Header Frame ID", HFILL } },
542
15
    { &hf_tcpros_message_header_frame_length,   { "Length", "tcpros.message.header.frame.len",
543
15
                    FT_UINT32, BASE_DEC, NULL, 0,
544
15
                    "ROS Message Header Frame ID Length", HFILL } },
545
15
    { &hf_tcpros_message_header_frame_value,     { "Value", "tcpros.message.header.frame.value",
546
15
                     FT_STRING, BASE_NONE, NULL, 0,
547
15
                     "ROS Message Header Frame ID Value", HFILL } },
548
549
15
    { &hf_tcpros_message_payload,               { "Payload", "tcpros.message.payload",
550
15
                    FT_BYTES, BASE_NONE, NULL, 0,
551
15
                    "ROS Message Packet Payload", HFILL } },
552
553
15
  };
554
555
15
  static int *ett[] = {
556
15
    &ett_tcpros,
557
15
  };
558
559
15
  module_t *tcpros_module;
560
561
15
  proto_tcpros = proto_register_protocol("TCP based Robot Operating System protocol (TCPROS)", "TCPROS", "tcpros");
562
563
15
  proto_register_field_array(proto_tcpros, hf, array_length(hf));
564
15
  proto_register_subtree_array(ett, array_length(ett));
565
566
15
  tcpros_handle = register_dissector("tcpros", dissect_tcpros, proto_tcpros);
567
568
15
  tcpros_module = prefs_register_protocol(proto_tcpros, NULL);
569
570
15
  prefs_register_bool_preference(tcpros_module, "desegment_tcpros_messages",
571
15
               "Reassemble TCPROS messages spanning multiple TCP segments",
572
15
               "Whether the TCPROS dissector should reassemble messages spanning multiple TCP segments."
573
15
               " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
574
15
               &tcpros_desegment);
575
576
577
15
}
578
579
/* Heuristics test */
580
static bool
581
test_tcpros(packet_info *pinfo, tvbuff_t *tvb, unsigned offset, void *data _U_)
582
0
{
583
0
  if (tvb_captured_length(tvb) < 8)
584
0
    return false;
585
586
0
  if( is_rosclock(tvb, pinfo, offset) )
587
0
    return true;
588
0
  if( is_rosmsg(tvb, pinfo, offset) )
589
0
    return true;
590
0
  if( is_rosconnection_header(tvb, pinfo, offset) )
591
0
    return true;
592
593
0
  return false;
594
0
}
595
596
static bool
597
dissect_tcpros_heur_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
598
0
{
599
0
  conversation_t *conversation;
600
601
0
  if (!test_tcpros(pinfo, tvb, 0, data))
602
0
    return false;
603
604
0
  conversation = find_or_create_conversation(pinfo);
605
0
  conversation_set_dissector(conversation, tcpros_handle);
606
607
0
  dissect_tcpros(tvb, pinfo, tree, data);
608
609
0
  return true;
610
0
}
611
612
613
614
void
615
proto_reg_handoff_tcpros(void)
616
15
{
617
15
  dissector_add_for_decode_as_with_preference("tcp.port", tcpros_handle);   /* for "decode-as" */
618
619
  /* register as heuristic dissector */
620
15
  heur_dissector_add("tcp", dissect_tcpros_heur_tcp, "TCPROS over TCP",
621
15
        "tcpros_tcp", proto_tcpros, HEURISTIC_DISABLE);
622
15
}
623
624
625
626
/*
627
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
628
 *
629
 * Local variables:
630
 * c-basic-offset: 8
631
 * tab-width: 8
632
 * indent-tabs-mode: t
633
 * End:
634
 *
635
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
636
 * :indentSize=8:tabSize=8:noTabs=false:
637
 */