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-psn.c
Line
Count
Source
1
/* packet-psn.c
2
 * Routines for PSN packet disassembly
3
 *
4
 * Copyright (c) 2025 by Matt Morris <mattm.dev.1[AT]gmail.com>
5
 *
6
 * Wireshark - Network traffic analyzer
7
 * By Gerald Combs <gerald@wireshark.org>
8
 * Copyright 1999 Gerald Combs
9
 *
10
 * Specification:
11
 * https://github.com/vyv/psn-cpp/blob/master/doc/PosiStageNetprotocol_v2.03_2019_09_09.pdf
12
 * https://posistage.net/wp-content/uploads/2019/01/PosiStageNetprotocol_v2.02_2016_09_15.pdf
13
 * https://posistage.net/wp-content/uploads/2018/07/PosiStageNetprotocolv1.7.pdf
14
 *
15
 * SPDX-License-Identifier: GPL-2.0-or-later
16
 */
17
18
19
/* Include files */
20
#include "config.h"
21
#include <epan/packet.h>
22
#include <epan/unit_strings.h>
23
#include <epan/tfs.h>
24
#include <epan/expert.h>
25
26
/* constants */
27
0
#define PSN_INFO_PACKET    0x6756
28
0
#define PSN_DATA_PACKET    0x6755
29
0
#define PSN_V1_INFO_PACKET 0x503c
30
0
#define PSN_V1_DATA_PACKET 0x6754
31
32
0
#define PSN_INFO_PACKET_HEADER 0x0000
33
0
#define PSN_INFO_SYSTEM_NAME   0x0001
34
0
#define PSN_INFO_TRACKER_LIST  0x0002
35
36
0
#define PSN_INFO_TRACKER_NAME  0x0000
37
38
0
#define PSN_DATA_PACKET_HEADER 0x0000
39
0
#define PSN_DATA_TRACKER_LIST  0x0001
40
41
0
#define PSN_DATA_TRACKER_POS       0x0000
42
0
#define PSN_DATA_TRACKER_SPEED     0x0001
43
0
#define PSN_DATA_TRACKER_ORI       0x0002
44
0
#define PSN_DATA_TRACKER_STATUS    0x0003
45
0
#define PSN_DATA_TRACKER_ACCEL     0x0004
46
0
#define PSN_DATA_TRACKER_TRGTPOS   0x0005
47
0
#define PSN_DATA_TRACKER_TIMESTAMP 0x0006
48
49
50
static int proto_psn;
51
52
static dissector_handle_t psn_handle;
53
54
static dissector_handle_t xml_dissector_handle;
55
56
/*  Open/Close trees */
57
static int ett_psn;
58
59
static int ett_psn_info_chunk;
60
static int ett_psn_data_chunk;
61
static int ett_psn_tracker_chunk;
62
static int ett_psn_tracker_info_chunk;
63
static int ett_psn_tracker_data_chunk;
64
65
static int ett_psn_chunk_data_field;
66
67
static int ett_psn_v1_header;
68
static int ett_psn_v1_tracker;
69
70
/* Expert Info  */
71
static expert_field ei_psn_chunk_id;
72
static expert_field ei_psn_chunk_len;
73
74
/*  Register fields */
75
static int hf_psn_info_chunk;
76
static int hf_psn_data_chunk;
77
static int hf_psn_tracker_chunk;
78
static int hf_psn_tracker_info_chunk;
79
static int hf_psn_tracker_data_chunk;
80
81
static int hf_psn_base_chunk_id;
82
static int hf_psn_info_chunk_id;
83
static int hf_psn_data_chunk_id;
84
static int hf_psn_tracker_id;
85
static int hf_psn_tracker_data_chunk_id;
86
static int hf_psn_tracker_info_chunk_id;
87
88
static int hf_psn_chunk_data_field;
89
static int hf_psn_chunk_length;
90
static int hf_psn_chunk_has_subchunks;
91
92
static int hf_psn_packet_timestamp;
93
static int hf_psn_version_high;
94
static int hf_psn_version_low;
95
static int hf_psn_frame_id;
96
static int hf_psn_frame_packet_count;
97
98
static int hf_psn_system_name;
99
static int hf_psn_tracker_name;
100
101
static int hf_psn_position_x;
102
static int hf_psn_position_y;
103
static int hf_psn_position_z;
104
static int hf_psn_speed_x;
105
static int hf_psn_speed_y;
106
static int hf_psn_speed_z;
107
static int hf_psn_ori_x;
108
static int hf_psn_ori_y;
109
static int hf_psn_ori_z;
110
static int hf_psn_accel_x;
111
static int hf_psn_accel_y;
112
static int hf_psn_accel_z;
113
static int hf_psn_trgtpos_x;
114
static int hf_psn_trgtpos_y;
115
static int hf_psn_trgtpos_z;
116
117
static int hf_psn_validity;
118
static int hf_psn_tracker_timestamp;
119
120
/* V1 only fields */
121
static int hf_psn_v1_header;
122
static int hf_psn_v1_identifier;
123
static int hf_psn_v1_tracker;
124
static int hf_psn_v1_position;
125
static int hf_psn_v1_velocity;
126
static int hf_psn_v1_packet_counter;
127
static int hf_psn_v1_world_id;
128
static int hf_psn_v1_tracker_count;
129
static int hf_psn_v1_frame_index;
130
static int hf_psn_v1_object_state;
131
static int hf_psn_v1_info_xml;
132
static int hf_psn_v1_reserved;
133
134
static int * const chunk_data_fields[] = {
135
    &hf_psn_chunk_length,
136
    &hf_psn_chunk_has_subchunks,
137
    NULL
138
};
139
140
141
static const value_string psn_base_chunk_id_names[] = {
142
  { PSN_INFO_PACKET,    "Information Packet" },
143
  { PSN_DATA_PACKET,    "Data Packet" },
144
  { PSN_V1_INFO_PACKET, "V1 Information Packet" },
145
  { PSN_V1_DATA_PACKET, "V1 Data Packet" },
146
  { 0,                         NULL },
147
};
148
149
static const value_string psn_info_chunk_id_names[] = {
150
  { PSN_INFO_PACKET_HEADER, "Packet Header" },
151
  { PSN_INFO_SYSTEM_NAME,   "System Name" },
152
  { PSN_INFO_TRACKER_LIST,  "Tracker List" },
153
  { 0,                      NULL },
154
};
155
156
static const value_string psn_tracker_info_chunk_id_names[] = {
157
  { PSN_INFO_TRACKER_NAME, "Tracker Name" },
158
  { 0,                      NULL },
159
};
160
161
static const value_string psn_data_chunk_id_names[] = {
162
  { PSN_DATA_PACKET_HEADER, "Packet Header" },
163
  { PSN_DATA_TRACKER_LIST,  "Tracker List" },
164
  { 0,                      NULL },
165
};
166
167
static const value_string psn_tracker_data_chunk_id_names[] = {
168
  { PSN_DATA_TRACKER_POS,       "Tracker Position" },
169
  { PSN_DATA_TRACKER_SPEED,     "Tracker Speed" },
170
  { PSN_DATA_TRACKER_ORI,       "Tracker Origin" },
171
  { PSN_DATA_TRACKER_STATUS,    "Tracker Status" },
172
  { PSN_DATA_TRACKER_ACCEL,     "Tracker Acceleration" },
173
  { PSN_DATA_TRACKER_TRGTPOS,   "Tracker Target Position" },
174
  { PSN_DATA_TRACKER_TIMESTAMP, "Tracker Timestamp" },
175
  { 0,                          NULL },
176
};
177
static const value_string psn_tracker_data_names[] = {
178
  { PSN_DATA_TRACKER_POS,       "Position" },
179
  { PSN_DATA_TRACKER_SPEED,     "Speed" },
180
  { PSN_DATA_TRACKER_ORI,       "Origin" },
181
  { PSN_DATA_TRACKER_STATUS,    "Status" },
182
  { PSN_DATA_TRACKER_ACCEL,     "Acceleration" },
183
  { PSN_DATA_TRACKER_TRGTPOS,   "Target Position" },
184
  { PSN_DATA_TRACKER_TIMESTAMP, "Timestamp" },
185
  { 0,                          NULL },
186
};
187
188
static const value_string psn_v1_identifier[] = {
189
  { 0x6754, "Start of Packet" },
190
  { 0x4576, "End of Packet" },
191
  { 0,      NULL },
192
};
193
194
195
196
/******************************************************************************/
197
/* Dissect protocol                                                           */
198
199
0
static proto_tree* dissect_psn_chunk_header(tvbuff_t *tvb, proto_tree *tree, proto_item** ti, unsigned* offset, unsigned* chunk_end, uint32_t* chunk_id, uint16_t* chunk_data_len, const int hf_chunk, const int ett_chunk, const int hf_chunk_id) {
200
0
    *ti = proto_tree_add_item(tree, hf_chunk, tvb, *offset, -1, ENC_NA);
201
0
    proto_tree *chunk_tree = proto_item_add_subtree(*ti, ett_chunk);
202
203
    /* Chunk Header */
204
0
    proto_tree_add_item_ret_uint(chunk_tree, hf_chunk_id, tvb, *offset, 2, ENC_LITTLE_ENDIAN, chunk_id);
205
0
    *offset+=2;
206
207
0
    *chunk_data_len = tvb_get_uint16(tvb, *offset, ENC_LITTLE_ENDIAN) & 0x7FFF;
208
0
    proto_item_set_len(*ti, (*chunk_data_len)+4);
209
0
    proto_tree_add_bitmask_with_flags(chunk_tree, tvb, *offset, hf_psn_chunk_data_field, ett_psn_chunk_data_field, chunk_data_fields, ENC_LITTLE_ENDIAN, BMT_NO_FLAGS);
210
0
    *offset+=2;
211
0
    *chunk_end = (*offset)+(*chunk_data_len);
212
213
0
    return chunk_tree;
214
0
}
215
216
0
static int dissect_psn_data_tracker(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset) {
217
0
    unsigned chunk_end;
218
0
    uint32_t track_id;
219
0
    uint16_t track_data_len;
220
0
    proto_item *track_item;
221
0
    proto_tree *track_tree = dissect_psn_chunk_header(tvb, tree, &track_item, &offset, &chunk_end,
222
0
        &track_id, &track_data_len, hf_psn_tracker_data_chunk, ett_psn_tracker_data_chunk, hf_psn_tracker_data_chunk_id);
223
224
0
    const char* tracker_name = val_to_str_const(track_id, psn_tracker_data_names, "Unknown");
225
0
    proto_item_append_text(track_item, ": %s", tracker_name);
226
227
0
    switch (track_id) {
228
0
        case PSN_DATA_TRACKER_POS:
229
0
            proto_tree_add_item(track_tree, hf_psn_position_x, tvb, offset, 4, ENC_LITTLE_ENDIAN);
230
0
            offset+=4;
231
0
            proto_tree_add_item(track_tree, hf_psn_position_y, tvb, offset, 4, ENC_LITTLE_ENDIAN);
232
0
            offset+=4;
233
0
            proto_tree_add_item(track_tree, hf_psn_position_z, tvb, offset, 4, ENC_LITTLE_ENDIAN);
234
0
            offset+=4;
235
0
            break;
236
0
        case PSN_DATA_TRACKER_SPEED:
237
0
            proto_tree_add_item(track_tree, hf_psn_speed_x, tvb, offset, 4, ENC_LITTLE_ENDIAN);
238
0
            offset+=4;
239
0
            proto_tree_add_item(track_tree, hf_psn_speed_y, tvb, offset, 4, ENC_LITTLE_ENDIAN);
240
0
            offset+=4;
241
0
            proto_tree_add_item(track_tree, hf_psn_speed_z, tvb, offset, 4, ENC_LITTLE_ENDIAN);
242
0
            offset+=4;
243
0
            break;
244
0
        case PSN_DATA_TRACKER_ORI:
245
0
            proto_tree_add_item(track_tree, hf_psn_ori_x, tvb, offset, 4, ENC_LITTLE_ENDIAN);
246
0
            offset+=4;
247
0
            proto_tree_add_item(track_tree, hf_psn_ori_y, tvb, offset, 4, ENC_LITTLE_ENDIAN);
248
0
            offset+=4;
249
0
            proto_tree_add_item(track_tree, hf_psn_ori_z, tvb, offset, 4, ENC_LITTLE_ENDIAN);
250
0
            offset+=4;
251
0
            break;
252
0
        case PSN_DATA_TRACKER_STATUS:
253
0
            proto_tree_add_item(track_tree, hf_psn_validity, tvb, offset, 4, ENC_LITTLE_ENDIAN);
254
0
            offset+=4;
255
0
            break;
256
0
        case PSN_DATA_TRACKER_ACCEL:
257
0
            proto_tree_add_item(track_tree, hf_psn_accel_x, tvb, offset, 4, ENC_LITTLE_ENDIAN);
258
0
            offset+=4;
259
0
            proto_tree_add_item(track_tree, hf_psn_accel_y, tvb, offset, 4, ENC_LITTLE_ENDIAN);
260
0
            offset+=4;
261
0
            proto_tree_add_item(track_tree, hf_psn_accel_z, tvb, offset, 4, ENC_LITTLE_ENDIAN);
262
0
            offset+=4;
263
0
            break;
264
0
        case PSN_DATA_TRACKER_TRGTPOS:
265
0
            proto_tree_add_item(track_tree, hf_psn_trgtpos_x, tvb, offset, 4, ENC_LITTLE_ENDIAN);
266
0
            offset+=4;
267
0
            proto_tree_add_item(track_tree, hf_psn_trgtpos_y, tvb, offset, 4, ENC_LITTLE_ENDIAN);
268
0
            offset+=4;
269
0
            proto_tree_add_item(track_tree, hf_psn_trgtpos_z, tvb, offset, 4, ENC_LITTLE_ENDIAN);
270
0
            offset+=4;
271
0
            break;
272
0
        case PSN_DATA_TRACKER_TIMESTAMP:
273
0
            proto_tree_add_item(track_tree, hf_psn_tracker_timestamp, tvb, offset, 8, ENC_LITTLE_ENDIAN|ENC_TIME_USECS);
274
0
            offset+=8;
275
0
            break;
276
0
        default:
277
            /* Unknown chunk type */
278
0
            expert_add_info(pinfo, track_tree, &ei_psn_chunk_id);
279
0
            return chunk_end;
280
0
    }
281
282
0
    if (offset != chunk_end)
283
0
        expert_add_info(pinfo, track_tree, &ei_psn_chunk_len);
284
0
    return chunk_end;
285
0
}
286
287
0
static int dissect_psn_data_tracker_list(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset) {
288
0
    unsigned chunk_end;
289
0
    uint32_t tracker_id;
290
0
    uint16_t chunk_data_len;
291
0
    proto_item *chunk_item;
292
0
    proto_tree *chunk_tree = dissect_psn_chunk_header(tvb, tree, &chunk_item, &offset, &chunk_end,
293
0
        &tracker_id, &chunk_data_len, hf_psn_tracker_chunk, ett_psn_tracker_chunk, hf_psn_tracker_id);
294
295
0
    proto_item_append_text(chunk_item, ", tracker ID: %u", tracker_id);
296
297
0
    while (offset < chunk_end) {
298
0
        offset = dissect_psn_data_tracker(tvb, pinfo, chunk_tree, offset);
299
0
    }
300
301
0
    if (offset != chunk_end)
302
0
        expert_add_info(pinfo, chunk_tree, &ei_psn_chunk_len);
303
0
    return chunk_end;
304
0
}
305
306
0
static int dissect_psn_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset) {
307
0
    unsigned chunk_end;
308
0
    uint32_t chunk_id;
309
0
    uint16_t chunk_data_len;
310
0
    proto_item *chunk_item;
311
0
    proto_tree *chunk_tree = dissect_psn_chunk_header(tvb, tree, &chunk_item, &offset, &chunk_end,
312
0
        &chunk_id, &chunk_data_len, hf_psn_data_chunk, ett_psn_data_chunk, hf_psn_data_chunk_id);
313
314
0
    switch (chunk_id) {
315
0
        case PSN_DATA_PACKET_HEADER: {
316
0
            proto_tree_add_item(chunk_tree, hf_psn_packet_timestamp, tvb, offset, 8, ENC_LITTLE_ENDIAN|ENC_TIME_USECS);
317
0
            offset+=8;
318
0
            proto_tree_add_item(chunk_tree, hf_psn_version_high, tvb, offset, 1, ENC_LITTLE_ENDIAN);
319
0
            offset+=1;
320
0
            proto_tree_add_item(chunk_tree, hf_psn_version_low, tvb, offset, 1, ENC_LITTLE_ENDIAN);
321
0
            offset+=1;
322
0
            uint32_t frame_id;
323
0
            proto_tree_add_item_ret_uint(chunk_tree, hf_psn_frame_id, tvb, offset, 1, ENC_LITTLE_ENDIAN, &frame_id);
324
0
            offset+=1;
325
0
            proto_tree_add_item(chunk_tree, hf_psn_frame_packet_count, tvb, offset, 1, ENC_LITTLE_ENDIAN);
326
0
            offset+=1;
327
328
0
            col_append_fstr(pinfo->cinfo, COL_INFO, ", Frame %3u", frame_id);
329
0
            proto_item_append_text(chunk_item, ": Header");
330
0
            break;
331
0
        }
332
0
        case PSN_DATA_TRACKER_LIST: {
333
0
            unsigned tracker_count = 0;
334
0
            while (offset < chunk_end) {
335
0
                offset = dissect_psn_data_tracker_list(tvb, pinfo, chunk_tree, offset);
336
0
                tracker_count++;
337
0
            }
338
0
            col_append_fstr(pinfo->cinfo, COL_INFO, ", %u Trackers", tracker_count);
339
0
            proto_item_append_text(chunk_item, ": Tracker List, %u trackers", tracker_count);
340
0
            break;
341
0
        }
342
0
        default:
343
            /* Unknown Chunk Type */
344
0
            expert_add_info(pinfo, chunk_tree, &ei_psn_chunk_id);
345
0
            return chunk_end;
346
0
    }
347
348
0
    if (offset != chunk_end)
349
0
        expert_add_info(pinfo, chunk_tree, &ei_psn_chunk_len);
350
0
    return chunk_end;
351
0
}
352
353
0
static int dissect_psn_info_tracker_list(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset) {
354
0
    unsigned chunk_end;
355
0
    uint32_t tracker_id;
356
0
    uint16_t chunk_data_len;
357
0
    proto_item *chunk_item;
358
0
    proto_tree *chunk_tree = dissect_psn_chunk_header(tvb, tree, &chunk_item, &offset, &chunk_end,
359
0
        &tracker_id, &chunk_data_len, hf_psn_tracker_chunk, ett_psn_tracker_chunk, hf_psn_tracker_id);
360
361
0
    proto_item_append_text(chunk_item, ", tracker ID: %u", tracker_id);
362
363
0
    while (offset < chunk_end) {
364
0
        unsigned name_chunk_end;
365
0
        uint32_t name_id;
366
0
        uint16_t name_data_len;
367
0
        proto_item *name_item;
368
0
        proto_tree *name_tree = dissect_psn_chunk_header(tvb, chunk_tree, &name_item, &offset, &name_chunk_end,
369
0
            &name_id, &name_data_len, hf_psn_tracker_info_chunk, ett_psn_tracker_info_chunk, hf_psn_tracker_info_chunk_id);
370
371
0
        if (name_id == PSN_INFO_TRACKER_NAME) {
372
0
            const uint8_t* tracker_name;
373
0
            proto_tree_add_item_ret_string(name_tree, hf_psn_tracker_name, tvb, offset, name_data_len, ENC_ASCII, pinfo->pool, &tracker_name);
374
375
0
            proto_item_append_text(name_item, ": Tracker Name, %s", tracker_name);
376
0
        }
377
        /* Skip all the data, even if unknown chunk */
378
0
        offset+=name_data_len;
379
0
    }
380
381
0
    if (offset != chunk_end)
382
0
        expert_add_info(pinfo, chunk_tree, &ei_psn_chunk_len);
383
0
    return chunk_end;
384
0
}
385
386
0
static int dissect_psn_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset) {
387
0
    unsigned chunk_end;
388
0
    uint32_t chunk_id;
389
0
    uint16_t chunk_data_len;
390
0
    proto_item *chunk_item;
391
0
    proto_tree *chunk_tree = dissect_psn_chunk_header(tvb, tree, &chunk_item, &offset, &chunk_end,
392
0
        &chunk_id, &chunk_data_len, hf_psn_info_chunk, ett_psn_info_chunk, hf_psn_info_chunk_id);
393
394
0
    switch (chunk_id) {
395
0
        case PSN_INFO_PACKET_HEADER: {
396
0
            proto_tree_add_item(chunk_tree, hf_psn_packet_timestamp, tvb, offset, 8, ENC_LITTLE_ENDIAN|ENC_TIME_USECS);
397
0
            offset+=8;
398
0
            proto_tree_add_item(chunk_tree, hf_psn_version_high, tvb, offset, 1, ENC_LITTLE_ENDIAN);
399
0
            offset+=1;
400
0
            proto_tree_add_item(chunk_tree, hf_psn_version_low, tvb, offset, 1, ENC_LITTLE_ENDIAN);
401
0
            offset+=1;
402
0
            uint32_t frame_id;
403
0
            proto_tree_add_item_ret_uint(chunk_tree, hf_psn_frame_id, tvb, offset, 1, ENC_LITTLE_ENDIAN, &frame_id);
404
0
            offset+=1;
405
0
            proto_tree_add_item(chunk_tree, hf_psn_frame_packet_count, tvb, offset, 1, ENC_LITTLE_ENDIAN);
406
0
            offset+=1;
407
408
0
            col_append_fstr(pinfo->cinfo, COL_INFO, ", Frame %3u", frame_id);
409
0
            proto_item_append_text(chunk_item, ": Header");
410
0
            break;
411
0
        }
412
0
        case PSN_INFO_SYSTEM_NAME: {
413
0
            const uint8_t* name;
414
0
            proto_tree_add_item_ret_string(chunk_tree, hf_psn_system_name, tvb, offset, chunk_data_len, ENC_ASCII, pinfo->pool, &name);
415
0
            offset+=chunk_data_len;
416
417
0
            proto_item_append_text(chunk_item, ": System Name, %s", name);
418
0
            break;
419
0
        }
420
0
        case PSN_INFO_TRACKER_LIST: {
421
0
            unsigned tracker_count = 0;
422
0
            while (offset < chunk_end) {
423
0
                offset = dissect_psn_info_tracker_list(tvb, pinfo, chunk_tree, offset);
424
0
                tracker_count++;
425
0
            }
426
0
            proto_item_append_text(chunk_item, ": Tracker List, %u trackers", tracker_count);
427
0
            col_append_fstr(pinfo->cinfo, COL_INFO, ", %u Trackers", tracker_count);
428
0
            break;
429
0
        }
430
0
        default:
431
            /* Unknown Chunk Type */
432
0
            expert_add_info(pinfo, chunk_tree, &ei_psn_chunk_id);
433
0
            return chunk_end;
434
0
    }
435
436
0
    if (offset != chunk_end)
437
0
        expert_add_info(pinfo, chunk_tree, &ei_psn_chunk_len);
438
0
    return chunk_end;
439
0
}
440
441
0
static int dissect_psn_v1_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset) {
442
0
    proto_tree_add_item(tree, hf_psn_v1_identifier, tvb, offset, 2, ENC_LITTLE_ENDIAN);
443
0
    offset+=2;
444
445
0
    proto_item* header = proto_tree_add_item(tree, hf_psn_v1_header, tvb, offset, 16, ENC_NA);
446
0
    proto_tree* header_tree = proto_item_add_subtree(header, ett_psn_v1_header);
447
448
0
    proto_tree_add_item(header_tree, hf_psn_v1_packet_counter, tvb, offset, 4, ENC_LITTLE_ENDIAN);
449
0
    offset+=4;
450
0
    proto_tree_add_item(header_tree, hf_psn_version_high, tvb, offset, 1, ENC_LITTLE_ENDIAN);
451
0
    offset+=1;
452
0
    proto_tree_add_item(header_tree, hf_psn_version_low, tvb, offset, 1, ENC_LITTLE_ENDIAN);
453
0
    offset+=1;
454
0
    proto_tree_add_item(header_tree, hf_psn_v1_world_id, tvb, offset, 2, ENC_LITTLE_ENDIAN);
455
0
    offset+=2;
456
0
    uint32_t tracker_count;
457
0
    proto_tree_add_item_ret_uint(header_tree, hf_psn_v1_tracker_count, tvb, offset, 2, ENC_LITTLE_ENDIAN, &tracker_count);
458
0
    offset+=2;
459
0
    uint32_t frame_id;
460
0
    proto_tree_add_item_ret_uint(header_tree, hf_psn_frame_id, tvb, offset, 1, ENC_LITTLE_ENDIAN, &frame_id);
461
0
    offset+=1;
462
0
    proto_tree_add_item(header_tree, hf_psn_frame_packet_count, tvb, offset, 1, ENC_LITTLE_ENDIAN);
463
0
    offset+=1;
464
0
    proto_tree_add_item(header_tree, hf_psn_v1_frame_index, tvb, offset, 1, ENC_LITTLE_ENDIAN);
465
0
    offset+=1;
466
    /* reserved */
467
0
    proto_tree_add_item(header_tree, hf_psn_v1_reserved, tvb, offset, 3, ENC_NA);
468
0
    offset+=3;
469
470
0
    col_append_fstr(pinfo->cinfo, COL_INFO, ", Frame %3u, %u Trackers", frame_id, tracker_count);
471
472
0
    for (unsigned int i = 0; i < tracker_count; i++) {
473
0
        proto_item* tracker = proto_tree_add_item(tree, hf_psn_v1_tracker, tvb, offset, 32, ENC_NA);
474
0
        proto_tree* tracker_tree = proto_item_add_subtree(tracker, ett_psn_v1_tracker);
475
476
0
        uint32_t tracker_id;
477
0
        proto_tree_add_item_ret_uint(tracker_tree, hf_psn_tracker_id, tvb, offset, 2, ENC_LITTLE_ENDIAN, &tracker_id);
478
0
        offset+=2;
479
0
        proto_tree_add_item(tracker_tree, hf_psn_v1_object_state, tvb, offset, 2, ENC_LITTLE_ENDIAN);
480
0
        offset+=2;
481
        /* reserved */
482
0
        proto_tree_add_item(tracker_tree, hf_psn_v1_reserved, tvb, offset, 4, ENC_NA);
483
0
        offset+=4;
484
0
        proto_tree_add_item(tracker_tree, hf_psn_position_x, tvb, offset, 4, ENC_LITTLE_ENDIAN);
485
0
        offset+=4;
486
0
        proto_tree_add_item(tracker_tree, hf_psn_position_y, tvb, offset, 4, ENC_LITTLE_ENDIAN);
487
0
        offset+=4;
488
0
        proto_tree_add_item(tracker_tree, hf_psn_position_z, tvb, offset, 4, ENC_LITTLE_ENDIAN);
489
0
        offset+=4;
490
0
        proto_item_prepend_text(
491
0
            proto_tree_add_item(tracker_tree, hf_psn_speed_x, tvb, offset, 4, ENC_LITTLE_ENDIAN),
492
0
            "Speed ");
493
0
        offset+=4;
494
0
        proto_item_prepend_text(
495
0
            proto_tree_add_item(tracker_tree, hf_psn_speed_y, tvb, offset, 4, ENC_LITTLE_ENDIAN),
496
0
            "Speed ");
497
0
        offset+=4;
498
0
        proto_item_prepend_text(
499
0
            proto_tree_add_item(tracker_tree, hf_psn_speed_z, tvb, offset, 4, ENC_LITTLE_ENDIAN),
500
0
            "Speed ");
501
0
        offset+=4;
502
503
0
        proto_item_append_text(tracker, ", ID: %u", tracker_id);
504
0
    }
505
0
    proto_tree_add_item(tree, hf_psn_v1_identifier, tvb, offset, 2, ENC_LITTLE_ENDIAN);
506
0
    offset+=2;
507
508
0
    return offset;
509
0
}
510
511
static int
512
dissect_psn(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
513
1
{
514
    /* Check it's a PSN packet */
515
1
    if (tvb_captured_length(tvb) < 2) {
516
0
        return 0;
517
0
    }
518
519
1
    uint16_t chunk_id = tvb_get_uint16(tvb, 0, ENC_LITTLE_ENDIAN);
520
1
    if (try_val_to_str(chunk_id, psn_base_chunk_id_names) == NULL) {
521
1
        return 0;
522
1
    }
523
524
0
    unsigned offset = 0;
525
0
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "PSN");
526
    /* Clear the info column */
527
0
    col_clear(pinfo->cinfo,COL_INFO);
528
529
0
    proto_item *ti = proto_tree_add_item(tree, proto_psn, tvb, 0, -1, ENC_NA);
530
0
    proto_tree *psn_tree = proto_item_add_subtree(ti, ett_psn);
531
532
    /* Chunk Header */
533
0
    switch (chunk_id) {
534
0
        case PSN_INFO_PACKET:
535
0
        case PSN_DATA_PACKET: {
536
0
            proto_tree_add_item(psn_tree, hf_psn_base_chunk_id, tvb, offset, 2, ENC_LITTLE_ENDIAN);
537
0
            offset+=2;
538
539
0
            uint16_t chunk_data_len = tvb_get_uint16(tvb, offset, ENC_LITTLE_ENDIAN) & 0x7FFF;
540
0
            proto_item_set_len(ti, chunk_data_len+4);
541
0
            proto_tree_add_bitmask_with_flags(psn_tree, tvb, offset, hf_psn_chunk_data_field, ett_psn_chunk_data_field, chunk_data_fields, ENC_LITTLE_ENDIAN, BMT_NO_FLAGS);
542
0
            offset+=2;
543
0
            unsigned chunk_end = offset+chunk_data_len;
544
545
0
            if (chunk_id == PSN_INFO_PACKET) {
546
0
                proto_item_append_text(ti, ", V2 Info Packet");
547
0
                col_set_str(pinfo->cinfo, COL_INFO, "PSN V2 Info");
548
0
                while (offset < chunk_end) {
549
0
                    offset = dissect_psn_info(tvb, pinfo, psn_tree, offset);
550
0
                }
551
0
            } else {
552
0
                proto_item_append_text(ti, ", V2 Data Packet");
553
0
                col_set_str(pinfo->cinfo, COL_INFO, "PSN V2 Data");
554
0
                while (offset < chunk_end) {
555
0
                    offset = dissect_psn_data(tvb, pinfo, psn_tree, offset);
556
0
                }
557
0
            }
558
0
            return chunk_end;
559
0
        }
560
0
        case PSN_V1_DATA_PACKET:
561
0
            proto_item_append_text(ti, ", V1 Data Packet");
562
0
            col_set_str(pinfo->cinfo, COL_INFO, "PSN V1 Data");
563
0
            offset = dissect_psn_v1_data(tvb, pinfo, psn_tree, offset);
564
0
            proto_item_set_len(ti, offset);
565
0
            return offset;
566
0
        case PSN_V1_INFO_PACKET:
567
0
            if (tvb_strneql(tvb, 0, "<PSN>", 5) == 0) {
568
0
                proto_item_append_text(ti, ", V1 Info Packet");
569
0
                col_set_str(pinfo->cinfo, COL_INFO, "PSN V1 Info");
570
0
            } else if (tvb_strneql(tvb, 0, "<PSN_config>", 12) == 0) {
571
0
                proto_item_append_text(ti, ", V1 Config Packet");
572
0
                col_set_str(pinfo->cinfo, COL_INFO, "PSN V1 Config");
573
0
            } else if (tvb_strneql(tvb, 0, "<PSN_config_ACK>", 16) == 0) {
574
0
                proto_item_append_text(ti, ", V1 Config Ack Packet");
575
0
                col_set_str(pinfo->cinfo, COL_INFO, "PSN V1 Config Ack");
576
0
            } else {
577
0
                proto_item_append_text(ti, ", V1 XML Packet");
578
0
                col_set_str(pinfo->cinfo, COL_INFO, "PSN V1 XML");
579
0
            }
580
0
            return call_dissector(xml_dissector_handle, tvb, pinfo, tree);
581
0
        default:
582
            /* UNREACHABLE */
583
0
            return 0;
584
0
    }
585
0
}
586
587
/******************************************************************************/
588
/* Register protocol                                                          */
589
void
590
15
proto_register_psn(void) {
591
15
    static hf_register_info hf[] = {
592
15
        { &hf_psn_info_chunk,
593
15
            { "Info Chunk", "psn.info",
594
15
            FT_NONE, BASE_NONE,
595
15
            NULL, 0x0,
596
15
            NULL, HFILL }
597
15
        },
598
15
        { &hf_psn_data_chunk,
599
15
            { "Data Chunk", "psn.data",
600
15
            FT_NONE, BASE_NONE,
601
15
            NULL, 0x0,
602
15
            NULL, HFILL }
603
15
        },
604
15
        { &hf_psn_tracker_chunk,
605
15
            { "Tracker Chunk", "psn.tracker",
606
15
            FT_NONE, BASE_NONE,
607
15
            NULL, 0x0,
608
15
            NULL, HFILL }
609
15
        },
610
15
        { &hf_psn_tracker_data_chunk,
611
15
            { "Tracker Data Chunk", "psn.tracker.data",
612
15
            FT_NONE, BASE_NONE,
613
15
            NULL, 0x0,
614
15
            NULL, HFILL }
615
15
        },
616
15
        { &hf_psn_tracker_info_chunk,
617
15
            { "Tracker Info Chunk", "psn.tracker.info",
618
15
            FT_NONE, BASE_NONE,
619
15
            NULL, 0x0,
620
15
            NULL, HFILL }
621
15
        },
622
623
15
        { &hf_psn_base_chunk_id,
624
15
            { "Chunk ID", "psn.chunk_id",
625
15
            FT_UINT16, BASE_HEX,
626
15
            VALS(psn_base_chunk_id_names), 0x0,
627
15
            NULL, HFILL }
628
15
        },
629
15
        { &hf_psn_info_chunk_id,
630
15
            { "Chunk ID", "psn.info.chunk_id",
631
15
            FT_UINT16, BASE_HEX,
632
15
            VALS(psn_info_chunk_id_names), 0x0,
633
15
            NULL, HFILL }
634
15
        },
635
15
        { &hf_psn_data_chunk_id,
636
15
            { "Chunk ID", "psn.data.chunk_id",
637
15
            FT_UINT16, BASE_HEX,
638
15
            VALS(psn_data_chunk_id_names), 0x0,
639
15
            NULL, HFILL }
640
15
        },
641
15
        { &hf_psn_tracker_id,
642
15
            { "Tracker ID", "psn.tracker.id",
643
15
            FT_UINT16, BASE_DEC,
644
15
            NULL, 0x0,
645
15
            NULL, HFILL }
646
15
        },
647
15
        { &hf_psn_tracker_data_chunk_id,
648
15
            { "Chunk ID", "psn.tracker.data.chunk_id",
649
15
            FT_UINT16, BASE_HEX,
650
15
            VALS(psn_tracker_data_chunk_id_names), 0x0,
651
15
            NULL, HFILL }
652
15
        },
653
15
        { &hf_psn_tracker_info_chunk_id,
654
15
            { "Chunk ID", "psn.tracker.info.chunk_id",
655
15
            FT_UINT16, BASE_HEX,
656
15
            VALS(psn_tracker_info_chunk_id_names), 0x0,
657
15
            NULL, HFILL }
658
15
        },
659
660
15
        { &hf_psn_chunk_data_field,
661
15
            { "Chunk Header", "psn.chunk.header",
662
15
            FT_UINT16, BASE_HEX,
663
15
            NULL, 0x0,
664
15
            NULL, HFILL }
665
15
        },
666
15
        { &hf_psn_chunk_length,
667
15
            { "Length", "psn.chunk.len",
668
15
            FT_UINT16, BASE_DEC,
669
15
            NULL, 0x7FFF,
670
15
            NULL, HFILL }
671
15
        },
672
15
        { &hf_psn_chunk_has_subchunks,
673
15
            { "Has Sub Chunks", "psn.chunk.has_subchunks",
674
15
            FT_BOOLEAN, 16,
675
15
            TFS(&tfs_yes_no), 0x8000,
676
15
            NULL, HFILL }
677
15
        },
678
679
15
        { &hf_psn_packet_timestamp,
680
15
            { "Timestamp", "psn.timestamp",
681
15
            FT_RELATIVE_TIME, BASE_NONE,
682
15
            NULL, 0x0,
683
15
            NULL, HFILL }
684
15
        },
685
15
        { &hf_psn_version_high,
686
15
            { "Version High", "psn.version_high",
687
15
            FT_UINT8, BASE_DEC,
688
15
            NULL, 0x0,
689
15
            NULL, HFILL }
690
15
        },
691
15
        { &hf_psn_version_low,
692
15
            { "Version Low", "psn.version_low",
693
15
            FT_UINT8, BASE_DEC,
694
15
            NULL, 0x0,
695
15
            NULL, HFILL }
696
15
        },
697
15
        { &hf_psn_frame_id,
698
15
            { "Frame ID", "psn.frame_id",
699
15
            FT_UINT8, BASE_DEC,
700
15
            NULL, 0x0,
701
15
            NULL, HFILL }
702
15
        },
703
15
        { &hf_psn_frame_packet_count,
704
15
            { "Frame Packet Count", "psn.frame_packet_count",
705
15
            FT_UINT8, BASE_DEC,
706
15
            NULL, 0x0,
707
15
            NULL, HFILL }
708
15
        },
709
710
711
15
        { &hf_psn_system_name,
712
15
            { "System Name", "psn.info.system_name",
713
15
            FT_STRING, BASE_NONE,
714
15
            NULL, 0x0,
715
15
            NULL, HFILL }
716
15
        },
717
15
        { &hf_psn_tracker_name,
718
15
            { "Tracker Name", "psn.tracker.info.name",
719
15
            FT_STRING, BASE_NONE,
720
15
            NULL, 0x0,
721
15
            NULL, HFILL }
722
15
        },
723
724
15
        { &hf_psn_position_x,
725
15
            { "X", "psn.position.x",
726
15
            FT_FLOAT, BASE_NONE|BASE_UNIT_STRING,
727
15
            UNS(&units_meters), 0x0,
728
15
            NULL, HFILL }
729
15
        },
730
15
        { &hf_psn_position_y,
731
15
            { "Y", "psn.position.y",
732
15
            FT_FLOAT, BASE_NONE|BASE_UNIT_STRING,
733
15
            UNS(&units_meters), 0x0,
734
15
            NULL, HFILL }
735
15
        },
736
15
        { &hf_psn_position_z,
737
15
            { "Z", "psn.position.z",
738
15
            FT_FLOAT, BASE_NONE|BASE_UNIT_STRING,
739
15
            UNS(&units_meters), 0x0,
740
15
            NULL, HFILL }
741
15
        },
742
15
        { &hf_psn_speed_x,
743
15
            { "X", "psn.speed.x",
744
15
            FT_FLOAT, BASE_NONE|BASE_UNIT_STRING,
745
15
            UNS(&units_m_s), 0x0,
746
15
            NULL, HFILL }
747
15
        },
748
15
        { &hf_psn_speed_y,
749
15
            { "Y", "psn.speed.y",
750
15
            FT_FLOAT, BASE_NONE|BASE_UNIT_STRING,
751
15
            UNS(&units_m_s), 0x0,
752
15
            NULL, HFILL }
753
15
        },
754
15
        { &hf_psn_speed_z,
755
15
            { "Z", "psn.speed.z",
756
15
            FT_FLOAT, BASE_NONE|BASE_UNIT_STRING,
757
15
            UNS(&units_m_s), 0x0,
758
15
            NULL, HFILL }
759
15
        },
760
15
        { &hf_psn_ori_x,
761
15
            { "X", "psn.origin.x",
762
15
            FT_FLOAT, BASE_NONE|BASE_UNIT_STRING,
763
15
            UNS(&units_meters), 0x0,
764
15
            NULL, HFILL }
765
15
        },
766
15
        { &hf_psn_ori_y,
767
15
            { "Y", "psn.origin.y",
768
15
            FT_FLOAT, BASE_NONE|BASE_UNIT_STRING,
769
15
            UNS(&units_meters), 0x0,
770
15
            NULL, HFILL }
771
15
        },
772
15
        { &hf_psn_ori_z,
773
15
            { "Z", "psn.origin.z",
774
15
            FT_FLOAT, BASE_NONE|BASE_UNIT_STRING,
775
15
            UNS(&units_meters), 0x0,
776
15
            NULL, HFILL }
777
15
        },
778
15
        { &hf_psn_accel_x,
779
15
            { "X", "psn.acceleration.x",
780
15
            FT_FLOAT, BASE_NONE|BASE_UNIT_STRING,
781
15
            UNS(&units_meter_sec_squared), 0x0,
782
15
            NULL, HFILL }
783
15
        },
784
15
        { &hf_psn_accel_y,
785
15
            { "Y", "psn.acceleration.y",
786
15
            FT_FLOAT, BASE_NONE|BASE_UNIT_STRING,
787
15
            UNS(&units_meter_sec_squared), 0x0,
788
15
            NULL, HFILL }
789
15
        },
790
15
        { &hf_psn_accel_z,
791
15
            { "Z", "psn.acceleration.z",
792
15
            FT_FLOAT, BASE_NONE|BASE_UNIT_STRING,
793
15
            UNS(&units_meter_sec_squared), 0x0,
794
15
            NULL, HFILL }
795
15
        },
796
15
        { &hf_psn_trgtpos_x,
797
15
            { "X", "psn.target_pos.x",
798
15
            FT_FLOAT, BASE_NONE|BASE_UNIT_STRING,
799
15
            UNS(&units_meters), 0x0,
800
15
            NULL, HFILL }
801
15
        },
802
15
        { &hf_psn_trgtpos_y,
803
15
            { "Y", "psn.target_pos.y",
804
15
            FT_FLOAT, BASE_NONE|BASE_UNIT_STRING,
805
15
            UNS(&units_meters), 0x0,
806
15
            NULL, HFILL }
807
15
        },
808
15
        { &hf_psn_trgtpos_z,
809
15
            { "Z", "psn.target_pos.z",
810
15
            FT_FLOAT, BASE_NONE|BASE_UNIT_STRING,
811
15
            UNS(&units_meters), 0x0,
812
15
            NULL, HFILL }
813
15
        },
814
15
        { &hf_psn_validity,
815
15
            { "Validity", "psn.tracker.data.validity",
816
15
            FT_FLOAT, BASE_NONE,
817
15
            NULL, 0x0,
818
15
            NULL, HFILL }
819
15
        },
820
15
        { &hf_psn_tracker_timestamp,
821
15
            { "Timestamp", "psn.tracker.data.timestamp",
822
15
            FT_RELATIVE_TIME, BASE_NONE,
823
15
            NULL, 0x0,
824
15
            NULL, HFILL }
825
15
        },
826
827
15
        { &hf_psn_v1_header,
828
15
            { "Header", "psn.v1.header",
829
15
            FT_NONE, BASE_NONE,
830
15
            NULL, 0x0,
831
15
            NULL, HFILL }
832
15
        },
833
15
        { &hf_psn_v1_identifier,
834
15
            { "Identifier", "psn.v1.identifier",
835
15
            FT_UINT16, BASE_HEX,
836
15
            VALS(psn_v1_identifier), 0x0,
837
15
            NULL, HFILL }
838
15
        },
839
15
        { &hf_psn_v1_tracker,
840
15
            { "Tracker", "psn.v1.tracker",
841
15
            FT_NONE, BASE_NONE,
842
15
            NULL, 0x0,
843
15
            NULL, HFILL }
844
15
        },
845
15
        { &hf_psn_v1_position,
846
15
            { "Position", "psn.v1.position",
847
15
            FT_NONE, BASE_NONE,
848
15
            NULL, 0x0,
849
15
            NULL, HFILL }
850
15
        },
851
15
        { &hf_psn_v1_velocity,
852
15
            { "Velocity", "psn.v1.velocity",
853
15
            FT_NONE, BASE_NONE,
854
15
            NULL, 0x0,
855
15
            NULL, HFILL }
856
15
        },
857
15
        { &hf_psn_v1_packet_counter,
858
15
            { "Packet Counter", "psn.v1.packet_counter",
859
15
            FT_UINT32, BASE_DEC,
860
15
            NULL, 0x0,
861
15
            NULL, HFILL }
862
15
        },
863
15
        { &hf_psn_v1_world_id,
864
15
            { "World ID", "psn.v1.world_id",
865
15
            FT_UINT32, BASE_DEC,
866
15
            NULL, 0x0,
867
15
            NULL, HFILL }
868
15
        },
869
15
        { &hf_psn_v1_tracker_count,
870
15
            { "Tracker Count", "psn.v1.tracker_count",
871
15
            FT_UINT16, BASE_DEC,
872
15
            NULL, 0x0,
873
15
            NULL, HFILL }
874
15
        },
875
15
        { &hf_psn_v1_frame_index,
876
15
            { "Frame Index", "psn.v1.frame_index",
877
15
            FT_UINT8, BASE_DEC,
878
15
            NULL, 0x0,
879
15
            NULL, HFILL }
880
15
        },
881
15
        { &hf_psn_v1_object_state,
882
15
            { "Object State", "psn.v1.object_state",
883
15
            FT_UINT16, BASE_DEC,
884
15
            NULL, 0x0,
885
15
            NULL, HFILL }
886
15
        },
887
15
        { &hf_psn_v1_info_xml,
888
15
            { "V1 Info XML", "psn.v1.info_xml",
889
15
            FT_STRING, BASE_NONE,
890
15
            NULL, 0x0,
891
15
            NULL, HFILL }
892
15
        },
893
15
        { &hf_psn_v1_reserved,
894
15
            { "Reserved", "psn.v1.reserved",
895
15
            FT_BYTES, BASE_NONE,
896
15
            NULL, 0x0,
897
15
            NULL, HFILL }
898
15
        },
899
15
    };
900
901
    /* Setup protocol subtree array */
902
15
    static int *ett[] = {
903
15
        &ett_psn,
904
905
15
        &ett_psn_info_chunk,
906
15
        &ett_psn_data_chunk,
907
15
        &ett_psn_tracker_chunk,
908
15
        &ett_psn_tracker_info_chunk,
909
15
        &ett_psn_tracker_data_chunk,
910
911
15
        &ett_psn_chunk_data_field,
912
913
15
        &ett_psn_v1_header,
914
15
        &ett_psn_v1_tracker,
915
15
    };
916
917
15
    static ei_register_info ei[] = {
918
15
        { &ei_psn_chunk_id,  { "psn.chunk_id.unknown", PI_PROTOCOL, PI_ERROR, "Unknown chunk ID for this location", EXPFILL }},
919
15
        { &ei_psn_chunk_len, { "psn.chunk.len.mismatch", PI_PROTOCOL, PI_WARN, "Mismatch between reported chunk length and consumed data", EXPFILL }},
920
15
    };
921
922
15
    proto_psn = proto_register_protocol("PosiStageNet", "PSN", "psn");
923
924
15
    proto_register_field_array(proto_psn, hf, array_length(hf));
925
15
    proto_register_subtree_array(ett, array_length(ett));
926
15
    expert_module_t* expert_psn = expert_register_protocol(proto_psn);
927
15
    expert_register_field_array(expert_psn, ei, array_length(ei));
928
929
15
    psn_handle = register_dissector_with_description (
930
15
        "psn",          /* dissector name           */
931
15
        "PSN",          /* dissector description    */
932
15
        dissect_psn,    /* dissector function       */
933
15
        proto_psn       /* protocol being dissected */
934
15
    );
935
15
}
936
937
/******************************************************************************/
938
/* Register handoff                                                           */
939
void
940
proto_reg_handoff_psn(void)
941
15
{
942
15
    dissector_add_uint_with_preference("udp.port", 56565, psn_handle);
943
944
15
    xml_dissector_handle = find_dissector_add_dependency("xml", proto_psn);
945
15
}
946
947
/*
948
 * Editor modelines
949
 *
950
 * Local Variables:
951
 * c-basic-offset: 4
952
 * tab-width: 8
953
 * indent-tabs-mode: nil
954
 * End:
955
 *
956
 * ex: set shiftwidth=4 tabstop=8 expandtab:
957
 * :indentSize=4:tabSize=8:noTabs=true:
958
 */