Coverage Report

Created: 2025-08-04 07:15

/src/wireshark/epan/dissectors/packet-osmo_trx.c
Line
Count
Source (jump to first uncovered line)
1
/* packet-osmo_trx.c
2
 * Dissector for OsmoTRX Protocol (GSM Transceiver control and data).
3
 *
4
 * (C) 2018 by Harald Welte <laforge@gnumonks.org>
5
 * (C) 2019 by Vadim Yanitskiy <axilirator@gmail.com>
6
 * (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
7
 *
8
 * Wireshark - Network traffic analyzer
9
 * By Gerald Combs <gerald@wireshark.org>
10
 * Copyright 1998 Gerald Combs
11
 *
12
 * SPDX-License-Identifier: GPL-2.0-or-later
13
 */
14
15
#include "config.h"
16
17
#include <epan/packet.h>
18
#include <epan/expert.h>
19
#include <epan/unit_strings.h>
20
#include <epan/tfs.h>
21
#include <wsutil/array.h>
22
23
/* This is a non-standard, ad-hoc protocol to pass baseband GSM bursts between
24
 * the transceiver (such as osmo-trx, fake_trx.py or grgsm_trx) and the L1
25
 * program (such as osmo-bts-trx or trxcon). Osmocom inherited this protocol
26
 * when forking OsmoTRX off the OpenBTS "Transceiver" program. */
27
28
void proto_register_osmo_trx(void);
29
void proto_reg_handoff_osmo_trx(void);
30
31
static dissector_handle_t otrxd_handle;
32
static dissector_handle_t otrxc_handle;
33
34
/* Which kind of message it is */
35
static int proto_otrxd;
36
static int proto_otrxc;
37
38
/* Generated fields */
39
static int hf_otrxd_burst_dir;
40
static int hf_otrxc_msg_dir;
41
42
/* TRXD PDU version */
43
static int hf_otrxd_pdu_ver;
44
45
/* TRXD common fields */
46
static int hf_otrxd_chdr_reserved;
47
static int hf_otrxd_shadow_ind;
48
static int hf_otrxd_batch_ind;
49
static int hf_otrxd_trx_num;
50
static int hf_otrxd_tdma_tn;
51
static int hf_otrxd_tdma_fn;
52
53
/* MTS (Modulation and Training Sequence) fields */
54
static int hf_otrxd_nope_ind;
55
static int hf_otrxd_nope_ind_pad;
56
static int hf_otrxd_mod_2b; /* 2 bit field */
57
static int hf_otrxd_mod_3b; /* 3 bit field */
58
static int hf_otrxd_mod_4b; /* 4 bit field */
59
static int hf_otrxd_tsc_set_x4;
60
static int hf_otrxd_tsc_set_x2;
61
static int hf_otrxd_tsc;
62
63
/* TRXD Rx header fields */
64
static int hf_otrxd_rssi;
65
static int hf_otrxd_toa256;
66
static int hf_otrxd_ci;
67
68
/* TRXD Tx header fields */
69
static int hf_otrxd_tx_att;
70
static int hf_otrxd_tx_scpir;
71
static int hf_otrxd_tx_rfu;
72
73
/* Burst soft (255 .. 0) / hard (1 or 0) bits */
74
static int hf_otrxd_soft_symbols;
75
static int hf_otrxd_hard_symbols;
76
static int hf_otrxd_burst_pad;
77
78
/* TRXC - Control and Clock protocol */
79
static int hf_otrxc_type;
80
static int hf_otrxc_delimiter;
81
static int hf_otrxc_verb;
82
static int hf_otrxc_params;
83
static int hf_otrxc_status;
84
85
static int ett_otrxd;
86
static int ett_otrxc;
87
88
static int ett_otrxd_rx_pdu;
89
static int ett_otrxd_tx_pdu;
90
91
static expert_field ei_otrxd_unknown_pdu_ver;
92
static expert_field ei_otrxd_injected_msg;
93
static expert_field ei_otrxd_unknown_dir;
94
static expert_field ei_otrxd_tail_octets;
95
96
static expert_field ei_otrxc_unknown_msg_type;
97
static expert_field ei_otrxc_bad_delimiter;
98
static expert_field ei_otrxc_rsp_no_code;
99
static expert_field ei_otrxc_injected_msg;
100
static expert_field ei_otrxc_unknown_dir;
101
102
/* Custom units */
103
static const unit_name_string otrx_units_toa256 = { " (1/256 of a symbol)", NULL };
104
105
/* TRXD SHADOW.ind value description */
106
static const true_false_string otrxd_shadow_bool_val = {
107
  "This is a shadow PDU",
108
  "This is a primary PDU",
109
};
110
111
/* TRXD BATCH.ind value description */
112
static const true_false_string otrxd_batch_bool_val = {
113
  "Another PDU follows",
114
  "This is the last PDU",
115
};
116
117
/* TRXD NOPE.{ind,req} value description */
118
static const true_false_string otrxd_nope_bool_val = {
119
  "Burst is not present",
120
  "Burst is present",
121
};
122
123
/* TRXD modulation types (2 bit field) */
124
static const value_string otrxd_mod_2b_vals[] = {
125
  /* .00xx... */  { 0x00, "GMSK" },
126
  /* .11xx... */  { 0x03, "AQPSK" },
127
  { 0, NULL },
128
};
129
130
/* TRXD modulation types (3 bit field) */
131
static const value_string otrxd_mod_3b_vals[] = {
132
  /* .010x... */  { 0x02, "8-PSK" },
133
  /* .100x... */  { 0x04, "16QAM" },
134
  /* .101x... */  { 0x05, "32QAM" },
135
  { 0, NULL },
136
};
137
138
/* TRXD modulation types (4 bit field) */
139
static const value_string otrxd_mod_4b_vals[] = {
140
  /* .0110... */  { 0x06, "GMSK (Access Burst)" },
141
  /* .0111... */  { 0x07, "RFU (Reserved for Future Use)" },
142
  { 0, NULL },
143
};
144
145
/* TRXD modulation type */
146
enum otrxd_mod_type {
147
  OTRXD_MOD_T_GMSK    = 0x00,
148
  OTRXD_MOD_T_8PSK    = 0x02,
149
  OTRXD_MOD_T_AQPSK   = 0x03,
150
  OTRXD_MOD_T_16QAM   = 0x04,
151
  OTRXD_MOD_T_32QAM   = 0x05,
152
  OTRXD_MOD_T_GMSK_AB   = 0x06,
153
  OTRXD_MOD_T_RFU     = 0x07,
154
};
155
156
/* See 3GPP TS 45.002, section 5.2 "Bursts" */
157
0
#define GMSK_BURST_LEN      148
158
159
/* TRXD modulation / burst length mapping */
160
static const uint16_t otrxd_burst_len[] = {
161
  [OTRXD_MOD_T_GMSK]    = GMSK_BURST_LEN * 1,
162
  [OTRXD_MOD_T_GMSK_AB]   = GMSK_BURST_LEN * 1,
163
  [OTRXD_MOD_T_AQPSK]   = GMSK_BURST_LEN * 2,
164
  [OTRXD_MOD_T_8PSK]    = GMSK_BURST_LEN * 3,
165
  [OTRXD_MOD_T_16QAM]   = GMSK_BURST_LEN * 4,
166
  [OTRXD_MOD_T_32QAM]   = GMSK_BURST_LEN * 5,
167
  [OTRXD_MOD_T_RFU]   = 0, /* unknown */
168
};
169
170
/* RSSI is encoded without a negative sign, so we need to show it */
171
static void format_rssi(char *buf, const uint32_t rssi)
172
0
{
173
0
  snprintf(buf, ITEM_LABEL_LENGTH, "-%u%s", rssi, unit_name_string_get_value(rssi, &units_dbm));
174
0
}
175
176
/* TSC (Training Sequence Code) set number in 3GPP TS 45.002 starts
177
 * from 1, while 'on the wire' it's encoded as X - 1 (starts from 0). */
178
static void format_tsc_set(char *buf, uint32_t tsc_set)
179
0
{
180
0
  snprintf(buf, ITEM_LABEL_LENGTH, "%u", tsc_set + 1);
181
0
}
182
183
/* Message direction */
184
enum otrxcd_dir_type {
185
  OTRXCD_DIR_UNKNOWN = 0,
186
  OTRXCD_DIR_L12TRX,
187
  OTRXCD_DIR_TRX2L1,
188
};
189
190
static const value_string otrxcd_dir_vals[] = {
191
  { OTRXCD_DIR_UNKNOWN, "Unknown" },
192
  { OTRXCD_DIR_L12TRX, "L1 -> TRX" },
193
  { OTRXCD_DIR_TRX2L1, "TRX -> L1" },
194
  { 0, NULL },
195
};
196
197
/* Determine message direction (L1 to TRX, or TRX to L1?) */
198
static enum otrxcd_dir_type otrxcd_get_dir(const packet_info *pinfo)
199
0
{
200
0
  if (pinfo->srcport - pinfo->destport == 100)
201
0
    return OTRXCD_DIR_L12TRX;
202
0
  else if (pinfo->destport - pinfo->srcport == 100)
203
0
    return OTRXCD_DIR_TRX2L1;
204
0
  else
205
0
    return OTRXCD_DIR_UNKNOWN;
206
0
}
207
208
/* Guess message direction (L1 to TRX, or TRX to L1?) */
209
static enum otrxcd_dir_type otrxcd_guess_dir(const packet_info *pinfo)
210
0
{
211
  /* TODO: srcport can be also used for guessing,
212
   * TODO: use port numbers from protocol preferences. */
213
0
  switch (pinfo->destport) {
214
  /* OsmoTRXD: Tx burst (L1 -> TRX) */
215
0
  case 5702: case 5704: case 6702:
216
0
    return OTRXCD_DIR_L12TRX;
217
  /* OsmoTRXD: Rx burst (TRX -> L1) */
218
0
  case 5802: case 5804: case 6802:
219
0
    return OTRXCD_DIR_TRX2L1;
220
  /* OsmoTRXC: Command (L1 -> TRX) */
221
0
  case 5701: case 5703: case 6701:
222
0
    return OTRXCD_DIR_L12TRX;
223
  /* OsmoTRXC: Response or Indication (TRX -> L1) */
224
0
  case 5801: case 5803: case 6801:
225
0
  case 5800: case 6800:
226
0
    return OTRXCD_DIR_TRX2L1;
227
0
  default:
228
0
    return OTRXCD_DIR_UNKNOWN;
229
0
  }
230
0
}
231
232
/* TRXC message types */
233
enum otrxc_msg_type {
234
  OTRXC_MSG_TYPE_UNKNOWN = 0,
235
  OTRXC_MSG_TYPE_COMMAND,
236
  OTRXC_MSG_TYPE_RESPONSE,
237
  OTRXC_MSG_TYPE_INDICATION,
238
};
239
240
static const value_string otrxc_msg_type_enc[] = {
241
  { OTRXC_MSG_TYPE_COMMAND, "CMD" },
242
  { OTRXC_MSG_TYPE_RESPONSE,  "RSP" },
243
  { OTRXC_MSG_TYPE_INDICATION,  "IND" },
244
  { 0, NULL },
245
};
246
247
static const value_string otrxc_msg_type_desc[] = {
248
  { OTRXC_MSG_TYPE_COMMAND, "Command" },
249
  { OTRXC_MSG_TYPE_RESPONSE,  "Response" },
250
  { OTRXC_MSG_TYPE_INDICATION,  "Indication" },
251
  { 0, NULL },
252
};
253
254
/* TRXD PDU information */
255
struct otrxd_pdu_info {
256
  /* PDU version */
257
  uint32_t ver;
258
  /* BATCH.ind marker */
259
  bool batch;
260
  /* SHADOW.ind marker */
261
  bool shadow;
262
  /* Number of batched PDUs */
263
  uint32_t num_pdus;
264
  /* TRX (RF channel) number */
265
  uint32_t trx_num;
266
  /* TDMA frame number */
267
  uint32_t fn;
268
  /* TDMA timeslot number */
269
  uint32_t tn;
270
  /* NOPE.{ind,req} marker */
271
  bool nope;
272
  /* Modulation type and string */
273
  enum otrxd_mod_type mod;
274
  const char *mod_str;
275
  /* Training Sequence Code */
276
  uint32_t tsc;
277
};
278
279
/* Dissector for common Rx/Tx TRXDv0/v1 header part */
280
static void dissect_otrxd_chdr_v0(tvbuff_t *tvb, packet_info *pinfo _U_,
281
          proto_item *ti, proto_tree *tree,
282
          struct otrxd_pdu_info *pi,
283
          int *offset)
284
0
{
285
0
  proto_tree_add_item(tree, hf_otrxd_chdr_reserved, tvb,
286
0
          *offset, 1, ENC_NA);
287
0
  proto_tree_add_item_ret_uint(tree, hf_otrxd_tdma_tn, tvb,
288
0
             *offset, 1, ENC_NA, &pi->tn);
289
0
  *offset += 1;
290
291
  /* TDMA frame number (4 octets, big endian) */
292
0
  proto_tree_add_item_ret_uint(tree, hf_otrxd_tdma_fn, tvb,
293
0
             *offset, 4, ENC_BIG_ENDIAN, &pi->fn);
294
0
  *offset += 4;
295
296
0
  proto_item_append_text(ti, "TDMA FN %07u TN %u", pi->fn, pi->tn);
297
0
}
298
299
/* Dissector for MTS (Modulation and Training Sequence) */
300
static void dissect_otrxd_mts(tvbuff_t *tvb, proto_tree *tree,
301
            struct otrxd_pdu_info *pi,
302
            int offset)
303
0
{
304
  /* NOPE indication contains no MTS information.
305
   *
306
   * | 7 6 5 4 3 2 1 0 | Bit numbers (value range)
307
   * | X . . . . . . . | NOPE / IDLE indication
308
   * | . X X X X . . . | MTS (Modulation and Training Sequence)
309
   * | . . . . . X X X | TSC (Training Sequence Code)
310
   */
311
0
  proto_tree_add_item_ret_boolean(tree, hf_otrxd_nope_ind, tvb,
312
0
          offset, 1, ENC_NA, &pi->nope);
313
0
  if (pi->nope) {
314
0
    proto_tree_add_item(tree, hf_otrxd_nope_ind_pad, tvb, offset, 1, ENC_NA);
315
0
    return;
316
0
  }
317
318
  /* MTS (Modulation and Training Sequence info).
319
   *
320
   * | 7 6 5 4 3 2 1 0 | Bit numbers (value range)
321
   * | . 0 0 X X . . . | GMSK, 4 TSC sets (0..3)
322
   * | . 0 1 0 X . . . | 8-PSK, 2 TSC sets (0..1)
323
   * | . 0 1 1 0 . . . | GMSK, Packet Access Burst
324
   * | . 0 1 1 1 . . . | RFU (Reserved for Future Use)
325
   * | . 1 0 0 X . . . | 16QAM, 2 TSC sets (0..1)
326
   * | . 1 0 1 X . . . | 32QAM, 2 TSC sets (0..1)
327
   * | . 1 1 X X . . . | AQPSK, 4 TSC sets (0..3)
328
   *
329
   * NOTE: 3GPP defines 4 TSC sets for both GMSK and AQPSK.
330
   */
331
0
  uint8_t mts = tvb_get_uint8(tvb, offset);
332
0
  if ((mts >> 5) == 0x00 || (mts >> 5) == 0x03) { /* 2 bit: GMSK (0) or AQPSK (3) */
333
0
    pi->mod = (enum otrxd_mod_type) (mts >> 5);
334
0
    pi->mod_str = val_to_str(mts >> 5, otrxd_mod_2b_vals, "Unknown 0x%02x");
335
0
    proto_tree_add_item(tree, hf_otrxd_mod_2b, tvb, offset, 1, ENC_NA);
336
0
    proto_tree_add_item(tree, hf_otrxd_tsc_set_x4, tvb, offset, 1, ENC_NA);
337
0
  } else if ((mts >> 4) != 0x03) { /* 3 bit: 8-PSK, 16QAM, or 32QAM */
338
0
    pi->mod = (enum otrxd_mod_type) (mts >> 4);
339
0
    pi->mod_str = val_to_str(mts >> 4, otrxd_mod_3b_vals, "Unknown 0x%02x");
340
0
    proto_tree_add_item(tree, hf_otrxd_mod_3b, tvb, offset, 1, ENC_NA);
341
0
    proto_tree_add_item(tree, hf_otrxd_tsc_set_x2, tvb, offset, 1, ENC_NA);
342
0
  } else { /* 4 bit (without TSC set): GMSK (Packet Access Burst) or RFU */
343
0
    pi->mod = (enum otrxd_mod_type) (mts >> 3);
344
0
    pi->mod_str = val_to_str(mts >> 3, otrxd_mod_4b_vals, "Unknown 0x%02x");
345
0
    proto_tree_add_item(tree, hf_otrxd_mod_4b, tvb, offset, 1, ENC_NA);
346
0
  }
347
348
0
  proto_tree_add_item_ret_uint(tree, hf_otrxd_tsc, tvb, offset, 1, ENC_NA, &pi->tsc);
349
0
}
350
351
/* Dissector for Rx TRXD header version 0 */
352
static int dissect_otrxd_rx_hdr_v0(tvbuff_t *tvb, packet_info *pinfo,
353
           proto_item *ti, proto_tree *tree,
354
           struct otrxd_pdu_info *pi,
355
           int offset)
356
0
{
357
0
  dissect_otrxd_chdr_v0(tvb, pinfo, ti, tree, pi, &offset);
358
359
0
  proto_tree_add_item(tree, hf_otrxd_rssi, tvb, offset++, 1, ENC_NA);
360
0
  proto_tree_add_item(tree, hf_otrxd_toa256, tvb, offset, 2, ENC_BIG_ENDIAN);
361
0
  offset += 2;
362
363
0
  return offset;
364
0
}
365
366
/* Dissector for Rx TRXD header version 1 */
367
static int dissect_otrxd_rx_hdr_v1(tvbuff_t *tvb, packet_info *pinfo,
368
           proto_item *ti, proto_tree *tree,
369
           struct otrxd_pdu_info *pi,
370
           int offset)
371
0
{
372
  /* Dissect V0 specific part first */
373
0
  offset = dissect_otrxd_rx_hdr_v0(tvb, pinfo, ti, tree, pi, offset);
374
375
  /* MTS (Modulation and Training Sequence) */
376
0
  dissect_otrxd_mts(tvb, tree, pi, offset++);
377
0
  if (!pi->nope)
378
0
    proto_item_append_text(ti, ", Modulation %s, TSC %u", pi->mod_str, pi->tsc);
379
0
  else
380
0
    proto_item_append_text(ti, ", NOPE.ind");
381
382
  /* C/I (Carrier to Interference ratio) */
383
0
  proto_tree_add_item(tree, hf_otrxd_ci, tvb, offset, 2, ENC_BIG_ENDIAN);
384
0
  offset += 2;
385
386
0
  return offset;
387
0
}
388
389
/* Dissector for TRXD Rx header version 2 */
390
static int dissect_otrxd_rx_hdr_v2(tvbuff_t *tvb, packet_info *pinfo _U_,
391
           proto_item *ti, proto_tree *tree,
392
           struct otrxd_pdu_info *pi,
393
           int offset)
394
0
{
395
0
  proto_tree_add_item(tree, hf_otrxd_chdr_reserved, tvb, offset, 1, ENC_NA);
396
0
  proto_tree_add_item_ret_uint(tree, hf_otrxd_tdma_tn, tvb,
397
0
             offset, 1, ENC_NA, &pi->tn);
398
0
  offset += 1;
399
400
0
  proto_tree_add_item_ret_boolean(tree, hf_otrxd_batch_ind, tvb,
401
0
          offset, 1, ENC_NA, &pi->batch);
402
0
  proto_tree_add_item_ret_boolean(tree, hf_otrxd_shadow_ind, tvb,
403
0
          offset, 1, ENC_NA, &pi->shadow);
404
0
  proto_tree_add_item_ret_uint(tree, hf_otrxd_trx_num, tvb,
405
0
             offset, 1, ENC_NA, &pi->trx_num);
406
0
  offset += 1;
407
408
  /* MTS (Modulation and Training Sequence) */
409
0
  dissect_otrxd_mts(tvb, tree, pi, offset++);
410
411
  /* RSSI (Received Signal Strength Indication) */
412
0
  proto_tree_add_item(tree, hf_otrxd_rssi, tvb, offset++, 1, ENC_NA);
413
414
  /* ToA256 (Timing of Arrival) and C/I (Carrier to Interference ratio) */
415
0
  proto_tree_add_item(tree, hf_otrxd_toa256, tvb, offset, 2, ENC_BIG_ENDIAN);
416
0
  proto_tree_add_item(tree, hf_otrxd_ci, tvb, offset + 2, 2, ENC_BIG_ENDIAN);
417
0
  offset += 4;
418
419
  /* TDMA frame number (absent in additional PDUs) */
420
0
  if (pi->num_pdus == 0) {
421
0
    proto_tree_add_item_ret_uint(tree, hf_otrxd_tdma_fn, tvb,
422
0
               offset, 4, ENC_BIG_ENDIAN, &pi->fn);
423
0
    offset += 4;
424
0
  }
425
426
0
  proto_item_append_text(ti, "TRXN %02u, TDMA FN %07u TN %u", pi->trx_num, pi->fn, pi->tn);
427
0
  if (!pi->nope)
428
0
    proto_item_append_text(ti, ", Modulation %s, TSC %u", pi->mod_str, pi->tsc);
429
0
  else
430
0
    proto_item_append_text(ti, ", NOPE.ind");
431
432
0
  return offset;
433
0
}
434
435
/* Burst data in Receive direction */
436
static int dissect_otrxd_rx(tvbuff_t *tvb, packet_info *pinfo,
437
          proto_item *pti, proto_tree *ptree,
438
          struct otrxd_pdu_info *pi,
439
          int offset)
440
0
{
441
0
  int start, burst_len, padding;
442
0
  proto_tree *tree;
443
0
  proto_item *ti;
444
445
0
loop:
446
  /* Add a sub-tree for each PDU (length is set below) */
447
0
  tree = proto_tree_add_subtree(ptree, tvb, offset, -1,
448
0
              ett_otrxd_rx_pdu, &ti,
449
0
              "TRXD Rx PDU: ");
450
0
  start = offset;
451
452
  /* Parse version specific TRXD header part */
453
0
  switch (pi->ver) {
454
0
  case 0:
455
0
    offset = dissect_otrxd_rx_hdr_v0(tvb, pinfo, ti, tree, pi, offset);
456
    /* The remaining octets is basically soft-bits of the burst */
457
0
    burst_len = tvb_reported_length(tvb) - offset;
458
    /* ... there must be at least 148 soft-bits */
459
0
    if (burst_len < GMSK_BURST_LEN)
460
0
      burst_len = GMSK_BURST_LEN; /* let it crash! */
461
    /* ... there can be 2 optional padding octets in the end */
462
0
    padding = burst_len % GMSK_BURST_LEN;
463
0
    proto_tree_add_item(tree, hf_otrxd_soft_symbols, tvb,
464
0
            offset, burst_len - padding, ENC_NA);
465
0
    offset += burst_len - padding;
466
0
    if (padding == 0)
467
0
      break;
468
0
    proto_tree_add_item(tree, hf_otrxd_burst_pad, tvb,
469
0
            offset, padding, ENC_NA);
470
0
    offset += padding;
471
0
    break;
472
0
  case 1:
473
0
    offset = dissect_otrxd_rx_hdr_v1(tvb, pinfo, ti, tree, pi, offset);
474
0
    if (pi->nope) /* NOPE.ind contains no burst */
475
0
      break;
476
0
    burst_len = otrxd_burst_len[pi->mod];
477
0
    proto_tree_add_item(tree, hf_otrxd_soft_symbols, tvb,
478
0
            offset, burst_len, ENC_NA);
479
0
    offset += burst_len;
480
0
    break;
481
0
  case 2:
482
0
    offset = dissect_otrxd_rx_hdr_v2(tvb, pinfo, ti, tree, pi, offset);
483
0
    if (pi->nope) /* NOPE.ind contains no burst */
484
0
      break;
485
0
    burst_len = otrxd_burst_len[pi->mod];
486
0
    proto_tree_add_item(tree, hf_otrxd_soft_symbols, tvb,
487
0
            offset, burst_len, ENC_NA);
488
0
    offset += burst_len;
489
0
    break;
490
0
  default:
491
0
    expert_add_info_format(pinfo, pti, &ei_otrxd_unknown_pdu_ver,
492
0
               "Unknown TRXD PDU version %u", pi->ver);
493
0
    offset = 1; /* Only the PDU version was parsed */
494
0
    return offset;
495
0
  }
496
497
0
  proto_item_set_len(ti, offset - start);
498
499
  /* Number of processed PDUs */
500
0
  pi->num_pdus += 1;
501
502
  /* There can be additional 'batched' PDUs */
503
0
  if (pi->batch)
504
0
    goto loop;
505
506
0
  return offset;
507
0
}
508
509
/* Dissector for TRXDv0/v1 Tx burst */
510
static void dissect_otrxd_tx_burst_v0(tvbuff_t *tvb, packet_info *pinfo _U_,
511
              proto_item *ti, proto_tree *tree,
512
              struct otrxd_pdu_info *pi,
513
              int *offset)
514
0
{
515
  /* Calculate the burst length */
516
0
  const int burst_len = tvb_reported_length(tvb) - *offset;
517
518
  /* Attempt to guess modulation by the length */
519
0
  switch (burst_len) {
520
  /* We may also have NOPE.req in the future (to drive fake_trx.py) */
521
0
  case 0:
522
0
    proto_item_append_text(ti, ", NOPE.req");
523
0
    pi->nope = true;
524
0
    return;
525
526
  /* TODO: introduce an enumerated type, detect other modulation types,
527
   * TODO: add a generated field for "osmo_trxd.mod" */
528
0
  case GMSK_BURST_LEN:
529
0
    proto_item_append_text(ti, ", Modulation GMSK");
530
0
    pi->mod_str = "GMSK";
531
0
    break;
532
0
  case 3 * GMSK_BURST_LEN:
533
0
    proto_item_append_text(ti, ", Modulation 8-PSK");
534
0
    pi->mod_str = "8-PSK";
535
0
    break;
536
0
  }
537
538
  /* Hard-bits (1 or 0) */
539
0
  proto_tree_add_item(tree, hf_otrxd_hard_symbols, tvb,
540
0
          *offset, burst_len, ENC_NA);
541
0
  *offset += burst_len;
542
0
}
543
544
/* Dissector for TRXD Tx header version 2 */
545
static void dissect_otrxd_tx_hdr_v2(tvbuff_t *tvb, packet_info *pinfo _U_,
546
            proto_item *ti, proto_tree *tree,
547
            struct otrxd_pdu_info *pi,
548
            int *offset)
549
0
{
550
0
  proto_tree_add_item(tree, hf_otrxd_chdr_reserved, tvb, *offset, 1, ENC_NA);
551
0
  proto_tree_add_item_ret_uint(tree, hf_otrxd_tdma_tn, tvb,
552
0
             *offset, 1, ENC_NA, &pi->tn);
553
0
  *offset += 1;
554
555
0
  proto_tree_add_item_ret_boolean(tree, hf_otrxd_batch_ind, tvb,
556
0
          *offset, 1, ENC_NA, &pi->batch);
557
0
  proto_tree_add_item_ret_uint(tree, hf_otrxd_trx_num, tvb,
558
0
             *offset, 1, ENC_NA, &pi->trx_num);
559
0
  *offset += 1;
560
561
  /* MTS (Modulation and Training Sequence) */
562
0
  dissect_otrxd_mts(tvb, tree, pi, *offset);
563
0
  *offset += 1;
564
565
  /* Tx power attenuation */
566
0
  proto_tree_add_item(tree, hf_otrxd_tx_att, tvb, *offset, 1, ENC_NA);
567
0
  *offset += 1;
568
569
  /* SCPIR (Subchannel Power Imbalance Ratio) */
570
0
  proto_tree_add_item(tree, hf_otrxd_tx_scpir, tvb, *offset, 1, ENC_NA);
571
0
  *offset += 1;
572
573
  /* RFU (currently just to make the header dword-alignment) */
574
0
  proto_tree_add_item(tree, hf_otrxd_tx_rfu, tvb, *offset, 3, ENC_NA);
575
0
  *offset += 3;
576
577
  /* TDMA frame number (absent in additional PDUs) */
578
0
  if (pi->num_pdus == 0) {
579
0
    proto_tree_add_item_ret_uint(tree, hf_otrxd_tdma_fn, tvb,
580
0
               *offset, 4, ENC_BIG_ENDIAN, &pi->fn);
581
0
    *offset += 4;
582
0
  }
583
584
0
  proto_item_append_text(ti, "TRXN %02u, TDMA FN %07u TN %u", pi->trx_num, pi->fn, pi->tn);
585
0
  if (!pi->nope)
586
0
    proto_item_append_text(ti, ", Modulation %s, TSC %u", pi->mod_str, pi->tsc);
587
0
  else
588
0
    proto_item_append_text(ti, ", NOPE.req");
589
0
}
590
591
/* Burst data in Transmit direction */
592
static int dissect_otrxd_tx(tvbuff_t *tvb, packet_info *pinfo,
593
          proto_item *pti, proto_tree *ptree,
594
          struct otrxd_pdu_info *pi,
595
          int offset)
596
0
{
597
0
  proto_tree *tree;
598
0
  proto_item *ti;
599
0
  int burst_len;
600
0
  int start;
601
602
0
loop:
603
  /* Add a sub-tree for each PDU (length is set below) */
604
0
  tree = proto_tree_add_subtree(ptree, tvb, offset, -1,
605
0
              ett_otrxd_tx_pdu, &ti,
606
0
              "TRXD Tx PDU: ");
607
0
  start = offset;
608
609
0
  switch (pi->ver) {
610
  /* Both versions feature the same PDU format */
611
0
  case 0:
612
0
  case 1:
613
0
    dissect_otrxd_chdr_v0(tvb, pinfo, ti, tree, pi, &offset);
614
0
    proto_tree_add_item(tree, hf_otrxd_tx_att, tvb, offset++, 1, ENC_NA);
615
0
    dissect_otrxd_tx_burst_v0(tvb, pinfo, ti, tree, pi, &offset);
616
0
    break;
617
0
  case 2:
618
0
    dissect_otrxd_tx_hdr_v2(tvb, pinfo, ti, tree, pi, &offset);
619
0
    if (pi->nope) /* NOPE.ind contains no burst */
620
0
      break;
621
0
    burst_len = otrxd_burst_len[pi->mod];
622
0
    proto_tree_add_item(tree, hf_otrxd_hard_symbols, tvb,
623
0
            offset, burst_len, ENC_NA);
624
0
    offset += burst_len;
625
0
    break;
626
0
  default:
627
0
    expert_add_info_format(pinfo, pti, &ei_otrxd_unknown_pdu_ver,
628
0
               "Unknown TRXD PDU version %u", pi->ver);
629
0
    offset = 1; /* Only the PDU version was parsed */
630
0
    return offset;
631
0
  }
632
633
0
  proto_item_set_len(ti, offset - start);
634
635
  /* Number of processed PDUs */
636
0
  pi->num_pdus += 1;
637
638
  /* There can be additional 'batched' PDUs */
639
0
  if (pi->batch)
640
0
    goto loop;
641
642
0
  return offset;
643
0
}
644
645
/* Common dissector for bursts in both directions */
646
static int dissect_otrxd(tvbuff_t *tvb, packet_info *pinfo,
647
       proto_tree *tree, void* data _U_)
648
0
{
649
0
  struct otrxd_pdu_info pi = { 0 };
650
0
  proto_tree *otrxd_tree;
651
0
  proto_item *ti, *gi;
652
0
  int offset = 0;
653
654
0
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "OsmoTRXD");
655
0
  col_clear(pinfo->cinfo, COL_INFO);
656
657
0
  ti = proto_tree_add_item(tree, proto_otrxd, tvb, 0, -1, ENC_NA);
658
0
  otrxd_tree = proto_item_add_subtree(ti, ett_otrxd);
659
660
  /* Determine the burst direction */
661
0
  int burst_dir = otrxcd_get_dir(pinfo);
662
663
  /* A burst might be injected by some other program using
664
   * a random source port, so let's try to guess by destport. */
665
0
  if (burst_dir == OTRXCD_DIR_UNKNOWN) {
666
0
    expert_add_info(pinfo, ti, &ei_otrxd_injected_msg);
667
0
    burst_dir = otrxcd_guess_dir(pinfo);
668
0
  }
669
670
0
  if (burst_dir == OTRXCD_DIR_L12TRX)
671
0
    col_append_str(pinfo->cinfo, COL_INFO, "Tx burst (L1 -> TRX): ");
672
0
  else if (burst_dir == OTRXCD_DIR_TRX2L1)
673
0
    col_append_str(pinfo->cinfo, COL_INFO, "Rx burst (TRX -> L1): ");
674
0
  else
675
0
    col_append_str(pinfo->cinfo, COL_INFO, "Tx/Rx burst (Unknown): ");
676
677
  /* Add a generated field, so we can filter bursts by direction */
678
0
  gi = proto_tree_add_uint(otrxd_tree, hf_otrxd_burst_dir,
679
0
         tvb, 0, 0, burst_dir);
680
0
  proto_item_set_generated(gi);
681
682
  /* Parse common TRXD PDU version */
683
0
  proto_tree_add_item_ret_uint(otrxd_tree, hf_otrxd_pdu_ver, tvb,
684
0
             offset, 1, ENC_NA, &pi.ver);
685
0
  proto_item_append_text(ti, " Version %u", pi.ver);
686
687
0
  if (burst_dir == OTRXCD_DIR_L12TRX)
688
0
    offset = dissect_otrxd_tx(tvb, pinfo, ti, otrxd_tree, &pi, offset);
689
0
  else if (burst_dir == OTRXCD_DIR_TRX2L1)
690
0
    offset = dissect_otrxd_rx(tvb, pinfo, ti, otrxd_tree, &pi, offset);
691
0
  else {
692
0
    expert_add_info(pinfo, ti, &ei_otrxd_unknown_dir);
693
0
    offset = 1; /* Only the PDU version was parsed */
694
0
  }
695
696
  /* Summary for all parsed PDUs */
697
0
  if (pi.num_pdus == 1) {
698
0
    col_append_fstr(pinfo->cinfo, COL_INFO, "TDMA FN %07u TN %u", pi.fn, pi.tn);
699
0
    if (pi.mod_str != NULL)
700
0
      col_append_fstr(pinfo->cinfo, COL_INFO, ", Modulation %s", pi.mod_str);
701
0
    else if (pi.nope && burst_dir == OTRXCD_DIR_TRX2L1)
702
0
      col_append_str(pinfo->cinfo, COL_INFO, ", NOPE.ind");
703
0
    else if (pi.nope && burst_dir == OTRXCD_DIR_L12TRX)
704
0
      col_append_str(pinfo->cinfo, COL_INFO, ", NOPE.req");
705
0
  } else if (pi.num_pdus > 1) {
706
0
    col_append_fstr(pinfo->cinfo, COL_INFO, "TDMA FN %07u", pi.fn);
707
0
    col_append_fstr(pinfo->cinfo, COL_INFO, ", %u batched PDUs ", pi.num_pdus);
708
0
  }
709
710
0
  proto_item_set_len(ti, offset);
711
712
  /* Let it warn us if there are unhandled tail octets */
713
0
  if ((unsigned) offset < tvb_reported_length(tvb))
714
0
    expert_add_info(pinfo, ti, &ei_otrxd_tail_octets);
715
716
0
  return offset;
717
0
}
718
719
/* Dissector for Control commands and responses, and Clock indications */
720
static int dissect_otrxc(tvbuff_t *tvb, packet_info *pinfo,
721
       proto_tree *tree, void *data _U_)
722
0
{
723
0
  int offset = 0, msg_len, end_verb, end_status;
724
0
  const uint8_t *msg_str, *msg_type_str;
725
0
  proto_item *ti, *gi, *delim_item;
726
0
  proto_tree *otrxc_tree;
727
0
  uint32_t delimiter;
728
729
0
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "OsmoTRXC");
730
0
  col_clear(pinfo->cinfo, COL_INFO);
731
732
0
  msg_len = tvb_reported_length(tvb);
733
0
  msg_str = tvb_get_string_enc(pinfo->pool, tvb, 0, msg_len, ENC_ASCII);
734
0
  col_add_str(pinfo->cinfo, COL_INFO, msg_str);
735
736
0
  ti = proto_tree_add_item(tree, proto_otrxc, tvb, 0, msg_len, ENC_ASCII);
737
0
  otrxc_tree = proto_item_add_subtree(ti, ett_otrxc);
738
739
  /* Determine the message direction */
740
0
  int msg_dir = otrxcd_get_dir(pinfo);
741
742
  /* A message might be injected by some other program using
743
   * a random source port, so let's try to guess by destport. */
744
0
  if (msg_dir == OTRXCD_DIR_UNKNOWN) {
745
0
    expert_add_info(pinfo, ti, &ei_otrxc_injected_msg);
746
0
    if ((msg_dir = otrxcd_guess_dir(pinfo)) == OTRXCD_DIR_UNKNOWN)
747
0
      expert_add_info(pinfo, ti, &ei_otrxc_unknown_dir);
748
0
  }
749
750
  /* Add a generated field, so we can filter bursts by direction */
751
0
  gi = proto_tree_add_uint(otrxc_tree, hf_otrxc_msg_dir,
752
0
         tvb, 0, 0, msg_dir);
753
0
  proto_item_set_generated(gi);
754
755
  /* First 3 bytes define a type of the message ("IND", "CMD", "RSP") */
756
0
  proto_tree_add_item_ret_string(otrxc_tree, hf_otrxc_type, tvb, offset, 3,
757
0
               ENC_NA | ENC_ASCII, pinfo->pool,
758
0
               &msg_type_str);
759
0
  offset += 3;
760
761
  /* Determine the message type */
762
0
  enum otrxc_msg_type msg_type = str_to_val((const char *) msg_type_str,
763
0
              otrxc_msg_type_enc,
764
0
              OTRXC_MSG_TYPE_UNKNOWN);
765
0
  proto_item_append_text(ti, ", %s", val_to_str_const(msg_type, otrxc_msg_type_desc,
766
0
                  "Unknown message type"));
767
0
  if (msg_type == OTRXC_MSG_TYPE_UNKNOWN) {
768
0
    expert_add_info(pinfo, ti, &ei_otrxc_unknown_msg_type);
769
0
    return offset;
770
0
  }
771
772
  /* The message type is separated by a delimiter */
773
0
  delim_item = proto_tree_add_item_ret_uint(otrxc_tree, hf_otrxc_delimiter,
774
0
              tvb, offset, 1, ENC_ASCII, &delimiter);
775
0
  proto_item_set_hidden(delim_item);
776
0
  offset += 1;
777
778
  /* Delimiter should be a space symbol */
779
0
  if (delimiter != 0x20)
780
0
    expert_add_info(pinfo, delim_item, &ei_otrxc_bad_delimiter);
781
782
  /* The message type is followed by a verb, e.g. "IND CLOCK", "CMD POWEROFF" */
783
0
  end_verb = tvb_find_uint8(tvb, offset, -1, (char) delimiter);
784
0
  if (end_verb < 0) {
785
    /* Just a command without parameters, e.g. "CMD POWERON" */
786
0
    proto_tree_add_item(otrxc_tree, hf_otrxc_verb, tvb,
787
0
                        offset, -1, ENC_ASCII);
788
0
    if (msg_type == OTRXC_MSG_TYPE_RESPONSE)
789
0
      expert_add_info(pinfo, ti, &ei_otrxc_rsp_no_code);
790
0
    return tvb_captured_length(tvb);
791
0
  } else {
792
0
    proto_tree_add_item(otrxc_tree, hf_otrxc_verb, tvb,
793
0
            offset, end_verb - offset,
794
0
            ENC_ASCII);
795
0
    offset = end_verb;
796
0
  }
797
798
  /* Another delimiter between the verb and status code / parameters */
799
0
  delim_item = proto_tree_add_item_ret_uint(otrxc_tree, hf_otrxc_delimiter,
800
0
              tvb, offset, 1, ENC_ASCII, &delimiter);
801
0
  proto_item_set_hidden(delim_item);
802
0
  offset += 1;
803
804
0
  if (msg_type == OTRXC_MSG_TYPE_RESPONSE) {
805
0
    end_status = tvb_find_uint8(tvb, offset, -1, (char) delimiter);
806
0
    if (end_status > 0) {
807
0
      proto_tree_add_item(otrxc_tree, hf_otrxc_status,
808
0
                          tvb, offset, end_status - offset, ENC_ASCII);
809
0
      offset = end_status;
810
811
      /* Another delimiter between the status code and parameters */
812
0
      delim_item = proto_tree_add_item_ret_uint(otrxc_tree, hf_otrxc_delimiter,
813
0
                  tvb, offset, 1, ENC_ASCII, &delimiter);
814
0
      proto_item_set_hidden(delim_item);
815
0
      offset += 1;
816
0
    } else if (offset < msg_len) {
817
      /* Response without parameters, e.g. "RSP POWEROFF 0" */
818
0
      proto_tree_add_item(otrxc_tree, hf_otrxc_status,
819
0
                          tvb, offset, msg_len - offset, ENC_ASCII);
820
0
      return tvb_captured_length(tvb);
821
0
    } else {
822
0
      expert_add_info(pinfo, ti, &ei_otrxc_rsp_no_code);
823
0
      return offset;
824
0
    }
825
0
  }
826
827
0
  if (offset < msg_len) {
828
0
    proto_tree_add_item(otrxc_tree, hf_otrxc_params,
829
0
                        tvb, offset, -1, ENC_ASCII);
830
0
  }
831
832
0
  return tvb_captured_length(tvb);
833
0
}
834
835
void proto_register_osmo_trx(void)
836
14
{
837
14
  static hf_register_info hf_otrxd[] = {
838
    /* Common generated field: burst direction */
839
14
    { &hf_otrxd_burst_dir, { "Burst Direction", "osmo_trx.direction",
840
14
      FT_UINT8, BASE_DEC, VALS(otrxcd_dir_vals), 0, NULL, HFILL } },
841
842
    /* Rx/Tx header fields */
843
14
    { &hf_otrxd_pdu_ver, { "PDU Version", "osmo_trxd.pdu_ver",
844
14
      FT_UINT8, BASE_DEC, NULL, 0xf0, NULL, HFILL } },
845
14
    { &hf_otrxd_chdr_reserved, { "Reserved", "osmo_trxd.chdr_reserved",
846
14
      FT_UINT8, BASE_DEC, NULL, 0x08, NULL, HFILL } },
847
14
    { &hf_otrxd_tdma_tn, { "TDMA Timeslot Number", "osmo_trxd.tdma.tn",
848
14
      FT_UINT8, BASE_DEC, NULL, 0x07, NULL, HFILL } },
849
14
    { &hf_otrxd_tdma_fn, { "TDMA Frame Number", "osmo_trxd.tdma.fn",
850
14
      FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } },
851
14
    { &hf_otrxd_batch_ind, { "BATCH Indication", "osmo_trxd.batch_ind",
852
14
      FT_BOOLEAN, 8, TFS(&otrxd_batch_bool_val), 0x80, NULL, HFILL } },
853
14
    { &hf_otrxd_shadow_ind, { "PDU class", "osmo_trxd.shadow_ind",
854
14
      FT_BOOLEAN, 8, TFS(&otrxd_shadow_bool_val), 0x40, NULL, HFILL } },
855
14
    { &hf_otrxd_trx_num, { "TRX (RF Channel) Number", "osmo_trxd.trx_num",
856
14
      FT_UINT8, BASE_DEC, NULL, 0x3f, NULL, HFILL } },
857
858
    /* Rx header fields */
859
14
    { &hf_otrxd_rssi, { "RSSI", "osmo_trxd.meas.rssi",
860
14
      FT_UINT8, BASE_CUSTOM, CF_FUNC(format_rssi), 0, NULL, HFILL } },
861
14
    { &hf_otrxd_toa256, { "Timing of Arrival", "osmo_trxd.meas.toa256",
862
14
      FT_INT16, BASE_DEC | BASE_UNIT_STRING, UNS(&otrx_units_toa256), 0, NULL, HFILL } },
863
864
    /* MTS (Modulation and Training Sequence) fields */
865
14
    { &hf_otrxd_nope_ind, { "NOPE Indication", "osmo_trxd.nope_ind",
866
14
      FT_BOOLEAN, 8, TFS(&otrxd_nope_bool_val), 0x80, NULL, HFILL } },
867
14
    { &hf_otrxd_nope_ind_pad, { "NOPE Padding", "osmo_trxd.nope_ind_pad",
868
14
      FT_UINT8, BASE_DEC, NULL, 0x7f, NULL, HFILL } },
869
14
    { &hf_otrxd_mod_2b, { "Modulation", "osmo_trxd.mod",
870
14
      FT_UINT8, BASE_DEC, VALS(otrxd_mod_2b_vals), 0x60, NULL, HFILL } },
871
14
    { &hf_otrxd_mod_3b, { "Modulation", "osmo_trxd.mod",
872
14
      FT_UINT8, BASE_DEC, VALS(otrxd_mod_3b_vals), 0x70, NULL, HFILL } },
873
14
    { &hf_otrxd_mod_4b, { "Modulation", "osmo_trxd.mod",
874
14
      FT_UINT8, BASE_DEC, VALS(otrxd_mod_4b_vals), 0x78, NULL, HFILL } },
875
14
    { &hf_otrxd_tsc_set_x2, { "TSC Set", "osmo_trxd.tsc_set",
876
14
      FT_UINT8, BASE_CUSTOM, CF_FUNC(format_tsc_set), 0x08, NULL, HFILL } },
877
14
    { &hf_otrxd_tsc_set_x4, { "TSC Set", "osmo_trxd.tsc_set",
878
14
      FT_UINT8, BASE_CUSTOM, CF_FUNC(format_tsc_set), 0x18, NULL, HFILL } },
879
14
    { &hf_otrxd_tsc, { "TSC (Training Sequence Code)", "osmo_trxd.tsc",
880
14
      FT_UINT8, BASE_DEC, NULL, 0x07, NULL, HFILL } },
881
14
    { &hf_otrxd_ci, { "C/I (Carrier-to-Interference ratio)", "osmo_trxd.meas.ci",
882
14
      FT_INT16, BASE_DEC | BASE_UNIT_STRING, UNS(&units_centibels), 0, NULL, HFILL } },
883
884
    /* Tx header fields */
885
14
    { &hf_otrxd_tx_att, { "Tx Attenuation", "osmo_trxd.tx_att",
886
14
      FT_UINT8, BASE_DEC | BASE_UNIT_STRING, UNS(&units_decibels), 0, NULL, HFILL } },
887
14
    { &hf_otrxd_tx_scpir, { "SCPIR Value", "osmo_trxd.scpir_val",
888
14
      FT_INT8, BASE_DEC | BASE_UNIT_STRING, UNS(&units_decibels), 0, NULL, HFILL } },
889
14
    { &hf_otrxd_tx_rfu, { "Spare padding", "osmo_trxd.spare",
890
14
      FT_BYTES, SEP_SPACE, NULL, 0, NULL, HFILL } },
891
892
    /* Burst soft (255 .. 0) / hard (1 or 0) bits */
893
14
    { &hf_otrxd_soft_symbols, { "Soft-bits", "osmo_trxd.burst.sbits",
894
14
      FT_BYTES, SEP_SPACE, NULL, 0, NULL, HFILL } },
895
14
    { &hf_otrxd_hard_symbols, { "Hard-bits", "osmo_trxd.burst.hbits",
896
14
      FT_BYTES, SEP_SPACE, NULL, 0, NULL, HFILL } },
897
14
    { &hf_otrxd_burst_pad, { "Legacy padding", "osmo_trxd.burst.pad",
898
14
      FT_BYTES, SEP_SPACE, NULL, 0, NULL, HFILL } },
899
14
  };
900
901
14
  static hf_register_info hf_otrxc[] = {
902
    /* Common generated field: message direction */
903
14
    { &hf_otrxc_msg_dir, { "Message Direction", "osmo_trx.direction",
904
14
      FT_UINT8, BASE_DEC, VALS(otrxcd_dir_vals), 0, NULL, HFILL } },
905
906
14
    { &hf_otrxc_type, { "Type", "osmo_trxc.type",
907
14
      FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
908
14
    { &hf_otrxc_delimiter, { "Delimiter", "osmo_trxc.delim",
909
14
      FT_CHAR, BASE_HEX, NULL, 0, NULL, HFILL } },
910
14
    { &hf_otrxc_verb, { "Verb", "osmo_trxc.verb",
911
14
      FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
912
14
    { &hf_otrxc_status, { "Status", "osmo_trxc.status",
913
14
      FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
914
14
    { &hf_otrxc_params, { "Parameters", "osmo_trxc.params",
915
14
      FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
916
14
  };
917
918
14
  static int *ett[] = {
919
14
    &ett_otrxd,
920
14
    &ett_otrxd_rx_pdu,
921
14
    &ett_otrxd_tx_pdu,
922
14
    &ett_otrxc,
923
14
  };
924
925
14
  proto_otrxd = proto_register_protocol("OsmoTRX Data Protocol",
926
14
                "OsmoTRXD", "osmo_trxd");
927
14
  proto_otrxc = proto_register_protocol("OsmoTRX Control / Clock Protocol",
928
14
                "OsmoTRXC", "osmo_trxc");
929
930
14
  proto_register_field_array(proto_otrxd, hf_otrxd, array_length(hf_otrxd));
931
14
  proto_register_field_array(proto_otrxc, hf_otrxc, array_length(hf_otrxc));
932
14
  proto_register_subtree_array(ett, array_length(ett));
933
934
14
  static ei_register_info ei_otrxd[] = {
935
14
    { &ei_otrxd_injected_msg, { "osmo_trx.ei.injected_msg",
936
14
      PI_COMMENTS_GROUP, PI_COMMENT, "Injected message", EXPFILL } },
937
14
    { &ei_otrxd_unknown_dir, { "osmo_trx.ei.unknown_dir",
938
14
      PI_UNDECODED, PI_ERROR, "Unknown direction", EXPFILL } },
939
14
    { &ei_otrxd_unknown_pdu_ver, { "osmo_trxd.ei.unknown_pdu_ver",
940
14
      PI_PROTOCOL, PI_ERROR, "Unknown PDU version", EXPFILL } },
941
14
    { &ei_otrxd_tail_octets, { "osmo_trxd.ei.tail_octets",
942
14
      PI_UNDECODED, PI_WARN, "Unhandled tail octets", EXPFILL } },
943
14
  };
944
945
14
  static ei_register_info ei_otrxc[] = {
946
14
    { &ei_otrxc_injected_msg, { "osmo_trx.ei.injected_msg",
947
14
      PI_COMMENTS_GROUP, PI_COMMENT, "Injected message", EXPFILL } },
948
14
    { &ei_otrxc_unknown_dir, { "osmo_trx.ei.unknown_dir",
949
14
      PI_ASSUMPTION, PI_WARN, "Unknown direction", EXPFILL } },
950
14
    { &ei_otrxc_bad_delimiter, { "osmo_trxc.ei.bad_delimiter",
951
14
      PI_PROTOCOL, PI_WARN, "Invalid delimiter", EXPFILL } },
952
14
    { &ei_otrxc_rsp_no_code, { "osmo_trxc.ei.rsp_no_code",
953
14
      PI_PROTOCOL, PI_ERROR, "Response without status code", EXPFILL } },
954
14
    { &ei_otrxc_unknown_msg_type, { "osmo_trxc.ei.unknown_msg_type",
955
14
      PI_PROTOCOL, PI_ERROR, "Unknown message type", EXPFILL } },
956
14
  };
957
958
  /* Expert info for OsmoTRXD protocol */
959
14
  expert_module_t *expert_otrxd = expert_register_protocol(proto_otrxd);
960
14
  expert_register_field_array(expert_otrxd, ei_otrxd, array_length(ei_otrxd));
961
962
  /* Expert info for OsmoTRXC protocol */
963
14
  expert_module_t *expert_otrxc = expert_register_protocol(proto_otrxc);
964
14
  expert_register_field_array(expert_otrxc, ei_otrxc, array_length(ei_otrxc));
965
966
  /* Register the dissectors */
967
14
  otrxd_handle = register_dissector("osmo_trxd", dissect_otrxd, proto_otrxd);
968
14
  otrxc_handle = register_dissector("osmo_trxc", dissect_otrxc, proto_otrxc);
969
14
}
970
971
void proto_reg_handoff_osmo_trx(void)
972
14
{
973
#if 0
974
/* The TRX-side control interface for C(N) is on port P = B + 2N + 1;
975
 * the corresponding core-side interface for every socket is at P + 100.
976
 * Give a base port B (5700), the master clock interface is at port P = B. */
977
#define OTRXC_UDP_PORTS \
978
  "5701,5703,5800,5801,5803,"  /* The BTS side (osmo-trx, osmo-bts-trx) */ \
979
  "6701,6703,6800,6801,6803"   /* The MS side (trxcon, fake_trx, grgsm_trx) */
980
981
/* The data interface is on an odd numbered port P = B + 2N + 2. */
982
#define OTRXD_UDP_PORTS \
983
  "5702,5802,"  /* The BTS side, TRX0 (osmo-trx, osmo-bts-trx) */ \
984
  "5704,5804,"  /* The BTS side, TRX1 (osmo-trx, osmo-bts-trx) */ \
985
  "6702,6802"   /* The MS side (trxcon, fake_trx, grgsm_trx) */
986
987
  dissector_add_uint_range_with_preference("udp.port", OTRXD_UDP_PORTS, otrxd_handle);
988
  dissector_add_uint_range_with_preference("udp.port", OTRXC_UDP_PORTS, otrxc_handle);
989
#endif
990
991
14
  dissector_add_for_decode_as("udp.port", otrxd_handle);
992
14
  dissector_add_for_decode_as("udp.port", otrxc_handle);
993
14
}
994
995
/*
996
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
997
 *
998
 * Local variables:
999
 * c-basic-offset: 8
1000
 * tab-width: 8
1001
 * indent-tabs-mode: t
1002
 * End:
1003
 *
1004
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1005
 * :indentSize=8:tabSize=8:noTabs=false:
1006
 */