Coverage Report

Created: 2026-05-14 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-realtek.c
Line
Count
Source
1
/* packet-realtek.c
2
 * Routines for Realtek layer 2 protocols dissection
3
 *
4
 * Based on code from a 2004 submission
5
 * Copyright 2004, Horst Kronstorfer <hkronsto@frequentis.com>
6
 * but significantly modernized.
7
 *
8
 * Wireshark - Network traffic analyzer
9
 * By Gerald Combs <gerald@ethereal.com>
10
 * Copyright 1998 Gerald Combs
11
 *
12
 * SPDX-License-Identifier: GPL-2.0-or-later
13
 */
14
15
#include "config.h"
16
17
#include <string.h>
18
#include <epan/packet.h>
19
#include <etypes.h>
20
21
void proto_register_realtek(void);
22
void proto_reg_handoff_realtek(void);
23
24
1
#define RTL_PROTOCOL_RRCP    0x01    /* RRCP */
25
1
#define RTL_PROTOCOL_REP     0x02    /* REP */
26
2
#define RTL_PROTOCOL_RLDP    0x03    /* RLDP */
27
1
#define RTL_PROTOCOL_RLDP2   0x23    /* also RLDP */
28
#define RTL_PROTOCOL_XXX_DSA 0x04    /* DSA protocol for some chip(s) */
29
30
/*
31
 * Values for the upper 4 bits of the protocol field, for
32
 * protocols where the lower 4 bits contain protocol data.
33
 *
34
 * See section 8.10 "CPU Tag Function" of
35
 *
36
 *    http://realtek.info/pdf/rtl8306sd%28m%29_datasheet_1.1.pdf
37
 *
38
 * for the RTL8306 DSA protocol tag format.
39
 */
40
#define RTL_PROTOCOL_8306_DSA   0x9    /* RTL8306 DSA protocol */
41
#define RTL_PROTOCOL_8366RB_DSA 0xA    /* RTL8366RB DSA protocol */
42
43
enum {
44
  RRCP_OPCODE_HELLO = 0,
45
  RRCP_OPCODE_GET = 1,
46
  RRCP_OPCODE_SET = 2
47
};
48
49
/* HELLO, HELLO_REPLY, GET, GET_REPLY, SET */
50
0
#define RRCP_OPCODE_FIELD_LENGTH 1
51
0
#define RRCP_REPLY_FIELD_LENGTH RRCP_OPCODE_FIELD_LENGTH
52
15
#define RRCP_REPLY_MASK 0x80
53
#define RRCP_REPLY_BIT_POS 7
54
15
#define RRCP_OPCODE_MASK 0x7f
55
0
#define RRCP_AUTHKEY_FIELD_LENGTH 2
56
/* GET, GET_REPLY, SET */
57
0
#define RRCP_REGADDR_FIELD_LENGTH 2
58
/* GET_REPLY, SET */
59
0
#define RRCP_REGDATA_FIELD_LENGTH 2
60
/* HELLO_REPLY */
61
0
#define RRCP_DLPORT_FIELD_LENGTH 1
62
0
#define RRCP_ULPORT_FIELD_LENGTH 1
63
0
#define RRCP_ULMAC_FIELD_LENGTH 6
64
0
#define RRCP_CHIPID_FIELD_LENGTH 2
65
0
#define RRCP_VENDID_FIELD_LENGTH 4
66
67
#define RRCP_HELLO_PACKET_LENGTH 4
68
#define RRCP_HELLO_REPLY_PACKET_LENGTH 18
69
#define RRCP_GET_SET_PACKET_LENGTH 8
70
71
static const value_string rrcp_opcode_names[] = {
72
   { RRCP_OPCODE_HELLO, "Hello" },
73
   { RRCP_OPCODE_GET,   "Get" },
74
   { RRCP_OPCODE_SET,   "Set" },
75
   {0, NULL}
76
};
77
78
static dissector_handle_t realtek_handle;
79
80
static int proto_realtek;
81
82
static int hf_realtek_packet;
83
84
static int proto_rrcp;
85
86
static int hf_rrcp_protocol;
87
static int hf_rrcp_reply;
88
static int hf_rrcp_opcode;
89
static int hf_rrcp_authkey;
90
static int hf_rrcp_regaddr;
91
static int hf_rrcp_regdata;
92
static int hf_rrcp_hello_reply_dl_port;
93
static int hf_rrcp_hello_reply_ul_port;
94
static int hf_rrcp_hello_reply_ul_mac;
95
static int hf_rrcp_hello_reply_chip_id;
96
static int hf_rrcp_hello_reply_vendor_id;
97
98
static int proto_rep;
99
static int hf_rep_protocol;
100
101
static int proto_rldp;
102
static int hf_rldp_protocol;
103
104
static int ett_realtek;
105
static int ett_rrcp;
106
static int ett_rep;
107
static int ett_rldp;
108
109
static heur_dissector_list_t realtek_heur_subdissector_list;
110
111
static const uint8_t ether_mac_bcast[] = {
112
   0xff, 0xff, 0xff, 0xff, 0xff, 0xff
113
};
114
115
/* Code to actually dissect the Realtek protocols */
116
static int
117
dissect_realtek(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
118
1
{
119
1
  proto_item *ti;
120
1
  proto_tree *realtek_tree;
121
1
  heur_dtbl_entry_t *hdtbl_entry;
122
123
1
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "Realtek");
124
1
  col_clear(pinfo->cinfo, COL_INFO);
125
126
1
  ti = proto_tree_add_item(tree, proto_realtek, tvb, 0, -1, ENC_NA);
127
1
  realtek_tree = proto_item_add_subtree(ti, ett_realtek);
128
129
1
  if (!dissector_try_heuristic(realtek_heur_subdissector_list, tvb, pinfo,
130
1
                               tree, &hdtbl_entry, NULL)) {
131
1
    proto_tree_add_item(realtek_tree, hf_realtek_packet, tvb, 0, -1, ENC_NA);
132
1
  }
133
1
  return tvb_captured_length(tvb);
134
1
}
135
136
/*
137
 * See section 8.20 "Realtek Remote Control Protocol" of
138
 *
139
 *    http://realtek.info/pdf/rtl8324.pdf
140
 *
141
 * and section 7.22 "Realtek Remote Control Protocol" of
142
 *
143
 *    http://realtek.info/pdf/rtl8326.pdf
144
 *
145
 * and this page on the OpenRRCP Wiki:
146
 *
147
 *    http://openrrcp.org.ru/wiki/rrcp_protocol
148
 *
149
 * for information on RRCP.
150
 */
151
static bool
152
dissect_rrcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
153
1
{
154
1
  proto_item *ti;
155
1
  proto_tree *rrcp_tree;
156
1
  uint8_t proto;
157
1
  int offset = 0;
158
1
  bool reply;
159
1
  uint32_t opcode;
160
161
1
  if (!tvb_bytes_exist(tvb, 0, 1))
162
0
    return false;
163
1
  proto = tvb_get_uint8(tvb, 0);
164
1
  if (proto != RTL_PROTOCOL_RRCP)
165
1
    return false;
166
167
0
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "RRCP");
168
0
  col_clear(pinfo->cinfo, COL_INFO);
169
170
0
  ti = proto_tree_add_item(tree, proto_rrcp, tvb, 0, -1, ENC_NA);
171
0
  rrcp_tree = proto_item_add_subtree(ti, ett_rrcp);
172
173
0
  proto_tree_add_uint(rrcp_tree, hf_rrcp_protocol, tvb, offset, 1,
174
0
                      proto);
175
0
  offset += 1;
176
0
  proto_tree_add_item_ret_boolean(rrcp_tree, hf_rrcp_reply, tvb,
177
0
                                  offset, RRCP_REPLY_FIELD_LENGTH,
178
0
                                  ENC_NA, &reply);
179
0
  proto_tree_add_item_ret_uint(rrcp_tree, hf_rrcp_opcode, tvb,
180
0
                               offset, RRCP_OPCODE_FIELD_LENGTH,
181
0
                               ENC_NA, &opcode);
182
0
  col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s",
183
0
               val_to_str(pinfo->pool, opcode, rrcp_opcode_names, "Unknown (%u)"),
184
0
               (reply) ? "Reply" : "Request");
185
0
  offset += RRCP_OPCODE_FIELD_LENGTH;
186
187
0
  proto_tree_add_item(rrcp_tree, hf_rrcp_authkey, tvb, offset,
188
0
                      RRCP_AUTHKEY_FIELD_LENGTH, ENC_BIG_ENDIAN);
189
0
  offset += RRCP_AUTHKEY_FIELD_LENGTH;
190
191
0
  if ((RRCP_OPCODE_GET == opcode) || (RRCP_OPCODE_SET == opcode)) {
192
0
    proto_tree_add_item(rrcp_tree, hf_rrcp_regaddr, tvb, offset,
193
0
                        RRCP_REGADDR_FIELD_LENGTH, ENC_BIG_ENDIAN);
194
0
    offset += RRCP_REGADDR_FIELD_LENGTH;
195
0
    proto_tree_add_item(rrcp_tree, hf_rrcp_regdata, tvb, offset,
196
0
                        RRCP_REGDATA_FIELD_LENGTH, ENC_BIG_ENDIAN);
197
0
    offset += RRCP_REGDATA_FIELD_LENGTH;
198
0
  }
199
0
  else if (RRCP_OPCODE_HELLO == opcode) {
200
0
    if (reply) {
201
0
      proto_tree_add_item(rrcp_tree, hf_rrcp_hello_reply_dl_port, tvb,
202
0
                          offset, RRCP_DLPORT_FIELD_LENGTH, ENC_NA);
203
0
      offset += RRCP_DLPORT_FIELD_LENGTH;
204
0
      proto_tree_add_item(rrcp_tree, hf_rrcp_hello_reply_ul_port, tvb,
205
0
                          offset, RRCP_ULPORT_FIELD_LENGTH, ENC_NA);
206
0
      offset += RRCP_ULPORT_FIELD_LENGTH;
207
0
      proto_tree_add_item(rrcp_tree, hf_rrcp_hello_reply_ul_mac, tvb,
208
0
                          offset, RRCP_ULMAC_FIELD_LENGTH, ENC_NA);
209
0
      offset += RRCP_ULMAC_FIELD_LENGTH;
210
0
      proto_tree_add_item(rrcp_tree, hf_rrcp_hello_reply_chip_id, tvb,
211
0
                          offset, RRCP_CHIPID_FIELD_LENGTH, ENC_BIG_ENDIAN);
212
0
      offset += RRCP_CHIPID_FIELD_LENGTH;
213
0
      proto_tree_add_item(rrcp_tree, hf_rrcp_hello_reply_vendor_id, tvb,
214
0
                          offset, RRCP_VENDID_FIELD_LENGTH, ENC_BIG_ENDIAN);
215
0
      offset += RRCP_VENDID_FIELD_LENGTH;
216
0
    }
217
0
  }
218
0
  proto_item_set_end(ti, tvb, offset);
219
  /* Let 'packet-eth' provide trailer/pad-bytes info */
220
0
  tvb_set_reported_length(tvb, offset);
221
0
  return true;
222
1
}
223
224
/*
225
 * See section 8.22 "Realtek Echo Protocol" of
226
 *
227
 *    http://realtek.info/pdf/rtl8324.pdf
228
 *
229
 * and section 7.24 "Realtek Echo Protocol" of
230
 *
231
 *    http://realtek.info/pdf/rtl8326.pdf
232
 *
233
 * for information on REP.
234
 */
235
static bool
236
dissect_rep(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
237
1
{
238
1
  proto_item *ti;
239
1
  proto_tree *rep_tree;
240
1
  uint8_t proto;
241
1
  int offset = 0;
242
1
  bool bcast;
243
244
1
  if (!tvb_bytes_exist(tvb, 0, 1))
245
0
    return false;
246
1
  proto = tvb_get_uint8(tvb, 0);
247
1
  if (proto != RTL_PROTOCOL_REP)
248
1
    return false;
249
250
0
  ti = proto_tree_add_item(tree, proto_rep, tvb, 0, -1, ENC_NA);
251
0
  rep_tree = proto_item_add_subtree(ti, ett_rep);
252
253
0
  bcast = (pinfo->dst.type == AT_ETHER &&
254
0
           memcmp(pinfo->dst.data, ether_mac_bcast, 6) == 0);
255
256
0
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "REP");
257
0
  col_add_fstr(pinfo->cinfo, COL_INFO,
258
0
               "Echo %s", (bcast) ? "Request" : "Reply");
259
260
0
  proto_tree_add_uint(rep_tree, hf_rep_protocol, tvb, offset, 1,
261
0
                      proto);
262
0
  offset += 1;
263
264
0
  proto_item_set_end(ti, tvb, offset);
265
  /* Let 'packet-eth' provide trailer/pad-bytes info */
266
0
  tvb_set_reported_length(tvb, offset);
267
0
  return true;
268
1
}
269
270
/*
271
 * See section 8.21 "Network Loop Connection Fault Detection" of
272
 *
273
 *    http://realtek.info/pdf/rtl8324.pdf
274
 *
275
 * and section 7.23 "Network Loop Connection Fault Detection" of
276
 *
277
 *    http://realtek.info/pdf/rtl8326.pdf
278
 *
279
 * for information on RLDP.
280
 *
281
 * See also section 7.3.8 "Loop Detection" of
282
 *
283
 *    http://www.ibselectronics.com/ibsstore/datasheet/RTL8306E-CG.pdf
284
 *
285
 * (revision 1.1 of the RTL8306E-CG datasheet), which describes a loop
286
 * detection protocol for which the payload has a 16-bit (presumably
287
 * big-endian) field containing the value 0x0300, followed by what is
288
 * presumably a 16-bit big-endian field the upper 12 bits of which are 0
289
 * and the lower 4 bits of which are a TTL value, followed by zeroes to
290
 * pad the packet out to the minimum Ethernet packet size.
291
 *
292
 * See also section 7.3.13 "Loop Detection" of
293
 *
294
 *    http://realtek.info/pdf/rtl8305sb.pdf
295
 *
296
 * (revision 1.3 of the RTL8305SB datasheet), which describes a similar
297
 * loop detection protocol that lacks the TTL field - all the bytes
298
 * after 0x0300 are zero.
299
 *
300
 * See also section 7.3.7 "Loop Detection" of
301
 *
302
 *    https://datasheet.lcsc.com/lcsc/1810221720_Realtek-Semicon-RTL8305NB-CG_C52146.pdf
303
 *
304
 * (revision 1.0 of the RTL8305NB-CT datasheet), which describes a loop
305
 * detection protocol similar to the one from the RTL8306E-CG datasheet,
306
 * except that the first value is 0x2300, not 0x0300.
307
 *
308
 * And, on top of all that, I've seen packets where the first octet of
309
 * the packet is 0x23, and that's followed by 6 unknown octets (a MAC
310
 * address of some sort?  It differs from packet to packet in a capture),
311
 * followed by the MAC address that appears in the source address in the
312
 * Ethernet header (possibly the originator, in case the packet is forwarded,
313
 * in which case the forwarded packets won't have the source address from
314
 * the Ethernet header there), followed by unknown stuff (0x0d followed by
315
 * zeroes for all such packets in one capture, 0x01 followed by zeroes for
316
 * all such packets in another capture, 0x07 followed by 0x20's for all
317
 * such packets in yet another capture).  The OpenRRCP issue at
318
 * https://github.com/illarionov/OpenRRCP/issues/3 shows a capture
319
 * similar to the last of those, but with 0x02 instead of 0x07.  Or is that
320
 * just crap in the buffer in which the chip constructed the packet, left
321
 * over from something else?
322
 */
323
static bool
324
dissect_rldp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
325
1
{
326
1
  proto_item *ti;
327
1
  proto_tree *rldp_tree;
328
1
  uint8_t proto;
329
1
  int offset = 0;
330
331
1
  if (!tvb_bytes_exist(tvb, 0, 1))
332
0
    return false;
333
1
  proto = tvb_get_uint8(tvb, 0);
334
1
  if (proto != RTL_PROTOCOL_RLDP && proto != RTL_PROTOCOL_RLDP2)
335
1
    return false;
336
337
0
  ti = proto_tree_add_item(tree, proto_rldp, tvb, 0, -1, ENC_NA);
338
0
  rldp_tree = proto_item_add_subtree(ti, ett_rep);
339
340
0
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "RLDP");
341
0
  col_set_str(pinfo->cinfo, COL_INFO, "Network Loop Detection");
342
343
0
  proto_tree_add_uint(rldp_tree, hf_rldp_protocol, tvb, offset, 1,
344
0
                      proto);
345
0
  offset += 1;
346
347
0
  proto_item_set_end(ti, tvb, offset);
348
  /* Let 'packet-eth' provide trailer/pad-bytes info */
349
0
  tvb_set_reported_length(tvb, offset);
350
0
  return true;
351
1
}
352
353
/* Register the protocol with Ethereal */
354
void
355
proto_register_realtek(void)
356
15
{
357
15
  static hf_register_info hf_realtek[] = {
358
15
    { &hf_realtek_packet, {
359
15
       "Unknown packet", "realtek.packet", FT_BYTES, BASE_NONE,
360
15
       NULL, 0x0, NULL, HFILL }},
361
15
  };
362
363
15
  static hf_register_info hf_rrcp[] = {
364
15
    { &hf_rrcp_protocol, {
365
15
       "Protocol", "rrcp.protocol", FT_UINT8, BASE_HEX,
366
15
       NULL, 0x0, NULL, HFILL }},
367
15
    { &hf_rrcp_reply, {
368
15
       "Reply", "rrcp.reply", FT_BOOLEAN, 8,
369
15
       NULL, RRCP_REPLY_MASK, "RRCP reply flag", HFILL}},
370
15
    { &hf_rrcp_opcode, {
371
15
       "Opcode", "rrcp.opcode", FT_UINT8, BASE_HEX,
372
15
       VALS(rrcp_opcode_names), RRCP_OPCODE_MASK, "RRCP operation code",
373
15
       HFILL }},
374
15
    { &hf_rrcp_authkey, {
375
15
       "Authentication key", "rrcp.authkey", FT_UINT16, BASE_HEX,
376
15
       NULL, 0, "RRCP authentication key", HFILL }},
377
15
    { &hf_rrcp_regaddr, {
378
15
       "Register address", "rrcp.regaddr", FT_UINT16, BASE_HEX,
379
15
       NULL, 0, "RRCP register address", HFILL }},
380
15
    { &hf_rrcp_regdata, {
381
15
       "Register data", "rrcp.regdata", FT_UINT16, BASE_HEX,
382
15
       NULL, 0, "RRCP register data", HFILL }},
383
15
    { &hf_rrcp_hello_reply_dl_port, {
384
15
       "Downlink port number", "rrcp.hello_reply.downlink_port",
385
15
       FT_UINT8, BASE_DEC, NULL, 0, "RRCP hello reply downlink port", HFILL }},
386
15
    { &hf_rrcp_hello_reply_ul_port, {
387
15
       "Uplink port number", "rrcp.hello_reply.uplink_port", FT_UINT8,
388
15
       BASE_DEC, NULL, 0, "RRCP hello reply uplink port", HFILL }},
389
15
    { &hf_rrcp_hello_reply_ul_mac, {
390
15
       "Uplink MAC address", "rrcp.hello_reply.uplink_mac", FT_ETHER,
391
15
       BASE_NONE, NULL, 0, "RRCP hello reply uplink MAC address", HFILL }},
392
15
    { &hf_rrcp_hello_reply_chip_id, {
393
15
       "Chip ID", "rrcp.hello_reply.chip_id", FT_UINT16,
394
15
       BASE_HEX, NULL, 0, "RRCP hello reply chip ID", HFILL }},
395
15
    { &hf_rrcp_hello_reply_vendor_id, {
396
15
       "Vendor ID", "rrcp.hello_reply.vendor_id", FT_UINT32, BASE_HEX,
397
15
       NULL, 0, "RRCP hello reply vendor ID", HFILL }}
398
15
  };
399
400
15
  static hf_register_info hf_rep[] = {
401
15
    { &hf_rep_protocol, {
402
15
       "Protocol", "rep.protocol", FT_UINT8, BASE_HEX,
403
15
       NULL, 0x0, NULL, HFILL }},
404
15
  };
405
406
15
  static hf_register_info hf_rldp[] = {
407
15
    { &hf_rldp_protocol, {
408
15
       "Protocol", "rldp.protocol", FT_UINT8, BASE_HEX,
409
15
       NULL, 0x0, NULL, HFILL }},
410
15
  };
411
412
15
  static int *ett[] = {
413
15
    &ett_realtek,
414
15
    &ett_rrcp,
415
15
    &ett_rep,
416
15
    &ett_rldp
417
15
  };
418
419
15
  proto_realtek = proto_register_protocol("Realtek Layer 2 Protocols",
420
15
                                          "Realtek", "realtek");
421
15
  realtek_handle = register_dissector("realtek", dissect_realtek, proto_realtek);
422
15
  proto_register_field_array(proto_realtek, hf_realtek, array_length(hf_realtek));
423
15
  realtek_heur_subdissector_list = register_heur_dissector_list_with_description("realtek",
424
15
                                                                "Realtek Layer 2 payload",
425
15
                                                                proto_realtek);
426
427
15
  proto_rrcp = proto_register_protocol("Realtek Remote Control Protocol",
428
15
                                       "RRCP", "rrcp");
429
15
  proto_register_field_array(proto_rrcp, hf_rrcp, array_length(hf_rrcp));
430
431
15
  proto_rep = proto_register_protocol("Realtek Echo Protocol",
432
15
                                      "REP", "rep");
433
15
  proto_register_field_array(proto_rrcp, hf_rep, array_length(hf_rep));
434
435
15
  proto_rldp = proto_register_protocol("Realtek Loop Detection Protocol",
436
15
                                       "RLDP", "rldp");
437
15
  proto_register_field_array(proto_rrcp, hf_rldp, array_length(hf_rldp));
438
439
15
  proto_register_subtree_array(ett, array_length(ett));
440
15
}
441
442
/* Sub-dissector registration */
443
void
444
proto_reg_handoff_realtek(void)
445
15
{
446
15
  dissector_add_uint("ethertype", ETHERTYPE_REALTEK, realtek_handle);
447
448
15
  heur_dissector_add("realtek", dissect_rrcp, "Realtek Remote Control Protocol",
449
15
                     "rrcp", proto_rrcp, HEURISTIC_ENABLE);
450
451
15
  heur_dissector_add("realtek", dissect_rep, "Realtek Echo Protocol",
452
15
                     "rep", proto_rep, HEURISTIC_ENABLE);
453
454
15
  heur_dissector_add("realtek", dissect_rldp, "Realtek Loop Detection Protocol",
455
15
                     "rldp", proto_rldp, HEURISTIC_ENABLE);
456
15
}