Coverage Report

Created: 2026-09-28 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-dtp.c
Line
Count
Source
1
/* packet-dtp.c
2
 * Routines for the disassembly for Cisco Dynamic Trunk Protocol
3
 *
4
 * Wireshark - Network traffic analyzer
5
 * By Gerald Combs <gerald@wireshark.org>
6
 * Copyright 1998 Gerald Combs
7
 *
8
 * DTP support added by Charlie Lenahan <clenahan@fortresstech.com>
9
 *
10
 * Additional information comes from Yersinia (http://www.yersinia.net/)
11
 * by Alfredo Andres and David Barroso
12
 *
13
 * Improved dissection for Trunk Status and Trunk Type TLVs
14
 * based on DTP description in U.S. Patent #6,445,715 and
15
 * consultations with Aninda Chatterjee @ Cisco
16
 * by Peter Paluch <Peter.Paluch@fri.uniza.sk>
17
 *
18
 * SPDX-License-Identifier: GPL-2.0-or-later
19
 *
20
 */
21
22
#include "config.h"
23
24
#include <epan/packet.h>
25
#include <epan/to_str.h>
26
#include <epan/expert.h>
27
#include "packet-cisco-pid.h"
28
29
/*
30
 * It's incomplete, and it appears to be inaccurate in a number of places,
31
 * but it's all I could find....
32
 */
33
void proto_register_dtp(void);
34
void proto_reg_handoff_dtp(void);
35
36
static dissector_handle_t dtp_handle;
37
38
static int proto_dtp;
39
static int hf_dtp_version;
40
static int hf_dtp_domain;
41
static int hf_dtp_tlvtype;
42
static int hf_dtp_tlvlength;
43
static int hf_dtp_senderid;
44
static int hf_dtp_tot;
45
static int hf_dtp_tat;
46
static int hf_dtp_tos;
47
static int hf_dtp_tas;
48
static int hf_dtp_data;
49
50
static int ett_dtp;
51
static int ett_dtp_tlv;
52
static int ett_dtp_status;
53
static int ett_dtp_type;
54
55
static expert_field ei_dtp_tlv_length_too_short;
56
static expert_field ei_dtp_tlv_length_invalid;
57
static expert_field ei_dtp_truncated;
58
59
static void
60
dissect_dtp_tlv(packet_info *pinfo, tvbuff_t *tvb, int offset, int length,
61
    proto_tree *tree, proto_item *ti, proto_item *tlv_length_item, uint8_t type);
62
63
64
0
#define DTP_TLV_DOMAIN    0x01 /* VTP Domain Name */
65
0
#define DTP_TLV_TRSTATUS  0x02 /* Trunk Status */
66
0
#define DTP_TLV_TRTYPE    0x03 /* Trunk Type */
67
0
#define DTP_TLV_SENDERID  0x04 /* Sender ID (MAC) */
68
69
0
#define DTP_TOS_SHIFT   7
70
0
#define DTP_TOT_SHIFT   5
71
72
16
#define DTP_TOS_MASK    0x80
73
16
#define DTP_TAS_MASK    0x07
74
75
16
#define DTP_TOT_MASK    0xE0
76
16
#define DTP_TAT_MASK    0x07
77
78
0
#define DTP_TOSVALUE(status)  (((status) & DTP_TOS_MASK) >> DTP_TOS_SHIFT)
79
0
#define DTP_TASVALUE(status)  ((status) & DTP_TAS_MASK)
80
81
0
#define DTP_TOTVALUE(type)  (((type) & DTP_TOT_MASK) >> DTP_TOT_SHIFT)
82
0
#define DTP_TATVALUE(type)  ((type) & DTP_TAT_MASK)
83
84
/* Trunk Operating Status */
85
#define DTP_TOS_ACCESS    0x0
86
#define DTP_TOS_TRUNK   0x1
87
88
/* Trunk Administrative Status */
89
#define DTP_TAS_ON    0x1
90
#define DTP_TAS_OFF   0x2
91
#define DTP_TAS_DESIRABLE 0x3
92
#define DTP_TAS_AUTO    0x4
93
94
/* Trunk Operating Type */
95
#define DTP_TOT_NATIVE    0x1
96
#define DTP_TOT_ISL   0x2
97
#define DTP_TOT_DOT1Q   0x5
98
99
/* Trunk Administrative Type */
100
#define DTP_TAT_NEGOTIATED  0x0
101
#define DTP_TAT_NATIVE    0x1
102
#define DTP_TAT_ISL   0x2
103
#define DTP_TAT_DOT1Q   0x5
104
105
106
static const value_string dtp_tlv_type_vals[] = {
107
  { DTP_TLV_DOMAIN, "Domain" },
108
  { DTP_TLV_TRSTATUS, "Trunk Status" },
109
  { DTP_TLV_TRTYPE, "Trunk Type" },
110
  { DTP_TLV_SENDERID,   "Sender ID" },
111
112
  { 0,      NULL }
113
};
114
115
static const value_string dtp_tos_vals[] = {
116
  { DTP_TOS_ACCESS, "Access" },
117
  { DTP_TOS_TRUNK,  "Trunk"},
118
119
  { 0,      NULL }
120
};
121
122
static const value_string dtp_tas_vals[] = {
123
  { DTP_TAS_ON,   "On" },
124
  { DTP_TAS_OFF,    "Off" },
125
  { DTP_TAS_DESIRABLE,  "Desirable" },
126
  { DTP_TAS_AUTO,   "Auto" },
127
128
  { 0,      NULL }
129
};
130
131
static const value_string dtp_tot_vals[] = {
132
  { DTP_TOT_NATIVE, "Native" },
133
  { DTP_TOT_ISL,    "ISL" },
134
  { DTP_TOT_DOT1Q,  "802.1Q" },
135
136
  { 0,      NULL }
137
};
138
139
static const value_string dtp_tat_vals[] = {
140
  { DTP_TAT_NEGOTIATED, "Negotiated" },
141
  { DTP_TAT_NATIVE, "Native" },
142
  { DTP_TAT_ISL,    "ISL" },
143
  { DTP_TAT_DOT1Q,  "802.1Q" },
144
145
  { 0,      NULL }
146
};
147
148
static int
149
dissect_dtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
150
0
{
151
0
  proto_item *ti;
152
0
  proto_tree *dtp_tree;
153
0
  proto_tree *tlv_tree;
154
0
  unsigned offset = 0;
155
156
0
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "DTP");
157
0
  col_set_str(pinfo->cinfo, COL_INFO, "Dynamic Trunk Protocol");
158
159
0
  ti = proto_tree_add_item(tree, proto_dtp, tvb, offset, -1, ENC_NA);
160
0
  dtp_tree = proto_item_add_subtree(ti, ett_dtp);
161
162
  /* We assume version */
163
0
  proto_tree_add_item(dtp_tree, hf_dtp_version, tvb, offset, 1, ENC_BIG_ENDIAN);
164
0
  offset += 1;
165
166
0
  while (tvb_reported_length_remaining(tvb, offset) > 0) {
167
0
    int type, length, valuelength;
168
0
    proto_item * tlv_length_item;
169
170
    /* XXX - why not just let tvbuff exceptions handle this? */
171
0
    if (tvb_reported_length_remaining(tvb, offset) < 4) {
172
0
      expert_add_info(pinfo, dtp_tree, &ei_dtp_truncated);
173
0
      break;
174
0
    }
175
176
0
    type = tvb_get_ntohs(tvb, offset);
177
0
    length = tvb_get_ntohs(tvb, offset + 2);
178
179
0
    tlv_tree = proto_tree_add_subtree(dtp_tree, tvb, offset, length, ett_dtp_tlv, NULL,
180
0
           val_to_str(pinfo->pool, type, dtp_tlv_type_vals, "Unknown TLV type: 0x%02x"));
181
182
0
    proto_tree_add_uint(tlv_tree, hf_dtp_tlvtype, tvb, offset, 2, type);
183
0
    offset+=2;
184
185
0
    tlv_length_item = proto_tree_add_uint(tlv_tree, hf_dtp_tlvlength, tvb, offset, 2, length);
186
0
    offset+=2;
187
188
0
    if (length <= 4) {
189
      /* Length includes type and length fields, so it
190
         must be >= 4, and no known TLVs have a value
191
         length of 0, so it must be > 4. */
192
0
      expert_add_info(pinfo, tlv_length_item, &ei_dtp_tlv_length_too_short);
193
0
      break;
194
0
    }
195
0
    valuelength = (length-4);
196
0
    dissect_dtp_tlv(pinfo, tvb, offset, valuelength, tlv_tree, ti, tlv_length_item, (uint8_t) type);
197
0
    offset += valuelength;
198
0
  }
199
0
  return tvb_captured_length(tvb);
200
0
}
201
202
static void
203
dissect_dtp_tlv(packet_info *pinfo, tvbuff_t *tvb, int offset, int length,
204
    proto_tree *tree, proto_item *ti, proto_item *tlv_length_item, uint8_t type)
205
0
{
206
0
  switch (type) {
207
208
0
  case DTP_TLV_DOMAIN:
209
0
    if (length <= 33) { /* VTP domain name is at most 32 bytes long and is null-terminated */
210
0
      proto_item_append_text(ti, ": %s", tvb_format_text(pinfo->pool, tvb, offset, length - 1));
211
0
      proto_tree_add_item(tree, hf_dtp_domain, tvb, offset, length, ENC_ASCII);
212
0
    }
213
0
    else
214
0
      expert_add_info(pinfo, tlv_length_item, &ei_dtp_tlv_length_invalid);
215
216
0
    break;
217
218
0
  case DTP_TLV_TRSTATUS:
219
0
    if (length == 1) { /* Value field length must be 1 byte */
220
0
      proto_tree * field_tree = NULL;
221
0
      uint8_t trunk_status = tvb_get_uint8(tvb, offset);
222
223
0
      proto_item_append_text(ti,
224
0
        " (Operating/Administrative): %s/%s (0x%02x)",
225
0
        val_to_str_const(DTP_TOSVALUE(trunk_status), dtp_tos_vals, "Unknown operating status"),
226
0
        val_to_str_const(DTP_TASVALUE(trunk_status), dtp_tas_vals, "Unknown administrative status"),
227
0
        trunk_status);
228
0
      field_tree = proto_tree_add_subtree_format(tree, tvb, offset, length, ett_dtp_status, NULL, "Value: %s/%s (0x%02x)",
229
0
        val_to_str_const(DTP_TOSVALUE(trunk_status), dtp_tos_vals, "Unknown operating status"),
230
0
        val_to_str_const(DTP_TASVALUE(trunk_status), dtp_tas_vals, "Unknown administrative status"),
231
0
        trunk_status);
232
0
      proto_tree_add_item(field_tree, hf_dtp_tos, tvb, offset, length, ENC_BIG_ENDIAN);
233
0
      proto_tree_add_item(field_tree, hf_dtp_tas, tvb, offset, length, ENC_BIG_ENDIAN);
234
0
      }
235
0
      else
236
0
        expert_add_info(pinfo, tlv_length_item, &ei_dtp_tlv_length_invalid);
237
238
0
    break;
239
240
0
  case DTP_TLV_TRTYPE:
241
0
    if (length == 1) { /* Value field length must be 1 byte */
242
0
      proto_tree * field_tree;
243
0
      uint8_t trunk_type = tvb_get_uint8(tvb, offset);
244
0
      proto_item_append_text(ti,
245
0
        " (Operating/Administrative): %s/%s (0x%02x)",
246
0
        val_to_str_const(DTP_TOTVALUE(trunk_type), dtp_tot_vals, "Unknown operating type"),
247
0
        val_to_str_const(DTP_TATVALUE(trunk_type), dtp_tat_vals, "Unknown administrative type"),
248
0
        trunk_type);
249
0
      field_tree = proto_tree_add_subtree_format(tree, tvb, offset, length, ett_dtp_type, NULL, "Value: %s/%s (0x%02x)",
250
0
        val_to_str_const(DTP_TOTVALUE(trunk_type), dtp_tot_vals, "Unknown operating type"),
251
0
        val_to_str_const(DTP_TATVALUE(trunk_type), dtp_tat_vals, "Unknown administrative type"),
252
0
        trunk_type);
253
0
      proto_tree_add_item(field_tree, hf_dtp_tot, tvb, offset, length, ENC_BIG_ENDIAN);
254
0
      proto_tree_add_item(field_tree, hf_dtp_tat, tvb, offset, length, ENC_BIG_ENDIAN);
255
0
      }
256
0
      else
257
0
        expert_add_info(pinfo, tlv_length_item, &ei_dtp_tlv_length_invalid);
258
259
0
    break;
260
261
0
  case DTP_TLV_SENDERID:
262
0
    if (length == 6) { /* Value length must be 6 bytes for a MAC address */
263
0
      proto_item_append_text(ti, ": %s",
264
0
        tvb_ether_to_str(pinfo->pool, tvb, offset));  /* XXX - resolve? */
265
0
      proto_tree_add_item(tree, hf_dtp_senderid, tvb, offset, length, ENC_NA);
266
0
    }
267
0
    else
268
0
      expert_add_info(pinfo, tlv_length_item, &ei_dtp_tlv_length_invalid);
269
270
0
    break;
271
272
0
  default:
273
0
    proto_tree_add_item(tree, hf_dtp_data, tvb, offset, length, ENC_NA);
274
0
    break;
275
0
  }
276
0
}
277
278
void
279
proto_register_dtp(void)
280
16
{
281
16
  static hf_register_info hf[] = {
282
16
  { &hf_dtp_version,
283
16
    { "Version",  "dtp.version", FT_UINT8, BASE_DEC,
284
16
    NULL, 0x0, NULL, HFILL }},
285
286
16
  { &hf_dtp_domain,
287
16
    { "Domain", "dtp.domain", FT_STRING, BASE_NONE,
288
16
    NULL, 0x0, NULL, HFILL }},
289
290
16
  { &hf_dtp_tlvtype,
291
16
    { "Type", "dtp.tlv_type", FT_UINT16, BASE_HEX,
292
16
    VALS(dtp_tlv_type_vals), 0x0, NULL, HFILL }},
293
294
16
  { &hf_dtp_tlvlength,
295
16
    { "Length", "dtp.tlv_len", FT_UINT16, BASE_DEC,
296
16
    NULL, 0x0, NULL, HFILL }},
297
298
16
  { &hf_dtp_tos,
299
16
    { "Trunk Operating Status", "dtp.tos", FT_UINT8, BASE_HEX,
300
16
    VALS(dtp_tos_vals), DTP_TOS_MASK, NULL, HFILL }},
301
302
16
  { &hf_dtp_tas,
303
16
    { "Trunk Administrative Status", "dtp.tas", FT_UINT8, BASE_HEX,
304
16
    VALS(dtp_tas_vals), DTP_TAS_MASK, NULL, HFILL }},
305
306
16
  { &hf_dtp_tot,
307
16
    { "Trunk Operating Type", "dtp.tot", FT_UINT8, BASE_HEX,
308
16
    VALS(dtp_tot_vals), DTP_TOT_MASK, NULL, HFILL }},
309
310
16
  { &hf_dtp_tat,
311
16
    { "Trunk Administrative Type", "dtp.tat", FT_UINT8, BASE_HEX,
312
16
    VALS(dtp_tat_vals), DTP_TAT_MASK, NULL, HFILL }},
313
314
16
  { &hf_dtp_senderid,
315
16
    { "Sender ID", "dtp.senderid", FT_ETHER, BASE_NONE,
316
16
    NULL, 0x0, "MAC Address of neighbor", HFILL }},
317
318
16
  { &hf_dtp_data,
319
16
    { "Data", "dtp.data", FT_ETHER, BASE_NONE,
320
16
    NULL, 0x0, NULL, HFILL }},
321
16
  };
322
323
16
  static int *ett[] = {
324
16
    &ett_dtp,
325
16
    &ett_dtp_tlv,
326
16
    &ett_dtp_status,
327
16
    &ett_dtp_type,
328
16
  };
329
330
16
  static ei_register_info ei[] = {
331
16
  { &ei_dtp_tlv_length_too_short,
332
16
    { "dtp.tlv_len.too_short", PI_MALFORMED, PI_ERROR,
333
16
      "Indicated length is less than the minimum length", EXPFILL }},
334
335
16
  { &ei_dtp_tlv_length_invalid,
336
16
    { "dtp.tlv_len.invalid", PI_MALFORMED, PI_ERROR,
337
16
      "Indicated length does not correspond to this record type", EXPFILL }},
338
339
16
  { &ei_dtp_truncated,
340
16
    { "dtp.truncated", PI_MALFORMED, PI_ERROR,
341
16
      "DTP message is truncated prematurely", EXPFILL }}
342
343
16
  };
344
345
16
  expert_module_t *expert_dtp;
346
347
16
  proto_dtp = proto_register_protocol("Dynamic Trunk Protocol", "DTP", "dtp");
348
16
  proto_register_field_array(proto_dtp, hf, array_length(hf));
349
16
  proto_register_subtree_array(ett, array_length(ett));
350
351
16
  expert_dtp = expert_register_protocol(proto_dtp);
352
16
  expert_register_field_array(expert_dtp, ei, array_length(ei));
353
354
16
  dtp_handle = register_dissector("dtp", dissect_dtp, proto_dtp);
355
16
}
356
357
void
358
proto_reg_handoff_dtp(void)
359
16
{
360
16
  dissector_add_uint("llc.cisco_pid", CISCO_PID_DTP, dtp_handle);
361
16
}
362
363
/*
364
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
365
 *
366
 * Local variables:
367
 * c-basic-offset: 8
368
 * tab-width: 8
369
 * indent-tabs-mode: t
370
 * End:
371
 *
372
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
373
 * :indentSize=8:tabSize=8:noTabs=false:
374
 */