1 /*
2 * Copyright (c) 1998-2007 The TCPDUMP project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that: (1) source code
6 * distributions retain the above copyright notice and this paragraph
7 * in its entirety, and (2) distributions including binary code include
8 * the above copyright notice and this paragraph in its entirety in
9 * the documentation or other materials provided with the distribution.
10 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
11 * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
12 * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
13 * FOR A PARTICULAR PURPOSE.
14 *
15 * Reference documentation:
16 * https://www.cisco.com/c/en/us/support/docs/lan-switching/vtp/10558-21.html
17 * https://docstore.mik.ua/univercd/cc/td/doc/product/lan/trsrb/frames.htm
18 *
19 * Original code ode by Carles Kishimoto <carles.kishimoto@gmail.com>
20 */
21
22 /* \summary: Cisco VLAN Trunking Protocol (VTP) printer */
23
24 #include <config.h>
25
26 #include "netdissect-stdinc.h"
27
28 #define ND_LONGJMP_FROM_TCHECK
29 #include "netdissect.h"
30 #include "addrtoname.h"
31 #include "extract.h"
32
33 #define VTP_HEADER_LEN 36
34 #define VTP_DOMAIN_NAME_LEN 32
35 #define VTP_MD5_DIGEST_LEN 16
36 #define VTP_UPDATE_TIMESTAMP_LEN 12
37 #define VTP_VLAN_INFO_FIXED_PART_LEN 12 /* length of VLAN info before VLAN name */
38
39 #define VTP_SUMMARY_ADV 0x01
40 #define VTP_SUBSET_ADV 0x02
41 #define VTP_ADV_REQUEST 0x03
42 #define VTP_JOIN_MESSAGE 0x04
43
44 struct vtp_vlan_ {
45 nd_uint8_t len;
46 nd_uint8_t status;
47 nd_uint8_t type;
48 nd_uint8_t name_len;
49 nd_uint16_t vlanid;
50 nd_uint16_t mtu;
51 nd_uint32_t index;
52 };
53
54 static const struct tok vtp_message_type_values[] = {
55 { VTP_SUMMARY_ADV, "Summary advertisement"},
56 { VTP_SUBSET_ADV, "Subset advertisement"},
57 { VTP_ADV_REQUEST, "Advertisement request"},
58 { VTP_JOIN_MESSAGE, "Join message"},
59 { 0, NULL }
60 };
61
62 static const struct tok vtp_header_values[] = {
63 { 0x01, "Followers"}, /* On Summary advertisement, 3rd byte is Followers */
64 { 0x02, "Seq number"}, /* On Subset advertisement, 3rd byte is Sequence number */
65 { 0x03, "Rsvd"}, /* On Adver. requests 3rd byte is Rsvd */
66 { 0x04, "Rsvd"}, /* On Adver. requests 3rd byte is Rsvd */
67 { 0, NULL }
68 };
69
70 static const struct tok vtp_vlan_type_values[] = {
71 { 0x01, "Ethernet"},
72 { 0x02, "FDDI"},
73 { 0x03, "TrCRF"},
74 { 0x04, "FDDI-net"},
75 { 0x05, "TrBRF"},
76 { 0, NULL }
77 };
78
79 static const struct tok vtp_vlan_status[] = {
80 { 0x00, "Operational"},
81 { 0x01, "Suspended"},
82 { 0, NULL }
83 };
84
85 #define VTP_VLAN_SOURCE_ROUTING_RING_NUMBER 0x01
86 #define VTP_VLAN_SOURCE_ROUTING_BRIDGE_NUMBER 0x02
87 #define VTP_VLAN_STP_TYPE 0x03
88 #define VTP_VLAN_PARENT_VLAN 0x04
89 #define VTP_VLAN_TRANS_BRIDGED_VLAN 0x05
90 #define VTP_VLAN_PRUNING 0x06
91 #define VTP_VLAN_BRIDGE_TYPE 0x07
92 #define VTP_VLAN_ARP_HOP_COUNT 0x08
93 #define VTP_VLAN_STE_HOP_COUNT 0x09
94 #define VTP_VLAN_BACKUP_CRF_MODE 0x0A
95
96 static const struct tok vtp_vlan_tlv_values[] = {
97 { VTP_VLAN_SOURCE_ROUTING_RING_NUMBER, "Source-Routing Ring Number TLV"},
98 { VTP_VLAN_SOURCE_ROUTING_BRIDGE_NUMBER, "Source-Routing Bridge Number TLV"},
99 { VTP_VLAN_STP_TYPE, "STP type TLV"},
100 { VTP_VLAN_PARENT_VLAN, "Parent VLAN TLV"},
101 { VTP_VLAN_TRANS_BRIDGED_VLAN, "Translationally bridged VLANs TLV"},
102 { VTP_VLAN_PRUNING, "Pruning TLV"},
103 { VTP_VLAN_BRIDGE_TYPE, "Bridge Type TLV"},
104 { VTP_VLAN_ARP_HOP_COUNT, "Max ARP Hop Count TLV"},
105 { VTP_VLAN_STE_HOP_COUNT, "Max STE Hop Count TLV"},
106 { VTP_VLAN_BACKUP_CRF_MODE, "Backup CRF Mode TLV"},
107 { 0, NULL }
108 };
109
110 static const struct tok vtp_stp_type_values[] = {
111 { 1, "SRT"},
112 { 2, "SRB"},
113 { 3, "Auto"},
114 { 0, NULL }
115 };
116
117 void
vtp_print(netdissect_options * ndo,const u_char * pptr,const u_int length)118 vtp_print(netdissect_options *ndo,
119 const u_char *pptr, const u_int length)
120 {
121 u_int type, len, name_len, tlv_len, tlv_value, mgmtd_len;
122 const u_char *tptr;
123 const struct vtp_vlan_ *vtp_vlan;
124
125 ndo->ndo_protocol = "vtp";
126 if (length < VTP_HEADER_LEN)
127 goto invalid;
128
129 tptr = pptr;
130
131 ND_TCHECK_LEN(tptr, VTP_HEADER_LEN);
132
133 type = GET_U_1(tptr + 1);
134 ND_PRINT("VTPv%u, Message %s (0x%02x), length %u",
135 GET_U_1(tptr),
136 tok2str(vtp_message_type_values,"Unknown message type", type),
137 type,
138 length);
139
140 /* In non-verbose mode, just print version and message type */
141 if (ndo->ndo_vflag < 1) {
142 goto tcheck_full_packet;
143 }
144
145 /* verbose mode print all fields */
146 ND_PRINT("\n\tDomain name: ");
147 mgmtd_len = GET_U_1(tptr + 3);
148 if (mgmtd_len < 1 || mgmtd_len > VTP_DOMAIN_NAME_LEN) {
149 ND_PRINT(" [MgmtD Len %u]", mgmtd_len);
150 goto invalid;
151 }
152 nd_printjnp(ndo, tptr + 4, mgmtd_len);
153 ND_PRINT(", %s: %u",
154 tok2str(vtp_header_values, "Unknown", type),
155 GET_U_1(tptr + 2));
156
157 tptr += VTP_HEADER_LEN;
158
159 switch (type) {
160
161 case VTP_SUMMARY_ADV:
162
163 /*
164 * SUMMARY ADVERTISEMENT
165 *
166 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
167 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
168 * | Version | Code | Followers | MgmtD Len |
169 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
170 * | Management Domain Name (zero-padded to 32 bytes) |
171 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
172 * | Configuration revision number |
173 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
174 * | Updater Identity IP address |
175 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
176 * | Update Timestamp (12 bytes) |
177 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
178 * | MD5 digest (16 bytes) |
179 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
180 *
181 */
182
183 ND_PRINT("\n\t Config Rev %x, Updater %s",
184 GET_BE_U_4(tptr),
185 GET_IPADDR_STRING(tptr+4));
186 tptr += 8;
187 ND_PRINT(", Timestamp 0x%08x 0x%08x 0x%08x",
188 GET_BE_U_4(tptr),
189 GET_BE_U_4(tptr + 4),
190 GET_BE_U_4(tptr + 8));
191 tptr += VTP_UPDATE_TIMESTAMP_LEN;
192 ND_PRINT(", MD5 digest: %08x%08x%08x%08x",
193 GET_BE_U_4(tptr),
194 GET_BE_U_4(tptr + 4),
195 GET_BE_U_4(tptr + 8),
196 GET_BE_U_4(tptr + 12));
197 tptr += VTP_MD5_DIGEST_LEN;
198 break;
199
200 case VTP_SUBSET_ADV:
201
202 /*
203 * SUBSET ADVERTISEMENT
204 *
205 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
206 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
207 * | Version | Code | Seq number | MgmtD Len |
208 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
209 * | Management Domain Name (zero-padded to 32 bytes) |
210 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
211 * | Configuration revision number |
212 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
213 * | VLAN info field 1 |
214 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
215 * | ................ |
216 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
217 * | VLAN info field N |
218 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
219 *
220 */
221
222 ND_PRINT(", Config Rev %x", GET_BE_U_4(tptr));
223
224 /*
225 * VLAN INFORMATION
226 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
227 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
228 * | V info len | Status | VLAN type | VLAN name len |
229 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
230 * | ISL vlan id | MTU size |
231 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
232 * | 802.10 index (SAID) |
233 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
234 * | VLAN name |
235 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
236 *
237 */
238
239 tptr += 4;
240 while ((unsigned)(tptr - pptr) < length) {
241
242 len = GET_U_1(tptr);
243 if (len == 0)
244 break;
245
246 ND_TCHECK_LEN(tptr, len);
247
248 vtp_vlan = (const struct vtp_vlan_*)tptr;
249 if (len < VTP_VLAN_INFO_FIXED_PART_LEN)
250 goto invalid;
251 ND_PRINT("\n\tVLAN info status %s, type %s, VLAN-id %u, MTU %u, SAID 0x%08x, Name ",
252 tok2str(vtp_vlan_status,"Unknown",GET_U_1(vtp_vlan->status)),
253 tok2str(vtp_vlan_type_values,"Unknown",GET_U_1(vtp_vlan->type)),
254 GET_BE_U_2(vtp_vlan->vlanid),
255 GET_BE_U_2(vtp_vlan->mtu),
256 GET_BE_U_4(vtp_vlan->index));
257 len -= VTP_VLAN_INFO_FIXED_PART_LEN;
258 tptr += VTP_VLAN_INFO_FIXED_PART_LEN;
259 name_len = GET_U_1(vtp_vlan->name_len);
260 if (len < 4*((name_len + 3)/4))
261 goto invalid;
262 nd_printjnp(ndo, tptr, name_len);
263
264 /*
265 * Vlan names are aligned to 32-bit boundaries.
266 */
267 len -= 4*((name_len + 3)/4);
268 tptr += 4*((name_len + 3)/4);
269
270 /* TLV information follows */
271
272 while (len > 0) {
273
274 /*
275 * Cisco specs say 2 bytes for type + 2 bytes for length;
276 * see https://docstore.mik.ua/univercd/cc/td/doc/product/lan/trsrb/frames.htm
277 * However, actual packets on the wire appear to use 1
278 * byte for the type and 1 byte for the length, so that's
279 * what we do.
280 */
281 if (len < 2)
282 goto invalid;
283 type = GET_U_1(tptr);
284 tlv_len = GET_U_1(tptr + 1);
285
286 ND_PRINT("\n\t\t%s (0x%04x) TLV",
287 tok2str(vtp_vlan_tlv_values, "Unknown", type),
288 type);
289
290 if (len < tlv_len * 2 + 2) {
291 ND_PRINT(" (TLV goes past the end of the packet)");
292 goto invalid;
293 }
294 ND_TCHECK_LEN(tptr, tlv_len * 2 + 2);
295
296 /*
297 * We assume the value is a 2-byte integer; the length is
298 * in units of 16-bit words.
299 */
300 if (tlv_len != 1) {
301 ND_PRINT(" [TLV length %u != 1]", tlv_len);
302 goto invalid;
303 } else {
304 tlv_value = GET_BE_U_2(tptr + 2);
305
306 switch (type) {
307 case VTP_VLAN_STE_HOP_COUNT:
308 ND_PRINT(", %u", tlv_value);
309 break;
310
311 case VTP_VLAN_PRUNING:
312 ND_PRINT(", %s (%u)",
313 tlv_value == 1 ? "Enabled" : "Disabled",
314 tlv_value);
315 break;
316
317 case VTP_VLAN_STP_TYPE:
318 ND_PRINT(", %s (%u)",
319 tok2str(vtp_stp_type_values, "Unknown", tlv_value),
320 tlv_value);
321 break;
322
323 case VTP_VLAN_BRIDGE_TYPE:
324 ND_PRINT(", %s (%u)",
325 tlv_value == 1 ? "SRB" : "SRT",
326 tlv_value);
327 break;
328
329 case VTP_VLAN_BACKUP_CRF_MODE:
330 ND_PRINT(", %s (%u)",
331 tlv_value == 1 ? "Backup" : "Not backup",
332 tlv_value);
333 break;
334
335 /*
336 * FIXME those are the defined TLVs that lack a decoder
337 * you are welcome to contribute code ;-)
338 */
339
340 case VTP_VLAN_SOURCE_ROUTING_RING_NUMBER:
341 case VTP_VLAN_SOURCE_ROUTING_BRIDGE_NUMBER:
342 case VTP_VLAN_PARENT_VLAN:
343 case VTP_VLAN_TRANS_BRIDGED_VLAN:
344 case VTP_VLAN_ARP_HOP_COUNT:
345 default:
346 print_unknown_data(ndo, tptr, "\n\t\t ", 2 + tlv_len*2);
347 break;
348 }
349 }
350 len -= 2 + tlv_len*2;
351 tptr += 2 + tlv_len*2;
352 }
353 }
354 break;
355
356 case VTP_ADV_REQUEST:
357
358 /*
359 * ADVERTISEMENT REQUEST
360 *
361 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
362 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
363 * | Version | Code | Reserved | MgmtD Len |
364 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
365 * | Management Domain Name (zero-padded to 32 bytes) |
366 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
367 * | Start value |
368 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
369 *
370 */
371
372 ND_PRINT("\n\tStart value: %u", GET_BE_U_4(tptr));
373 break;
374
375 case VTP_JOIN_MESSAGE:
376
377 /* FIXME - Could not find message format */
378 break;
379
380 default:
381 break;
382 }
383
384 return;
385
386 invalid:
387 nd_print_invalid(ndo);
388 tcheck_full_packet:
389 ND_TCHECK_LEN(pptr, length);
390 }
391