Coverage Report

Created: 2026-06-30 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-rk512.c
Line
Count
Source
1
/* packet-rk512.c
2
 * Routines for RK 512 protocol dissection
3
 * Copyright 2022 Michael Mann
4
 *
5
 * SPDX-License-Identifier: GPL-2.0-or-later
6
 */
7
8
#include "config.h"
9
10
#include <epan/packet.h>
11
#include <epan/expert.h>
12
#include <epan/dissectors/packet-tcp.h>
13
#include <wsutil/crc16.h>
14
15
void proto_register_rk512(void);
16
void proto_reg_handoff_rk512(void);
17
18
static int proto_rk512;
19
20
//static int hf_rk512_garbage_data;
21
static int hf_rk512_reply_header;
22
static int hf_rk512_data_block_type;
23
static int hf_rk512_size;
24
static int hf_rk512_coordination_flag;
25
static int hf_rk512_device_code;
26
static int hf_rk512_protocol_version;
27
static int hf_rk512_status;
28
static int hf_rk512_scan_number;
29
static int hf_rk512_telegram_number;
30
static int hf_rk512_data_type;
31
static int hf_rk512_measurement_data_type;
32
static int hf_rk512_measurement_data;
33
static int hf_rk512_measurement_data_distance;
34
static int hf_rk512_measurement_data_flags;
35
static int hf_rk512_checksum;
36
static int hf_rk512_checksum_status;
37
38
static int ett_rk512;
39
static int ett_rk512_measurement_data;
40
static int ett_rk512_measurement_data_value;
41
static int ett_rk512_continuous_data;
42
43
static expert_field ei_rk512_reply_header;
44
static expert_field ei_rk512_data_type;
45
static expert_field ei_rk512_checksum;
46
47
//Preferences
48
static unsigned rk512_num_measurements_pts = 541;
49
50
1.67k
#define RK512_HEADER_SIZE   4
51
162
#define RK512_CRC_SIZE   2
52
//Used to find the start of packets
53
static const uint8_t HEADER_SEQUENCE[RK512_HEADER_SIZE] = { 0, 0, 0, 0 };
54
static tvbuff_t* tvb_header_signature = NULL;
55
56
13
#define MEASUREMENT_DATA    0xBBBB
57
0
#define REFLECTOR_DATA      0xCCCC
58
59
60
static const value_string device_code_vals[] = {
61
  { 7, "Host" },
62
  { 8, "Guest" },
63
  { 0, NULL }
64
};
65
66
static const value_string status_vals[] = {
67
  { 0, "Normal" },
68
  { 1, "Lockout" },
69
  { 0, NULL }
70
};
71
72
876
#define BLOCKNUM_CONTINUOUSDATA   0x0000    // 0
73
0
#define BLOCKNUM_SCID       0x0017    // 23
74
6
#define BLOCKNUM_TOKEN        0x0019    // 25
75
0
#define BLOCKNUM_DATAMODE     0x0067    // 103
76
77
static const value_string data_block_type_vals[] = {
78
  { BLOCKNUM_CONTINUOUSDATA, "Continuous Data" },
79
  { BLOCKNUM_SCID, "SCID" },
80
  { BLOCKNUM_TOKEN, "Token" },
81
  { BLOCKNUM_DATAMODE, "Data Mode" },
82
  { 0, NULL }
83
};
84
85
static const value_string datatype_vals[] = {
86
  { MEASUREMENT_DATA, "Measurement data" },
87
  { REFLECTOR_DATA, "Reflector data" },
88
  { 0, NULL }
89
};
90
91
static int* const rk512_measurement_data_fields[] = {
92
  &hf_rk512_measurement_data_distance,
93
  &hf_rk512_measurement_data_flags,
94
  NULL,
95
};
96
97
static unsigned
98
get_rk512_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
99
162
{
100
162
  uint32_t len = 0;
101
102
162
  len = tvb_get_ntohs(tvb, offset + RK512_HEADER_SIZE + 2);  //length in words
103
104
  //Yes, this is a horrible hack but works with the captures seen
105
162
  if ((rk512_num_measurements_pts == 381) && (len == 392))
106
0
    len = 390;
107
108
162
  return ((len*2) + RK512_HEADER_SIZE + 2 + RK512_CRC_SIZE);
109
162
}
110
111
static int
112
dissect_rk512_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
113
161
{
114
161
  proto_tree    *rk512_tree, *continous_data_tree, *measurement_tree;
115
161
  proto_item    *ti, *header_item, *continous_data_item, *data_item;
116
161
  int       offset = 0, start_offset;
117
161
  uint32_t      tag, block_type, data_type, sub_data_type, size;
118
119
161
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "RK512");
120
161
  col_clear(pinfo->cinfo, COL_INFO);
121
122
161
  ti = proto_tree_add_item(tree, proto_rk512, tvb, 0, -1, ENC_NA);
123
161
  rk512_tree = proto_item_add_subtree(ti, ett_rk512);
124
125
161
  header_item = proto_tree_add_item_ret_uint(rk512_tree, hf_rk512_reply_header, tvb, offset, 4, ENC_BIG_ENDIAN, &tag);
126
161
  offset += 4;
127
161
  if (tag != 0)
128
44
    expert_add_info(pinfo, header_item, &ei_rk512_reply_header);
129
130
161
  proto_tree_add_item_ret_uint(rk512_tree, hf_rk512_data_block_type, tvb, offset, 2, ENC_BIG_ENDIAN, &block_type);
131
161
  offset += 2;
132
133
161
  col_set_str(pinfo->cinfo, COL_INFO, val_to_str_const(block_type, data_block_type_vals, "Unknown"));
134
135
161
  switch (block_type)
136
161
  {
137
121
  case BLOCKNUM_CONTINUOUSDATA:
138
121
    start_offset = offset;
139
121
    continous_data_tree = proto_tree_add_subtree(rk512_tree, tvb, offset, -1, ett_rk512_continuous_data, &continous_data_item, "Continuous Data");
140
141
121
    proto_tree_add_item_ret_uint(continous_data_tree, hf_rk512_size, tvb, offset, 2, ENC_BIG_ENDIAN, &size);
142
121
    size *= 2;    //size is in words
143
121
    offset += 2;
144
121
    proto_tree_add_item(continous_data_tree, hf_rk512_coordination_flag, tvb, offset, 1, ENC_BIG_ENDIAN);
145
121
    offset += 1;
146
121
    proto_tree_add_item(continous_data_tree, hf_rk512_device_code, tvb, offset, 1, ENC_BIG_ENDIAN);
147
121
    offset += 1;
148
121
    proto_tree_add_item(continous_data_tree, hf_rk512_protocol_version, tvb, offset, 2, ENC_BIG_ENDIAN);
149
121
    offset += 2;
150
121
    proto_tree_add_item(continous_data_tree, hf_rk512_status, tvb, offset, 2, ENC_BIG_ENDIAN);
151
121
    offset += 2;
152
121
    proto_tree_add_item(continous_data_tree, hf_rk512_scan_number, tvb, offset, 4, ENC_LITTLE_ENDIAN);
153
121
    offset += 4;
154
121
    proto_tree_add_item(continous_data_tree, hf_rk512_telegram_number, tvb, offset, 2, ENC_LITTLE_ENDIAN);
155
121
    offset += 2;
156
121
    data_item = proto_tree_add_item_ret_uint(continous_data_tree, hf_rk512_data_type, tvb, offset, 2, ENC_LITTLE_ENDIAN, &data_type);
157
121
    offset += 2;
158
159
121
    switch (data_type)
160
121
    {
161
13
    case MEASUREMENT_DATA:
162
13
    {
163
      //Yes, this is a horrible hack but works with the captures seen
164
13
      if ((rk512_num_measurements_pts == 381) && (size == 784))
165
0
        size = 780;
166
167
13
      uint8_t* crc_data;
168
13
      proto_tree_add_item_ret_uint(continous_data_tree, hf_rk512_measurement_data_type, tvb, offset, 2, ENC_LITTLE_ENDIAN, &sub_data_type);
169
13
      offset += 2;
170
13
      measurement_tree = proto_tree_add_subtree(continous_data_tree, tvb, offset, rk512_num_measurements_pts * 2, ett_rk512_measurement_data, NULL, "Measurement Data");
171
1.42k
      for (unsigned i = 0; i < rk512_num_measurements_pts; i++)
172
1.41k
      {
173
1.41k
        proto_tree_add_bitmask(measurement_tree, tvb, offset, hf_rk512_measurement_data, ett_rk512_measurement_data_value, rk512_measurement_data_fields, ENC_BIG_ENDIAN);
174
1.41k
        offset += 2;
175
1.41k
      }
176
177
      //Create data to compute the CRC
178
13
      crc_data = (uint8_t*)wmem_alloc(pinfo->pool, size + 2);
179
      //start with a word with value 0
180
13
      crc_data[0] = 0;
181
13
      crc_data[1] = 0;
182
      //copy the rest of the packet
183
13
      tvb_memcpy(tvb, &crc_data[2], start_offset, size);
184
185
13
      proto_tree_add_checksum(rk512_tree, tvb, offset,
186
13
        hf_rk512_checksum, hf_rk512_checksum_status, &ei_rk512_checksum, pinfo,
187
13
        crc16_x25_ccitt_seed(crc_data, size + 2, 0xFFFF), ENC_LITTLE_ENDIAN, PROTO_CHECKSUM_VERIFY);
188
13
      offset += 2;
189
13
      break;
190
0
    }
191
0
    case REFLECTOR_DATA:
192
0
      break;
193
5
    default:
194
5
      expert_add_info(pinfo, data_item, &ei_rk512_data_type);
195
5
      break;
196
121
    }
197
198
5
    proto_item_set_len(continous_data_item, offset - start_offset);
199
5
    break;
200
0
  case BLOCKNUM_SCID:
201
0
    break;
202
6
  case BLOCKNUM_TOKEN:
203
6
    break;
204
0
  case BLOCKNUM_DATAMODE:
205
0
    break;
206
161
  }
207
208
45
  return tvb_captured_length(tvb);
209
161
}
210
211
static int
212
dissect_rk512(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
213
24
{
214
24
  tcp_dissect_pdus(tvb, pinfo, tree, true, 8, get_rk512_pdu_len, dissect_rk512_pdu, data);
215
24
  return tvb_captured_length(tvb);
216
24
}
217
218
static bool
219
dissect_rk512_heur(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data _U_)
220
2.20k
{
221
2.20k
  unsigned signature_start, offset;
222
2.20k
  uint16_t block_type;
223
2.20k
  tvbuff_t* rk512_tvb;
224
225
2.20k
  if (!tvb_find_tvb_remaining(tvb, tvb_header_signature, 0, &signature_start)) {
226
856
    return false;
227
856
  }
228
229
  //keep track of the offset for verification of upcoming fields
230
1.34k
  offset = signature_start + RK512_HEADER_SIZE;
231
232
  //Make sure there are enough bytes for the rest of the field checks
233
1.34k
  if (tvb_captured_length_remaining(tvb, offset) < 2)
234
32
    return false;
235
236
  //Make sure it's supported block type
237
1.31k
  block_type = tvb_get_ntohs(tvb, offset);
238
1.31k
  if (try_val_to_str(block_type, data_block_type_vals) == NULL)
239
560
    return false;
240
241
755
  offset += 2;
242
755
  if (block_type == BLOCKNUM_CONTINUOUSDATA)
243
749
  {
244
749
    uint16_t datatype;
245
749
    if (tvb_captured_length_remaining(tvb, offset) < 18)
246
169
      return false;
247
248
580
    datatype = tvb_get_ntohs(tvb, offset+14);
249
580
    if (try_val_to_str(datatype, datatype_vals) == NULL)
250
562
      return false;
251
580
  }
252
253
24
  rk512_tvb = tvb_new_subset_remaining(tvb, signature_start);
254
24
  dissect_rk512(rk512_tvb, pinfo, tree, data);
255
24
  return true;
256
755
}
257
258
static void
259
rk512_shutdown(void)
260
0
{
261
0
  tvb_free(tvb_header_signature);
262
0
}
263
264
static void
265
rk512_fmt_version( char *result, uint32_t revision )
266
0
{
267
0
    snprintf( result, ITEM_LABEL_LENGTH, "%d.%d",
268
0
    (uint8_t)(revision & 0xFF), (uint8_t)(( revision & 0xFF00 ) >> 8));
269
0
}
270
271
void
272
proto_register_rk512(void)
273
14
{
274
14
  expert_module_t* expert_rk512;
275
276
14
  static hf_register_info hf[] = {
277
    //{ &hf_rk512_garbage_data,
278
    //  { "Garbage Data", "rk512.garbage_data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
279
14
    { &hf_rk512_reply_header,
280
14
      { "Reply Header", "rk512.reply_header", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
281
14
    { &hf_rk512_data_block_type,
282
14
      { "Data Block Type", "rk512.data_block_type", FT_UINT16, BASE_DEC, VALS(data_block_type_vals), 0x0, NULL, HFILL } },
283
14
    { &hf_rk512_size,
284
14
      { "Message Size", "rk512.size", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
285
14
    { &hf_rk512_coordination_flag,
286
14
      { "Coordination Flag", "rk512.coordination_flag", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } },
287
14
    { &hf_rk512_device_code,
288
14
      { "Device Code", "rk512.device_code", FT_UINT8, BASE_HEX, VALS(device_code_vals), 0x0, NULL, HFILL } },
289
14
    { &hf_rk512_protocol_version,
290
14
      { "Protocol Version", "rk512.protocol_version.version", FT_UINT16, BASE_CUSTOM, CF_FUNC(rk512_fmt_version), 0x0, NULL, HFILL } },
291
14
    { &hf_rk512_status,
292
14
      { "Status", "rk512.status", FT_UINT16, BASE_HEX, VALS(status_vals), 0x0, NULL, HFILL } },
293
14
    { &hf_rk512_scan_number,
294
14
      { "Scan number", "rk512.scan_number", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
295
14
    { &hf_rk512_telegram_number,
296
14
      { "Telegram number", "rk512.telegram_number", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
297
14
    { &hf_rk512_data_type,
298
14
      { "Data type", "rk512.data_type", FT_UINT16, BASE_HEX, VALS(datatype_vals), 0x0, NULL, HFILL } },
299
14
    { &hf_rk512_measurement_data_type,
300
14
      { "Measurement datatype", "rk512.measurement.data_type", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
301
14
    { &hf_rk512_measurement_data,
302
14
      { "Measurement data", "rk512.measurement.data", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
303
14
    { &hf_rk512_measurement_data_distance,
304
14
      { "Distance", "rk512.measurement.data.distance", FT_UINT16, BASE_DEC, NULL, 0x1FFF, NULL, HFILL } },
305
14
    { &hf_rk512_measurement_data_flags,
306
14
      { "Flags", "rk512.measurement.data.flags", FT_UINT16, BASE_HEX, NULL, 0xE000, NULL, HFILL } },
307
14
    { &hf_rk512_checksum,
308
14
      { "Checksum", "rk512.checksum", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
309
14
    { &hf_rk512_checksum_status,
310
14
      { "CRC Status", "rk512.checksum_status", FT_UINT8, BASE_NONE, VALS(proto_checksum_vals), 0x0, NULL, HFILL } },
311
312
14
  };
313
314
14
  static int *ett[] = {
315
14
    &ett_rk512,
316
14
    &ett_rk512_continuous_data,
317
14
    &ett_rk512_measurement_data,
318
14
    &ett_rk512_measurement_data_value
319
14
  };
320
321
14
  static ei_register_info ei[] = {
322
14
    { &ei_rk512_reply_header, { "rk512.reply_header.not_zero", PI_PROTOCOL, PI_WARN, "Reply header not zero", EXPFILL }},
323
14
    { &ei_rk512_data_type, { "rk512.data_type.unknown", PI_PROTOCOL, PI_WARN, "Unknown data type", EXPFILL }},
324
14
    { &ei_rk512_checksum, { "rk512.checksum.incorrect", PI_CHECKSUM, PI_WARN, "Checksum incorrect", EXPFILL }},
325
14
  };
326
327
328
14
  proto_rk512 = proto_register_protocol("SICK RK512", "RK512", "rk512");
329
14
  proto_register_field_array(proto_rk512, hf, array_length(hf));
330
14
  proto_register_subtree_array(ett, array_length(ett));
331
14
  expert_rk512 = expert_register_protocol(proto_rk512);
332
14
  expert_register_field_array(expert_rk512, ei, array_length(ei));
333
334
14
  module_t* rk512_module = prefs_register_protocol(proto_rk512, NULL);
335
14
  prefs_register_uint_preference(rk512_module, "num_measurement_pts",
336
14
      "Number of measurement points",
337
14
      "Number of measurement points within the packet",
338
14
      10, &rk512_num_measurements_pts);
339
340
14
  tvb_header_signature = tvb_new_real_data(HEADER_SEQUENCE, sizeof(HEADER_SEQUENCE), sizeof(HEADER_SEQUENCE));
341
342
14
  register_shutdown_routine(rk512_shutdown);
343
14
}
344
345
void
346
proto_reg_handoff_rk512(void)
347
14
{
348
14
  dissector_handle_t rk512_handle;
349
350
14
  rk512_handle = create_dissector_handle(dissect_rk512, proto_rk512);
351
14
  dissector_add_for_decode_as("tcp.port", rk512_handle);
352
14
  heur_dissector_add("tcp", dissect_rk512_heur, "RK512 over TCP", "rk512_tcp", proto_rk512, HEURISTIC_ENABLE);
353
14
}
354
355
/*
356
* Editor modelines  -  http://www.wireshark.org/tools/modelines.html
357
*
358
* Local variables:
359
* c-basic-offset: 4
360
* tab-width: 8
361
* indent-tabs-mode: t
362
* End:
363
*
364
* vi: set shiftwidth=4 tabstop=8 expandtab:
365
* :indentSize=4:tabSize=8:noTabs=false:
366
*/