2 * Routines for the disassembly of the "UniDirectional Link Detection"
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
13 #include <epan/packet.h>
14 #include <epan/expert.h>
15 #include <epan/cisco_pid.h>
20 * https://tools.ietf.org/rfc/rfc5171
22 * for some information on UDLD.
25 /* Offsets in TLV structure. */
29 void proto_register_udld(void);
30 void proto_reg_handoff_udld(void);
32 static dissector_handle_t udld_handle
;
34 static int proto_udld
;
35 static int hf_udld_version
;
36 static int hf_udld_opcode
;
37 static int hf_udld_flags
;
38 static int hf_udld_flags_rt
;
39 static int hf_udld_flags_rsy
;
40 static int hf_udld_checksum
;
41 static int hf_udld_tlvtype
;
42 static int hf_udld_tlvlength
;
43 static int hf_udld_device_id
;
44 static int hf_udld_sent_through_interface
;
45 static int hf_udld_data
;
48 static expert_field ei_udld_tlvlength
;
51 static int ett_udld_flags
;
52 static int ett_udld_tlv
;
54 #define TYPE_DEVICE_ID 0x0001
55 #define TYPE_PORT_ID 0x0002
56 #define TYPE_ECHO 0x0003
57 #define TYPE_MESSAGE_INTERVAL 0x0004
58 #define TYPE_TIMEOUT_INTERVAL 0x0005
59 #define TYPE_DEVICE_NAME 0x0006
60 #define TYPE_SEQUENCE_NUMBER 0x0007
63 static const value_string type_vals
[] = {
64 { TYPE_DEVICE_ID
, "Device ID" },
65 { TYPE_PORT_ID
, "Port ID" },
66 { TYPE_ECHO
, "Echo" },
67 { TYPE_MESSAGE_INTERVAL
, "Message interval" },
68 { TYPE_TIMEOUT_INTERVAL
, "Timeout interval" },
69 { TYPE_DEVICE_NAME
, "Device name" },
70 { TYPE_SEQUENCE_NUMBER
, "Sequence number" },
74 #define OPCODE_RESERVED 0x00
75 #define OPCODE_PROBE 0x01
76 #define OPCODE_ECHO 0x02
77 #define OPCODE_FLUSH 0x03
79 static const value_string opcode_vals
[] = {
80 { OPCODE_RESERVED
, "Reserved" },
81 { OPCODE_PROBE
, "Probe" },
82 { OPCODE_ECHO
, "Echo" },
83 { OPCODE_FLUSH
, "Flush" },
88 dissect_udld(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
91 proto_tree
*udld_tree
= NULL
;
98 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "UDLD");
99 col_clear(pinfo
->cinfo
, COL_INFO
);
102 proto_item
*flags_ti
;
103 proto_tree
*flags_tree
;
105 ti
= proto_tree_add_item(tree
, proto_udld
, tvb
, offset
, -1, ENC_NA
);
106 udld_tree
= proto_item_add_subtree(ti
, ett_udld
);
109 proto_tree_add_item(udld_tree
, hf_udld_version
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
110 proto_tree_add_item(udld_tree
, hf_udld_opcode
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
112 flags_ti
= proto_tree_add_item(udld_tree
, hf_udld_flags
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
113 flags_tree
= proto_item_add_subtree(flags_ti
, ett_udld_flags
);
114 proto_tree_add_item(flags_tree
, hf_udld_flags_rt
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
115 proto_tree_add_item(flags_tree
, hf_udld_flags_rsy
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
117 proto_tree_add_checksum(udld_tree
, tvb
, offset
, hf_udld_checksum
, -1, NULL
, pinfo
, 0, ENC_BIG_ENDIAN
, PROTO_CHECKSUM_NO_FLAGS
);
120 offset
+= 4; /* The version/opcode/flags/checksum fields from above */
123 while (tvb_reported_length_remaining(tvb
, offset
) != 0) {
124 type
= tvb_get_ntohs(tvb
, offset
+ TLV_TYPE
);
125 length
= tvb_get_ntohs(tvb
, offset
+ TLV_LENGTH
);
127 tlv_tree
= proto_tree_add_subtree_format(udld_tree
, tvb
, offset
, 4,
128 ett_udld_tlv
, NULL
, "TLV with invalid length %u (< 4)", length
);
129 proto_tree_add_uint(tlv_tree
, hf_udld_tlvtype
, tvb
,
130 offset
+ TLV_TYPE
, 2, type
);
131 ti
= proto_tree_add_uint(tlv_tree
, hf_udld_tlvlength
, tvb
,
132 offset
+ TLV_LENGTH
, 2, length
);
133 expert_add_info(pinfo
, ti
, &ei_udld_tlvlength
);
143 col_append_fstr(pinfo
->cinfo
, COL_INFO
,
145 tvb_format_stringzpad(pinfo
->pool
, tvb
, offset
+ 4,
149 tlv_tree
= proto_tree_add_subtree_format(udld_tree
, tvb
, offset
,
150 length
, ett_udld_tlv
, NULL
, "Device ID: %s",
151 tvb_format_stringzpad(pinfo
->pool
, tvb
, offset
+ 4, length
- 4));
152 proto_tree_add_uint(tlv_tree
, hf_udld_tlvtype
, tvb
,
153 offset
+ TLV_TYPE
, 2, type
);
154 proto_tree_add_uint(tlv_tree
, hf_udld_tlvlength
, tvb
,
155 offset
+ TLV_LENGTH
, 2, length
);
156 proto_tree_add_item(tlv_tree
, hf_udld_device_id
, tvb
, offset
+ 4,
157 length
- 4, ENC_ASCII
);
163 real_length
= length
;
164 if (tvb_get_uint8(tvb
, offset
+ real_length
) != 0x00) {
165 /* The length in the TLV doesn't appear to be the
166 length of the TLV, as the byte just past it
167 isn't the first byte of a 2-byte big-endian
168 small integer; make the length of the TLV the length
169 in the TLV, plus 4 bytes for the TLV type and length,
170 minus 1 because that's what makes one capture work. */
171 real_length
= length
+ 3;
174 col_append_fstr(pinfo
->cinfo
, COL_INFO
,
176 tvb_format_stringzpad(pinfo
->pool
, tvb
, offset
+ 4, length
- 4));
179 tlv_tree
= proto_tree_add_subtree_format(udld_tree
, tvb
, offset
,
180 real_length
, ett_udld_tlv
, NULL
, "Port ID: %s",
181 tvb_format_text(pinfo
->pool
, tvb
, offset
+ 4, real_length
- 4));
182 proto_tree_add_uint(tlv_tree
, hf_udld_tlvtype
, tvb
,
183 offset
+ TLV_TYPE
, 2, type
);
184 proto_tree_add_uint(tlv_tree
, hf_udld_tlvlength
, tvb
,
185 offset
+ TLV_LENGTH
, 2, length
);
186 proto_tree_add_item(tlv_tree
, hf_udld_sent_through_interface
, tvb
, offset
+ 4,
187 real_length
- 4, ENC_ASCII
);
189 offset
+= real_length
;
193 case TYPE_MESSAGE_INTERVAL
:
194 case TYPE_TIMEOUT_INTERVAL
:
195 case TYPE_DEVICE_NAME
:
196 case TYPE_SEQUENCE_NUMBER
:
198 tlv_tree
= proto_tree_add_subtree_format(udld_tree
, tvb
, offset
,
199 length
, ett_udld_tlv
, NULL
, "Type: %s, length: %u",
200 val_to_str(type
, type_vals
, "Unknown (0x%04x)"),
202 proto_tree_add_uint(tlv_tree
, hf_udld_tlvtype
, tvb
,
203 offset
+ TLV_TYPE
, 2, type
);
204 proto_tree_add_uint(tlv_tree
, hf_udld_tlvlength
, tvb
,
205 offset
+ TLV_LENGTH
, 2, length
);
207 proto_tree_add_item(tlv_tree
, hf_udld_data
, tvb
, offset
+ 4,
216 call_data_dissector(tvb_new_subset_remaining(tvb
, offset
), pinfo
, udld_tree
);
217 return tvb_captured_length(tvb
);
221 proto_register_udld(void)
223 static hf_register_info hf
[] = {
225 { "Version", "udld.version", FT_UINT8
, BASE_DEC
, NULL
, 0xE0,
229 { "Opcode", "udld.opcode", FT_UINT8
, BASE_DEC
, VALS(opcode_vals
), 0x1F,
233 { "Flags", "udld.flags", FT_UINT8
, BASE_DEC
, NULL
, 0x0,
237 { "Recommended timeout", "udld.flags.rt", FT_UINT8
, BASE_HEX
, NULL
, 0x01,
240 { &hf_udld_flags_rsy
,
241 { "ReSynch", "udld.flags.rsy", FT_UINT8
, BASE_HEX
, NULL
, 0x02,
245 { "Checksum", "udld.checksum", FT_UINT16
, BASE_HEX
, NULL
, 0x0,
249 { "Type", "udld.tlv.type", FT_UINT16
, BASE_HEX
, VALS(type_vals
), 0x0,
252 { &hf_udld_tlvlength
,
253 { "Length", "udld.tlv.len", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
256 { &hf_udld_device_id
,
257 { "Device ID", "udld.device_id", FT_STRINGZ
, BASE_NONE
, NULL
, 0x0,
260 { &hf_udld_sent_through_interface
,
261 { "Sent through Interface", "udld.sent_through_interface", FT_STRING
, BASE_NONE
, NULL
, 0x0,
265 { "Data", "udld.data", FT_BYTES
, BASE_NONE
, NULL
, 0x0,
269 static int *ett
[] = {
275 static ei_register_info ei
[] = {
276 { &ei_udld_tlvlength
, { "udld.tlv.len.invalid", PI_PROTOCOL
, PI_WARN
, "TLV with invalid length (< 4)", EXPFILL
}},
279 expert_module_t
* expert_udld
;
281 proto_udld
= proto_register_protocol("Unidirectional Link Detection",
283 proto_register_field_array(proto_udld
, hf
, array_length(hf
));
284 proto_register_subtree_array(ett
, array_length(ett
));
285 expert_udld
= expert_register_protocol(proto_udld
);
286 expert_register_field_array(expert_udld
, ei
, array_length(ei
));
287 udld_handle
= register_dissector("udld", dissect_udld
, proto_udld
);
291 proto_reg_handoff_udld(void)
293 dissector_add_uint("llc.cisco_pid", CISCO_PID_UDLD
, udld_handle
);
294 dissector_add_uint("chdlc.protocol", 0x0111, udld_handle
);
298 * Editor modelines - https://www.wireshark.org/tools/modelines.html
303 * indent-tabs-mode: nil
306 * vi: set shiftwidth=4 tabstop=8 expandtab:
307 * :indentSize=4:tabSize=8:noTabs=true: