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