Coverage Report

Created: 2026-11-12 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-gsm_rlp.c
Line
Count
Source
1
/* packet-gsm_rlp.c
2
 * Routines for GSM RLP (3GPP TS 24.022) frame dissection
3
 * (C) 2023 Harald Welte <laforge@osmocom.org>
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
#include "config.h"
13
14
#include <epan/packet.h>
15
#include <epan/prefs.h>
16
#include <epan/expert.h>
17
#include <epan/reassemble.h>
18
#include <epan/conversation.h>
19
20
void proto_register_gsmrlp(void);
21
22
static int proto_gsmrlp;
23
24
static int hf_gsmrlp_cr;
25
static int hf_gsmrlp_pf;
26
static int hf_gsmrlp_n_r;
27
static int hf_gsmrlp_n_s;
28
static int hf_gsmrlp_ftype;
29
static int hf_gsmrlp_s_ftype;
30
static int hf_gsmrlp_u_ftype;
31
static int hf_gsmrlp_fcs;
32
static int hf_gsmrlp_fcs_status;
33
34
static int hf_gsmrlp_xid_p_type;
35
static int hf_gsmrlp_xid_p_len;
36
static int hf_gsmrlp_xid_p_val;
37
38
static int ett_gsmrlp;
39
static int ett_gsmrlp_xid;
40
41
static expert_field ei_gsmrlp_fcs_bad;
42
43
static dissector_handle_t l2rcop_handle;
44
static bool decode_as_l2rcop = true;
45
46
/* 3GPP TS 24.002 Section 5.2.1 */
47
enum rlp_ftype {
48
  RLP_FT_U,
49
  RLP_FT_S,
50
  RLP_FT_IS,
51
};
52
53
static const value_string rlp_ftype_vals[] = {
54
  { RLP_FT_U, "U" },
55
  { RLP_FT_S, "S" },
56
  { RLP_FT_IS,  "IS" },
57
  { 0, NULL }
58
};
59
60
/* 3GPP TS 24.002 Section 5.2.1 */
61
enum rlp_u_ftype {
62
  RLP_U_FT_SABM = 0x07,
63
  RLP_U_FT_UA = 0x0c,
64
  RLP_U_FT_DISC = 0x08,
65
  RLP_U_FT_DM = 0x03,
66
  RLP_U_FT_NULL = 0x0f,
67
  RLP_U_FT_UI = 0x00,
68
  RLP_U_FT_XID  = 0x17,
69
  RLP_U_FT_TEST = 0x1c,
70
  RLP_U_FT_REMAP  = 0x11,
71
};
72
static const value_string rlp_ftype_u_vals[] = {
73
  { RLP_U_FT_SABM,  "SABM" },
74
  { RLP_U_FT_UA,    "UA" },
75
  { RLP_U_FT_DISC,  "DISC" },
76
  { RLP_U_FT_DM,    "DM" },
77
  { RLP_U_FT_NULL,  "NULL" },
78
  { RLP_U_FT_UI,    "UI" },
79
  { RLP_U_FT_XID,   "XID" },
80
  { RLP_U_FT_TEST,  "TEST" },
81
  { RLP_U_FT_REMAP, "REMAP" },
82
  { 0, NULL }
83
};
84
85
/* 3GPP TS 24.002 Section 5.2.1 */
86
enum rlp_s_ftype {
87
  RLP_S_FT_RR = 0,
88
  RLP_S_FT_REJ  = 2,
89
  RLP_S_FT_RNR  = 1,
90
  RLP_S_FT_SREJ = 3,
91
};
92
static const value_string rlp_ftype_s_vals[] = {
93
  { RLP_S_FT_RR,    "RR" },
94
  { RLP_S_FT_REJ,   "REJ" },
95
  { RLP_S_FT_RNR,   "RNR" },
96
  { RLP_S_FT_SREJ,  "SREJ" },
97
  { 0, NULL }
98
};
99
100
/* 3GPP TS 24.002 Section 5.2.2.6 */
101
enum rlp_xid_param_type {
102
  XID_P_DELIMITER     = 0,
103
  XID_P_RLP_VERSION   = 1,
104
  XID_P_IWF_TO_UE_WIN_SIZE  = 2,
105
  XID_P_UE_TO_IWF_WIN_SIZE  = 3,
106
  XID_P_ACK_TIMER_T1    = 4,
107
  XID_P_RETRANS_ATTEMPTS_N2 = 5,
108
  XID_P_REPLY_DELAY_T2    = 6,
109
  XID_P_COMPRESSION_PT    = 7,
110
  XID_P_RESEQUENCING_T4   = 8,
111
  XID_P_OPTIONAL_FEATURES   = 9,
112
};
113
114
static const value_string rlp_xid_param_vals[] = {
115
  { XID_P_DELIMITER,    "Delimiter (end of parameters)" },
116
  { XID_P_RLP_VERSION,    "RLP version number" },
117
  { XID_P_IWF_TO_UE_WIN_SIZE, "IWF to UE window size" },
118
  { XID_P_UE_TO_IWF_WIN_SIZE, "UE to IWF window size" },
119
  { XID_P_ACK_TIMER_T1,   "Acknowledgement Timer (T1)" },
120
  { XID_P_RETRANS_ATTEMPTS_N2,  "Retransmission attempts (N2)" },
121
  { XID_P_REPLY_DELAY_T2,   "Reply delay (T2)" },
122
  { XID_P_COMPRESSION_PT,   "Compression PT" },
123
  { XID_P_RESEQUENCING_T4,  "Re-sequencing timer (T4)" },
124
  { XID_P_OPTIONAL_FEATURES,  "Optional Features" },
125
  { 0, NULL }
126
};
127
128
static const uint32_t rlp_fcs_table[256] = {
129
  0x00B29D2D, 0x00643A5B, 0x0044D87A, 0x00927F0C, 0x00051C38, 0x00D3BB4E, 0x00F3596F, 0x0025FE19,
130
  0x008694BC, 0x005033CA, 0x0070D1EB, 0x00A6769D, 0x003115A9, 0x00E7B2DF, 0x00C750FE, 0x0011F788,
131
  0x00DA8E0F, 0x000C2979, 0x002CCB58, 0x00FA6C2E, 0x006D0F1A, 0x00BBA86C, 0x009B4A4D, 0x004DED3B,
132
  0x00EE879E, 0x003820E8, 0x0018C2C9, 0x00CE65BF, 0x0059068B, 0x008FA1FD, 0x00AF43DC, 0x0079E4AA,
133
  0x0062BB69, 0x00B41C1F, 0x0094FE3E, 0x00425948, 0x00D53A7C, 0x00039D0A, 0x00237F2B, 0x00F5D85D,
134
  0x0056B2F8, 0x0080158E, 0x00A0F7AF, 0x007650D9, 0x00E133ED, 0x0037949B, 0x001776BA, 0x00C1D1CC,
135
  0x000AA84B, 0x00DC0F3D, 0x00FCED1C, 0x002A4A6A, 0x00BD295E, 0x006B8E28, 0x004B6C09, 0x009DCB7F,
136
  0x003EA1DA, 0x00E806AC, 0x00C8E48D, 0x001E43FB, 0x008920CF, 0x005F87B9, 0x007F6598, 0x00A9C2EE,
137
  0x0049DA1E, 0x009F7D68, 0x00BF9F49, 0x0069383F, 0x00FE5B0B, 0x0028FC7D, 0x00081E5C, 0x00DEB92A,
138
  0x007DD38F, 0x00AB74F9, 0x008B96D8, 0x005D31AE, 0x00CA529A, 0x001CF5EC, 0x003C17CD, 0x00EAB0BB,
139
  0x0021C93C, 0x00F76E4A, 0x00D78C6B, 0x00012B1D, 0x00964829, 0x0040EF5F, 0x00600D7E, 0x00B6AA08,
140
  0x0015C0AD, 0x00C367DB, 0x00E385FA, 0x0035228C, 0x00A241B8, 0x0074E6CE, 0x005404EF, 0x0082A399,
141
  0x0099FC5A, 0x004F5B2C, 0x006FB90D, 0x00B91E7B, 0x002E7D4F, 0x00F8DA39, 0x00D83818, 0x000E9F6E,
142
  0x00ADF5CB, 0x007B52BD, 0x005BB09C, 0x008D17EA, 0x001A74DE, 0x00CCD3A8, 0x00EC3189, 0x003A96FF,
143
  0x00F1EF78, 0x0027480E, 0x0007AA2F, 0x00D10D59, 0x00466E6D, 0x0090C91B, 0x00B02B3A, 0x00668C4C,
144
  0x00C5E6E9, 0x0013419F, 0x0033A3BE, 0x00E504C8, 0x007267FC, 0x00A4C08A, 0x008422AB, 0x005285DD,
145
  0x001F18F0, 0x00C9BF86, 0x00E95DA7, 0x003FFAD1, 0x00A899E5, 0x007E3E93, 0x005EDCB2, 0x00887BC4,
146
  0x002B1161, 0x00FDB617, 0x00DD5436, 0x000BF340, 0x009C9074, 0x004A3702, 0x006AD523, 0x00BC7255,
147
  0x00770BD2, 0x00A1ACA4, 0x00814E85, 0x0057E9F3, 0x00C08AC7, 0x00162DB1, 0x0036CF90, 0x00E068E6,
148
  0x00430243, 0x0095A535, 0x00B54714, 0x0063E062, 0x00F48356, 0x00222420, 0x0002C601, 0x00D46177,
149
  0x00CF3EB4, 0x001999C2, 0x00397BE3, 0x00EFDC95, 0x0078BFA1, 0x00AE18D7, 0x008EFAF6, 0x00585D80,
150
  0x00FB3725, 0x002D9053, 0x000D7272, 0x00DBD504, 0x004CB630, 0x009A1146, 0x00BAF367, 0x006C5411,
151
  0x00A72D96, 0x00718AE0, 0x005168C1, 0x0087CFB7, 0x0010AC83, 0x00C60BF5, 0x00E6E9D4, 0x00304EA2,
152
  0x00932407, 0x00458371, 0x00656150, 0x00B3C626, 0x0024A512, 0x00F20264, 0x00D2E045, 0x00044733,
153
  0x00E45FC3, 0x0032F8B5, 0x00121A94, 0x00C4BDE2, 0x0053DED6, 0x008579A0, 0x00A59B81, 0x00733CF7,
154
  0x00D05652, 0x0006F124, 0x00261305, 0x00F0B473, 0x0067D747, 0x00B17031, 0x00919210, 0x00473566,
155
  0x008C4CE1, 0x005AEB97, 0x007A09B6, 0x00ACAEC0, 0x003BCDF4, 0x00ED6A82, 0x00CD88A3, 0x001B2FD5,
156
  0x00B84570, 0x006EE206, 0x004E0027, 0x0098A751, 0x000FC465, 0x00D96313, 0x00F98132, 0x002F2644,
157
  0x00347987, 0x00E2DEF1, 0x00C23CD0, 0x00149BA6, 0x0083F892, 0x00555FE4, 0x0075BDC5, 0x00A31AB3,
158
  0x00007016, 0x00D6D760, 0x00F63541, 0x00209237, 0x00B7F103, 0x00615675, 0x0041B454, 0x00971322,
159
  0x005C6AA5, 0x008ACDD3, 0x00AA2FF2, 0x007C8884, 0x00EBEBB0, 0x003D4CC6, 0x001DAEE7, 0x00CB0991,
160
  0x00686334, 0x00BEC442, 0x009E2663, 0x00488115, 0x00DFE221, 0x00094557, 0x0029A776, 0x00FF0000
161
};
162
163
/*! compute RLP FCS according to 3GPP TS 24.022 Section 4.4 */
164
static uint32_t rlp_fcs_compute(const unsigned char *in, size_t in_len)
165
1
{
166
1
  uint32_t divider = 0;
167
1
  size_t i;
168
169
661
  for (i = 0; i < in_len; i++) {
170
660
    unsigned char input = in[i] ^ (divider & 0xff);
171
660
    divider = (divider >> 8) ^ rlp_fcs_table[input];
172
660
  }
173
174
1
  return ((divider & 0xff) << 16) | (divider & 0xff00) | ((divider & 0xff0000) >> 16);
175
1
}
176
177
static int
178
dissect_gsmrlp_xid(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
179
0
{
180
0
  int cur;
181
182
0
  for (cur = offset; cur < (int) tvb_reported_length(tvb);) {
183
0
    uint8_t len = tvb_get_uint8(tvb, cur) & 0x0f;
184
0
    uint8_t type = tvb_get_uint8(tvb, cur) >> 4;
185
0
    proto_tree *xid_tree;
186
187
0
    proto_tree_add_subtree_format(tree, tvb, cur, 1 + len, ett_gsmrlp_xid, &xid_tree, "XID Parameter: %s",
188
0
                            val_to_str_const(type, rlp_xid_param_vals, "Unknown"));
189
0
    proto_tree_add_item(xid_tree, hf_gsmrlp_xid_p_type, tvb, cur, 1, ENC_BIG_ENDIAN);
190
0
    proto_tree_add_item(xid_tree, hf_gsmrlp_xid_p_len, tvb, cur, 1, ENC_BIG_ENDIAN);
191
0
    if (len)
192
0
      proto_tree_add_item(xid_tree, hf_gsmrlp_xid_p_val, tvb, cur + 1, len, ENC_NA);
193
0
    cur += 1 + len;
194
0
    if (type == XID_P_DELIMITER)
195
0
      break;
196
0
  }
197
198
0
  return cur - offset;
199
0
}
200
201
202
/* Dissect a RLP v0/v1 message as described in TS 24.022 section 5.2 */
203
static int
204
dissect_gsmrlp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
205
1
{
206
1
  int reported_len = tvb_reported_length(tvb);
207
1
  proto_tree *rlp_tree;
208
1
  proto_item *ti;
209
1
  uint8_t n_s, n_r;
210
211
  /* we currently support the 16bit header of RLP v0 + v1 */
212
213
1
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "GSM-RLP");
214
215
1
  n_s = (tvb_get_uint8(tvb, 0)) >> 3 | ((tvb_get_uint8(tvb, 1) & 1) << 5);
216
1
  n_r = (tvb_get_uint8(tvb, 1) >> 2);
217
218
1
  ti = proto_tree_add_protocol_format(tree, proto_gsmrlp, tvb, 0, reported_len,
219
1
              "GSM RLP");
220
1
  rlp_tree = proto_item_add_subtree(ti, ett_gsmrlp);
221
222
1
  proto_tree_add_item(rlp_tree, hf_gsmrlp_cr, tvb, 0, 1, ENC_BIG_ENDIAN);
223
1
  proto_tree_add_item(rlp_tree, hf_gsmrlp_pf, tvb, 1, 1, ENC_BIG_ENDIAN);
224
1
  if (n_s == 0x3f) { /* U frame */
225
0
    unsigned u_ftype;
226
0
    proto_tree_add_uint(rlp_tree, hf_gsmrlp_ftype, tvb, 0, 1, RLP_FT_U);
227
0
    proto_tree_add_item_ret_uint(rlp_tree, hf_gsmrlp_u_ftype, tvb, 1, 1, ENC_BIG_ENDIAN, &u_ftype);
228
0
    if ((n_r & 0x1f) == RLP_U_FT_XID)
229
0
      dissect_gsmrlp_xid(tvb, 2, pinfo, rlp_tree);
230
0
    proto_item_append_text(ti, " U-Frame: %s", val_to_str(pinfo->pool, u_ftype, rlp_ftype_u_vals, "Unknown 0x%02x"));
231
1
  } else if (n_s == 0x3e) { /* S Frame */
232
0
    unsigned s_ftype;
233
0
    proto_tree_add_uint(rlp_tree, hf_gsmrlp_ftype, tvb, 0, 1, RLP_FT_S);
234
0
    proto_tree_add_item_ret_uint(rlp_tree, hf_gsmrlp_s_ftype, tvb, 0, 1, ENC_BIG_ENDIAN, &s_ftype);
235
0
    proto_tree_add_uint(rlp_tree, hf_gsmrlp_n_r, tvb, 1, 1, n_r);
236
0
    proto_item_append_text(ti, " S-Frame: %s, N(S): %u, N(R): %u",
237
0
               val_to_str(pinfo->pool, s_ftype, rlp_ftype_s_vals, "Unknown 0x%02x"), n_s, n_r);
238
1
  } else { /* IS Frame */
239
1
    tvbuff_t *next_tvb;
240
1
    unsigned s_ftype;
241
1
    int data_len;
242
243
1
    proto_tree_add_uint(rlp_tree, hf_gsmrlp_ftype, tvb, 0, 1, RLP_FT_IS);
244
1
    proto_tree_add_item_ret_uint(rlp_tree, hf_gsmrlp_s_ftype, tvb, 0, 1, ENC_BIG_ENDIAN, &s_ftype);
245
1
    proto_tree_add_uint(rlp_tree, hf_gsmrlp_n_s, tvb, 0, 2, n_s);
246
1
    proto_tree_add_uint(rlp_tree, hf_gsmrlp_n_r, tvb, 1, 1, n_r);
247
1
    proto_item_append_text(ti, " IS-Frame: %s, N(S): %u, N(R): %u",
248
1
               val_to_str(pinfo->pool, s_ftype, rlp_ftype_s_vals, "Unknown 0x%02x"), n_s, n_r);
249
250
    /* dispatch user data */
251
1
    data_len = reported_len - 2 /* header */ - 3 /* FCS */;
252
1
    next_tvb = tvb_new_subset_length(tvb, 2, data_len);
253
1
    if (decode_as_l2rcop && l2rcop_handle)
254
1
      call_dissector(l2rcop_handle, next_tvb, pinfo, rlp_tree);
255
0
    else
256
0
      call_data_dissector(next_tvb, pinfo, rlp_tree);
257
1
  }
258
259
  /* FCS is always the last 3 bytes of the message */
260
1
  tvb_ensure_bytes_exist(tvb, 0, reported_len - 3);
261
1
  uint32_t fcs_computed = rlp_fcs_compute(tvb_get_ptr(tvb, 0, reported_len - 3), reported_len - 3);
262
1
  proto_tree_add_checksum(rlp_tree, tvb, reported_len - 3, hf_gsmrlp_fcs, hf_gsmrlp_fcs_status,
263
1
        &ei_gsmrlp_fcs_bad, pinfo, fcs_computed, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
264
265
1
  return tvb_reported_length(tvb);
266
1
}
267
268
void
269
proto_register_gsmrlp(void)
270
16
{
271
16
  static hf_register_info hf[] = {
272
16
      { &hf_gsmrlp_cr,
273
16
        { "C/R", "gsm_rlp.cr", FT_UINT8, BASE_DEC, NULL, 0x01,
274
16
    "Command/Response bit", HFILL }},
275
16
      { &hf_gsmrlp_pf,
276
16
        { "P/F", "gsm_rlp.pf", FT_UINT8, BASE_DEC, NULL, 0x02,
277
16
    "Poll/Final bit", HFILL }},
278
16
      { &hf_gsmrlp_n_r,
279
16
        { "N(R)", "gsm_rlp.n_r", FT_UINT8, BASE_DEC, NULL, 0,
280
16
    "Receive Sequence Number", HFILL }},
281
16
      { &hf_gsmrlp_n_s,
282
16
        { "N(S)", "gsm_rlp.n_s", FT_UINT8, BASE_DEC, NULL, 0,
283
16
    "Send Sequence Number", HFILL }},
284
16
      { &hf_gsmrlp_ftype,
285
16
        { "Frame type", "gsm_rlp.ftype", FT_UINT8, BASE_HEX,
286
16
    VALS(rlp_ftype_vals), 0, NULL, HFILL }},
287
16
      { &hf_gsmrlp_u_ftype,
288
16
        { "U Frame type", "gsm_rlp.u_ftype", FT_UINT8, BASE_HEX,
289
16
    VALS(rlp_ftype_u_vals), 0x7c, NULL, HFILL }},
290
16
      { &hf_gsmrlp_s_ftype,
291
16
        { "S frame type", "gsm_rlp.s_ftype", FT_UINT8, BASE_HEX,
292
16
    VALS(rlp_ftype_s_vals), 0x06, NULL, HFILL }},
293
16
      { &hf_gsmrlp_fcs,
294
16
        { "Frame Check Sequence", "gsm_rlp.fcs", FT_UINT24, BASE_HEX,
295
16
    NULL, 0, NULL, HFILL }},
296
16
      { &hf_gsmrlp_fcs_status,
297
16
        { "FCS Status", "gsm_rlp.fcs.status", FT_UINT8, BASE_NONE,
298
16
    VALS(proto_checksum_vals), 0, NULL, HFILL }},
299
300
16
      { &hf_gsmrlp_xid_p_type,
301
16
        { "XID Parameter Type", "gsm_rlp.xid.param_type", FT_UINT8, BASE_HEX,
302
16
    VALS(rlp_xid_param_vals), 0xf0, NULL, HFILL }},
303
16
      { &hf_gsmrlp_xid_p_len,
304
16
        { "XID Parameter Length", "gsm_rlp.xid.param_len", FT_UINT8, BASE_DEC,
305
16
    NULL, 0x0f, NULL, HFILL }},
306
16
      { &hf_gsmrlp_xid_p_val,
307
16
        { "XID Parameter Value", "gsm_rlp.xid.param_value", FT_BYTES, BASE_NONE,
308
16
    NULL, 0, NULL, HFILL }},
309
16
  };
310
16
  static int *ett[] = {
311
16
    &ett_gsmrlp,
312
16
    &ett_gsmrlp_xid,
313
16
  };
314
16
  static ei_register_info ei[] = {
315
16
    { &ei_gsmrlp_fcs_bad, { "gsm_rlp.fcs_bad", PI_CHECKSUM, PI_ERROR, "Bad checksum" , EXPFILL }},
316
16
  };
317
16
  module_t *rlp_module;
318
16
  expert_module_t *expert_gsmrlp;
319
320
16
  proto_gsmrlp = proto_register_protocol("GSM Radio Link Protocol (RLP)", "GSM-RLP", "gsm_rlp");
321
16
  proto_register_field_array(proto_gsmrlp, hf, array_length(hf));
322
16
  proto_register_subtree_array(ett, array_length(ett));
323
16
  expert_gsmrlp = expert_register_protocol(proto_gsmrlp);
324
16
  expert_register_field_array(expert_gsmrlp, ei, array_length(ei));
325
326
16
  register_dissector("gsm_rlp", dissect_gsmrlp, proto_gsmrlp);
327
328
16
  rlp_module = prefs_register_protocol(proto_gsmrlp, NULL);
329
16
  prefs_register_bool_preference(rlp_module, "decode_as_l2rcop", "Decode payload as L2RCOP",
330
16
               NULL, &decode_as_l2rcop);
331
16
}
332
void
333
proto_reg_handoff_gsmrlp(void)
334
16
{
335
16
  l2rcop_handle = find_dissector_add_dependency("gsm_l2rcop", proto_gsmrlp);
336
16
}
337
338
/*
339
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
340
 *
341
 * Local variables:
342
 * c-basic-offset: 4
343
 * tab-width: 8
344
 * indent-tabs-mode: nil
345
 * End:
346
 *
347
 * vi: set shiftwidth=4 tabstop=8 expandtab:
348
 * :indentSize=4:tabSize=8:noTabs=true:
349
 */