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