xref: /netbsd-src/external/bsd/tcpdump/dist/print-stp.c (revision 26ba0b503b498a5194a71ac319838b7f5497f3fe)
10f74e101Schristos /*
20f74e101Schristos  * Copyright (c) 2000 Lennert Buytenhek
30f74e101Schristos  *
40f74e101Schristos  * This software may be distributed either under the terms of the
50f74e101Schristos  * BSD-style license that accompanies tcpdump or the GNU General
60f74e101Schristos  * Public License
70f74e101Schristos  *
80f74e101Schristos  * Contributed by Lennert Buytenhek <buytenh@gnu.org>
90f74e101Schristos  */
100f74e101Schristos 
11dc860a36Sspz /* \summary: IEEE 802.1d Spanning Tree Protocol (STP) printer */
12dc860a36Sspz 
1311b3aaa1Schristos #include <sys/cdefs.h>
140f74e101Schristos #ifndef lint
15*26ba0b50Schristos __RCSID("$NetBSD: print-stp.c,v 1.10 2024/09/02 16:15:33 christos Exp $");
160f74e101Schristos #endif
170f74e101Schristos 
18c74ad251Schristos #include <config.h>
190f74e101Schristos 
20c74ad251Schristos #include "netdissect-stdinc.h"
210f74e101Schristos 
220f74e101Schristos #include <stdio.h>
230f74e101Schristos 
24fdccd7e4Schristos #include "netdissect.h"
250f74e101Schristos #include "extract.h"
260f74e101Schristos 
270f74e101Schristos #define	RSTP_EXTRACT_PORT_ROLE(x) (((x)&0x0C)>>2)
280f74e101Schristos /* STP timers are expressed in multiples of 1/256th second */
290f74e101Schristos #define STP_TIME_BASE 256
300f74e101Schristos #define STP_BPDU_MSTP_MIN_LEN 102
310f74e101Schristos 
320f74e101Schristos struct stp_bpdu_ {
33c74ad251Schristos     nd_uint16_t protocol_id;
34c74ad251Schristos     nd_uint8_t  protocol_version;
35c74ad251Schristos     nd_uint8_t  bpdu_type;
36c74ad251Schristos     nd_uint8_t  flags;
37c74ad251Schristos     nd_byte     root_id[8];
38c74ad251Schristos     nd_uint32_t root_path_cost;
39c74ad251Schristos     nd_byte     bridge_id[8];
40c74ad251Schristos     nd_uint16_t port_id;
41c74ad251Schristos     nd_uint16_t message_age;
42c74ad251Schristos     nd_uint16_t max_age;
43c74ad251Schristos     nd_uint16_t hello_time;
44c74ad251Schristos     nd_uint16_t forward_delay;
45c74ad251Schristos     nd_uint8_t  v1_length;
460f74e101Schristos };
470f74e101Schristos 
480f74e101Schristos #define STP_PROTO_REGULAR 0x00
490f74e101Schristos #define STP_PROTO_RAPID   0x02
500f74e101Schristos #define STP_PROTO_MSTP    0x03
51870189d2Schristos #define STP_PROTO_SPB     0x04
520f74e101Schristos 
53870189d2Schristos static const struct tok stp_proto_values[] = {
540f74e101Schristos     { STP_PROTO_REGULAR, "802.1d" },
550f74e101Schristos     { STP_PROTO_RAPID, "802.1w" },
560f74e101Schristos     { STP_PROTO_MSTP, "802.1s" },
57870189d2Schristos     { STP_PROTO_SPB, "802.1aq" },
580f74e101Schristos     { 0, NULL}
590f74e101Schristos };
600f74e101Schristos 
610f74e101Schristos #define STP_BPDU_TYPE_CONFIG      0x00
620f74e101Schristos #define STP_BPDU_TYPE_RSTP        0x02
630f74e101Schristos #define STP_BPDU_TYPE_TOPO_CHANGE 0x80
640f74e101Schristos 
65870189d2Schristos static const struct tok stp_bpdu_flag_values[] = {
660f74e101Schristos     { 0x01, "Topology change" },
670f74e101Schristos     { 0x02, "Proposal" },
680f74e101Schristos     { 0x10, "Learn" },
690f74e101Schristos     { 0x20, "Forward" },
700f74e101Schristos     { 0x40, "Agreement" },
710f74e101Schristos     { 0x80, "Topology change ACK" },
720f74e101Schristos     { 0, NULL}
730f74e101Schristos };
740f74e101Schristos 
75870189d2Schristos static const struct tok stp_bpdu_type_values[] = {
760f74e101Schristos     { STP_BPDU_TYPE_CONFIG, "Config" },
770f74e101Schristos     { STP_BPDU_TYPE_RSTP, "Rapid STP" },
780f74e101Schristos     { STP_BPDU_TYPE_TOPO_CHANGE, "Topology Change" },
790f74e101Schristos     { 0, NULL}
800f74e101Schristos };
810f74e101Schristos 
82870189d2Schristos static const struct tok rstp_obj_port_role_values[] = {
830f74e101Schristos     { 0x00, "Unknown" },
840f74e101Schristos     { 0x01, "Alternate" },
850f74e101Schristos     { 0x02, "Root" },
860f74e101Schristos     { 0x03, "Designated" },
870f74e101Schristos     { 0, NULL}
880f74e101Schristos };
890f74e101Schristos 
900f74e101Schristos static char *
91c74ad251Schristos stp_print_bridge_id(netdissect_options *ndo, const u_char *p)
920f74e101Schristos {
930f74e101Schristos     static char bridge_id_str[sizeof("pppp.aa:bb:cc:dd:ee:ff")];
940f74e101Schristos 
950f74e101Schristos     snprintf(bridge_id_str, sizeof(bridge_id_str),
960f74e101Schristos              "%.2x%.2x.%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
97c74ad251Schristos              GET_U_1(p), GET_U_1(p + 1), GET_U_1(p + 2),
98c74ad251Schristos              GET_U_1(p + 3), GET_U_1(p + 4), GET_U_1(p + 5),
99c74ad251Schristos              GET_U_1(p + 6), GET_U_1(p + 7));
1000f74e101Schristos 
1010f74e101Schristos     return bridge_id_str;
1020f74e101Schristos }
1030f74e101Schristos 
104c74ad251Schristos static void
105b3a00663Schristos stp_print_config_bpdu(netdissect_options *ndo, const struct stp_bpdu_ *stp_bpdu,
106b3a00663Schristos                       u_int length)
1070f74e101Schristos {
108c74ad251Schristos     uint8_t bpdu_flags;
1090f74e101Schristos 
110c74ad251Schristos     bpdu_flags = GET_U_1(stp_bpdu->flags);
111c74ad251Schristos     ND_PRINT(", Flags [%s]",
112c74ad251Schristos            bittok2str(stp_bpdu_flag_values, "none", bpdu_flags));
113c74ad251Schristos 
114c74ad251Schristos     ND_PRINT(", bridge-id %s.%04x, length %u",
115c74ad251Schristos            stp_print_bridge_id(ndo, stp_bpdu->bridge_id),
116c74ad251Schristos            GET_BE_U_2(stp_bpdu->port_id), length);
1170f74e101Schristos 
1180f74e101Schristos     /* in non-verbose mode just print the bridge-id */
119b3a00663Schristos     if (!ndo->ndo_vflag) {
120c74ad251Schristos         return;
1210f74e101Schristos     }
1220f74e101Schristos 
123c74ad251Schristos     ND_PRINT("\n\tmessage-age %.2fs, max-age %.2fs"
1240f74e101Schristos            ", hello-time %.2fs, forwarding-delay %.2fs",
125c74ad251Schristos            (float) GET_BE_U_2(stp_bpdu->message_age) / STP_TIME_BASE,
126c74ad251Schristos            (float) GET_BE_U_2(stp_bpdu->max_age) / STP_TIME_BASE,
127c74ad251Schristos            (float) GET_BE_U_2(stp_bpdu->hello_time) / STP_TIME_BASE,
128c74ad251Schristos            (float) GET_BE_U_2(stp_bpdu->forward_delay) / STP_TIME_BASE);
1290f74e101Schristos 
130c74ad251Schristos     ND_PRINT("\n\troot-id %s, root-pathcost %u",
131c74ad251Schristos            stp_print_bridge_id(ndo, stp_bpdu->root_id),
132c74ad251Schristos            GET_BE_U_4(stp_bpdu->root_path_cost));
1330f74e101Schristos 
1340f74e101Schristos     /* Port role is only valid for 802.1w */
135c74ad251Schristos     if (GET_U_1(stp_bpdu->protocol_version) == STP_PROTO_RAPID) {
136c74ad251Schristos         ND_PRINT(", port-role %s",
1370f74e101Schristos                tok2str(rstp_obj_port_role_values, "Unknown",
138c74ad251Schristos                        RSTP_EXTRACT_PORT_ROLE(bpdu_flags)));
1390f74e101Schristos     }
1400f74e101Schristos }
1410f74e101Schristos 
1420f74e101Schristos /*
1430f74e101Schristos  * MSTP packet format
1440f74e101Schristos  * Ref. IEEE 802.1Q 2003 Ed. Section 14
1450f74e101Schristos  *
1460f74e101Schristos  * MSTP BPDU
1470f74e101Schristos  *
1480f74e101Schristos  * 2 -  bytes Protocol Id
1490f74e101Schristos  * 1 -  byte  Protocol Ver.
150c74ad251Schristos  * 1 -  byte  BPDU type
1510f74e101Schristos  * 1 -  byte  Flags
1520f74e101Schristos  * 8 -  bytes CIST Root Identifier
1530f74e101Schristos  * 4 -  bytes CIST External Path Cost
1540f74e101Schristos  * 8 -  bytes CIST Regional Root Identifier
1550f74e101Schristos  * 2 -  bytes CIST Port Identifier
1560f74e101Schristos  * 2 -  bytes Message Age
1570f74e101Schristos  * 2 -  bytes Max age
1580f74e101Schristos  * 2 -  bytes Hello Time
1590f74e101Schristos  * 2 -  bytes Forward delay
1600f74e101Schristos  * 1 -  byte  Version 1 length. Must be 0
1610f74e101Schristos  * 2 -  bytes Version 3 length
1620f74e101Schristos  * 1 -  byte  Config Identifier
1630f74e101Schristos  * 32 - bytes Config Name
1640f74e101Schristos  * 2 -  bytes Revision level
1650f74e101Schristos  * 16 - bytes Config Digest [MD5]
1660f74e101Schristos  * 4 -  bytes CIST Internal Root Path Cost
1670f74e101Schristos  * 8 -  bytes CIST Bridge Identifier
1680f74e101Schristos  * 1 -  byte  CIST Remaining Hops
1690f74e101Schristos  * 16 - bytes MSTI information [Max 64 MSTI, each 16 bytes]
1700f74e101Schristos  *
171870189d2Schristos  *
172870189d2Schristos  * SPB BPDU
173870189d2Schristos  * Ref. IEEE 802.1aq. Section 14
174870189d2Schristos  *
175870189d2Schristos  * 2 -  bytes Version 4 length
176870189d2Schristos  * 1 -  byte  Aux Config Identifier
177870189d2Schristos  * 32 - bytes Aux Config Name
178870189d2Schristos  * 2 -  bytes Aux Revision level
179870189d2Schristos  * 16 - bytes Aux Config Digest [MD5]
180870189d2Schristos  * 1 -  byte  (1 - 2) Agreement Number
181870189d2Schristos  *            (3 - 4) Discarded Agreement Number
182870189d2Schristos  *            (5) Agreement Valid Flag
183870189d2Schristos  *            (6) Restricted Role Flag
184870189d2Schristos  *            (7 - 8) Unused sent zero
185870189d2Schristos  * 1 -  byte Unused
186870189d2Schristos  * 1 -  byte (1 - 4) Agreement Digest Format Identifier
187870189d2Schristos  *           (5 - 8) Agreement Digest Format Capabilities
188870189d2Schristos  * 1 -  byte (1 - 4) Agreement Digest Convention Identifier
189870189d2Schristos  *           (5 - 8) Agreement Digest Convention Capabilities
190870189d2Schristos  * 2 -  bytes Agreement Digest Edge Count
191870189d2Schristos  * 8 -  byte Reserved Set
192870189d2Schristos  * 20 - bytes Computed Topology Digest
193870189d2Schristos  *
194870189d2Schristos  *
1950f74e101Schristos  * MSTI Payload
1960f74e101Schristos  *
1970f74e101Schristos  * 1 - byte  MSTI flag
1980f74e101Schristos  * 8 - bytes MSTI Regional Root Identifier
1990f74e101Schristos  * 4 - bytes MSTI Regional Path Cost
2000f74e101Schristos  * 1 - byte  MSTI Bridge Priority
2010f74e101Schristos  * 1 - byte  MSTI Port Priority
2020f74e101Schristos  * 1 - byte  MSTI Remaining Hops
203870189d2Schristos  *
2040f74e101Schristos  */
2050f74e101Schristos 
2060f74e101Schristos #define MST_BPDU_MSTI_LENGTH		    16
2070f74e101Schristos #define MST_BPDU_CONFIG_INFO_LENGTH	    64
2080f74e101Schristos 
209c74ad251Schristos /* Offsets of fields from the beginning for the packet */
2100f74e101Schristos #define MST_BPDU_VER3_LEN_OFFSET	    36
2110f74e101Schristos #define MST_BPDU_CONFIG_NAME_OFFSET	    39
2120f74e101Schristos #define MST_BPDU_CONFIG_DIGEST_OFFSET	    73
2130f74e101Schristos #define MST_BPDU_CIST_INT_PATH_COST_OFFSET  89
2140f74e101Schristos #define MST_BPDU_CIST_BRIDGE_ID_OFFSET	    93
2150f74e101Schristos #define MST_BPDU_CIST_REMAIN_HOPS_OFFSET    101
2160f74e101Schristos #define MST_BPDU_MSTI_OFFSET		    102
2170f74e101Schristos /* Offsets within  an MSTI */
2180f74e101Schristos #define MST_BPDU_MSTI_ROOT_PRIO_OFFSET	    1
2190f74e101Schristos #define MST_BPDU_MSTI_ROOT_PATH_COST_OFFSET 9
2200f74e101Schristos #define MST_BPDU_MSTI_BRIDGE_PRIO_OFFSET    13
2210f74e101Schristos #define MST_BPDU_MSTI_PORT_PRIO_OFFSET	    14
2220f74e101Schristos #define MST_BPDU_MSTI_REMAIN_HOPS_OFFSET    15
2230f74e101Schristos 
224870189d2Schristos #define SPB_BPDU_MIN_LEN                  87
225870189d2Schristos #define SPB_BPDU_CONFIG_NAME_OFFSET       3
226870189d2Schristos #define SPB_BPDU_CONFIG_REV_OFFSET        SPB_BPDU_CONFIG_NAME_OFFSET + 32
227870189d2Schristos #define SPB_BPDU_CONFIG_DIGEST_OFFSET     SPB_BPDU_CONFIG_REV_OFFSET + 2
228870189d2Schristos #define SPB_BPDU_AGREEMENT_OFFSET         SPB_BPDU_CONFIG_DIGEST_OFFSET + 16
229870189d2Schristos #define SPB_BPDU_AGREEMENT_UNUSED_OFFSET  SPB_BPDU_AGREEMENT_OFFSET + 1
230870189d2Schristos #define SPB_BPDU_AGREEMENT_FORMAT_OFFSET  SPB_BPDU_AGREEMENT_UNUSED_OFFSET + 1
231870189d2Schristos #define SPB_BPDU_AGREEMENT_CON_OFFSET     SPB_BPDU_AGREEMENT_FORMAT_OFFSET + 1
232870189d2Schristos #define SPB_BPDU_AGREEMENT_EDGE_OFFSET    SPB_BPDU_AGREEMENT_CON_OFFSET + 1
233870189d2Schristos #define SPB_BPDU_AGREEMENT_RES1_OFFSET    SPB_BPDU_AGREEMENT_EDGE_OFFSET + 2
234870189d2Schristos #define SPB_BPDU_AGREEMENT_RES2_OFFSET    SPB_BPDU_AGREEMENT_RES1_OFFSET + 4
235870189d2Schristos #define SPB_BPDU_AGREEMENT_DIGEST_OFFSET  SPB_BPDU_AGREEMENT_RES2_OFFSET + 4
236870189d2Schristos 
237c74ad251Schristos static void
238b3a00663Schristos stp_print_mstp_bpdu(netdissect_options *ndo, const struct stp_bpdu_ *stp_bpdu,
239b3a00663Schristos                     u_int length)
2400f74e101Schristos {
2410f74e101Schristos     const u_char *ptr;
242c74ad251Schristos     uint8_t	    bpdu_flags;
243b3a00663Schristos     uint16_t	    v3len;
244b3a00663Schristos     uint16_t	    len;
245b3a00663Schristos     uint16_t	    msti;
246870189d2Schristos     u_int	    offset;
2470f74e101Schristos 
2480f74e101Schristos     ptr = (const u_char *)stp_bpdu;
249c74ad251Schristos     bpdu_flags = GET_U_1(stp_bpdu->flags);
250c74ad251Schristos     ND_PRINT(", CIST Flags [%s], length %u",
251c74ad251Schristos            bittok2str(stp_bpdu_flag_values, "none", bpdu_flags), length);
2520f74e101Schristos 
2530f74e101Schristos     /*
254870189d2Schristos      * in non-verbose mode just print the flags.
2550f74e101Schristos      */
256b3a00663Schristos     if (!ndo->ndo_vflag) {
257c74ad251Schristos         return;
2580f74e101Schristos     }
2590f74e101Schristos 
260c74ad251Schristos     ND_PRINT("\n\tport-role %s, ",
261870189d2Schristos            tok2str(rstp_obj_port_role_values, "Unknown",
262c74ad251Schristos                    RSTP_EXTRACT_PORT_ROLE(bpdu_flags)));
2630f74e101Schristos 
264c74ad251Schristos     ND_PRINT("CIST root-id %s, CIST ext-pathcost %u",
265c74ad251Schristos            stp_print_bridge_id(ndo, stp_bpdu->root_id),
266c74ad251Schristos            GET_BE_U_4(stp_bpdu->root_path_cost));
267870189d2Schristos 
268c74ad251Schristos     ND_PRINT("\n\tCIST regional-root-id %s, ",
269c74ad251Schristos            stp_print_bridge_id(ndo, stp_bpdu->bridge_id));
270870189d2Schristos 
271c74ad251Schristos     ND_PRINT("CIST port-id %04x,", GET_BE_U_2(stp_bpdu->port_id));
2720f74e101Schristos 
273c74ad251Schristos     ND_PRINT("\n\tmessage-age %.2fs, max-age %.2fs"
2740f74e101Schristos            ", hello-time %.2fs, forwarding-delay %.2fs",
275c74ad251Schristos            (float) GET_BE_U_2(stp_bpdu->message_age) / STP_TIME_BASE,
276c74ad251Schristos            (float) GET_BE_U_2(stp_bpdu->max_age) / STP_TIME_BASE,
277c74ad251Schristos            (float) GET_BE_U_2(stp_bpdu->hello_time) / STP_TIME_BASE,
278c74ad251Schristos            (float) GET_BE_U_2(stp_bpdu->forward_delay) / STP_TIME_BASE);
2790f74e101Schristos 
280c74ad251Schristos     ND_PRINT("\n\tv3len %u, ", GET_BE_U_2(ptr + MST_BPDU_VER3_LEN_OFFSET));
281c74ad251Schristos     ND_PRINT("MCID Name ");
282c74ad251Schristos     nd_printjnp(ndo, ptr + MST_BPDU_CONFIG_NAME_OFFSET, 32);
283c74ad251Schristos     ND_PRINT(", rev %u,"
284870189d2Schristos             "\n\t\tdigest %08x%08x%08x%08x, ",
285c74ad251Schristos 	          GET_BE_U_2(ptr + MST_BPDU_CONFIG_NAME_OFFSET + 32),
286c74ad251Schristos 	          GET_BE_U_4(ptr + MST_BPDU_CONFIG_DIGEST_OFFSET),
287c74ad251Schristos 	          GET_BE_U_4(ptr + MST_BPDU_CONFIG_DIGEST_OFFSET + 4),
288c74ad251Schristos 	          GET_BE_U_4(ptr + MST_BPDU_CONFIG_DIGEST_OFFSET + 8),
289c74ad251Schristos 	          GET_BE_U_4(ptr + MST_BPDU_CONFIG_DIGEST_OFFSET + 12));
2900f74e101Schristos 
291c74ad251Schristos     ND_PRINT("CIST int-root-pathcost %u,",
292c74ad251Schristos             GET_BE_U_4(ptr + MST_BPDU_CIST_INT_PATH_COST_OFFSET));
293870189d2Schristos 
294c74ad251Schristos     ND_PRINT("\n\tCIST bridge-id %s, ",
295c74ad251Schristos            stp_print_bridge_id(ndo, ptr + MST_BPDU_CIST_BRIDGE_ID_OFFSET));
296870189d2Schristos 
297c74ad251Schristos     ND_PRINT("CIST remaining-hops %u",
298c74ad251Schristos              GET_U_1(ptr + MST_BPDU_CIST_REMAIN_HOPS_OFFSET));
2990f74e101Schristos 
3000f74e101Schristos     /* Dump all MSTI's */
301c74ad251Schristos     v3len = GET_BE_U_2(ptr + MST_BPDU_VER3_LEN_OFFSET);
3020f74e101Schristos     if (v3len > MST_BPDU_CONFIG_INFO_LENGTH) {
3030f74e101Schristos         len = v3len - MST_BPDU_CONFIG_INFO_LENGTH;
3040f74e101Schristos         offset = MST_BPDU_MSTI_OFFSET;
3050f74e101Schristos         while (len >= MST_BPDU_MSTI_LENGTH) {
306c74ad251Schristos             msti = GET_BE_U_2(ptr + offset + MST_BPDU_MSTI_ROOT_PRIO_OFFSET);
3070f74e101Schristos             msti = msti & 0x0FFF;
3080f74e101Schristos 
309c74ad251Schristos             ND_PRINT("\n\tMSTI %u, Flags [%s], port-role %s",
310c74ad251Schristos                    msti,
311c74ad251Schristos                    bittok2str(stp_bpdu_flag_values, "none", GET_U_1(ptr + offset)),
3120f74e101Schristos                    tok2str(rstp_obj_port_role_values, "Unknown",
313c74ad251Schristos                            RSTP_EXTRACT_PORT_ROLE(GET_U_1(ptr + offset))));
314c74ad251Schristos             ND_PRINT("\n\t\tMSTI regional-root-id %s, pathcost %u",
315c74ad251Schristos                    stp_print_bridge_id(ndo, ptr + offset +
3160f74e101Schristos                                        MST_BPDU_MSTI_ROOT_PRIO_OFFSET),
317c74ad251Schristos                    GET_BE_U_4(ptr + offset + MST_BPDU_MSTI_ROOT_PATH_COST_OFFSET));
318c74ad251Schristos             ND_PRINT("\n\t\tMSTI bridge-prio %u, port-prio %u, hops %u",
319c74ad251Schristos                    GET_U_1(ptr + offset + MST_BPDU_MSTI_BRIDGE_PRIO_OFFSET) >> 4,
320c74ad251Schristos                    GET_U_1(ptr + offset + MST_BPDU_MSTI_PORT_PRIO_OFFSET) >> 4,
321c74ad251Schristos                    GET_U_1(ptr + offset + MST_BPDU_MSTI_REMAIN_HOPS_OFFSET));
3220f74e101Schristos 
3230f74e101Schristos             len -= MST_BPDU_MSTI_LENGTH;
3240f74e101Schristos             offset += MST_BPDU_MSTI_LENGTH;
3250f74e101Schristos         }
3260f74e101Schristos     }
3270f74e101Schristos }
3280f74e101Schristos 
329c74ad251Schristos static void
330b3a00663Schristos stp_print_spb_bpdu(netdissect_options *ndo, const struct stp_bpdu_ *stp_bpdu,
331b3a00663Schristos                    u_int offset)
332870189d2Schristos {
333870189d2Schristos     const u_char *ptr;
334870189d2Schristos 
3350f74e101Schristos     /*
336870189d2Schristos      * in non-verbose mode don't print anything.
337870189d2Schristos      */
338b3a00663Schristos     if (!ndo->ndo_vflag) {
339c74ad251Schristos         return;
340870189d2Schristos     }
341870189d2Schristos 
342870189d2Schristos     ptr = (const u_char *)stp_bpdu;
343dc860a36Sspz 
344c74ad251Schristos     ND_PRINT("\n\tv4len %u, ", GET_BE_U_2(ptr + offset));
345c74ad251Schristos     ND_PRINT("AUXMCID Name ");
346c74ad251Schristos     nd_printjnp(ndo, ptr + offset + SPB_BPDU_CONFIG_NAME_OFFSET, 32);
347c74ad251Schristos     ND_PRINT(", Rev %u,\n\t\tdigest %08x%08x%08x%08x",
348c74ad251Schristos             GET_BE_U_2(ptr + offset + SPB_BPDU_CONFIG_REV_OFFSET),
349c74ad251Schristos             GET_BE_U_4(ptr + offset + SPB_BPDU_CONFIG_DIGEST_OFFSET),
350c74ad251Schristos             GET_BE_U_4(ptr + offset + SPB_BPDU_CONFIG_DIGEST_OFFSET + 4),
351c74ad251Schristos             GET_BE_U_4(ptr + offset + SPB_BPDU_CONFIG_DIGEST_OFFSET + 8),
352c74ad251Schristos             GET_BE_U_4(ptr + offset + SPB_BPDU_CONFIG_DIGEST_OFFSET + 12));
353870189d2Schristos 
354c74ad251Schristos     ND_PRINT("\n\tAgreement num %u, Discarded Agreement num %u, Agreement valid-"
355c74ad251Schristos             "flag %u,\n\tRestricted role-flag: %u, Format id %u cap %u, "
356c74ad251Schristos             "Convention id %u cap %u,\n\tEdge count %u, "
357c74ad251Schristos             "Agreement digest %08x%08x%08x%08x%08x",
358c74ad251Schristos             GET_U_1(ptr + offset + SPB_BPDU_AGREEMENT_OFFSET)>>6,
359c74ad251Schristos             GET_U_1(ptr + offset + SPB_BPDU_AGREEMENT_OFFSET)>>4 & 0x3,
360c74ad251Schristos             GET_U_1(ptr + offset + SPB_BPDU_AGREEMENT_OFFSET)>>3 & 0x1,
361c74ad251Schristos             GET_U_1(ptr + offset + SPB_BPDU_AGREEMENT_OFFSET)>>2 & 0x1,
362c74ad251Schristos             GET_U_1(ptr + offset + SPB_BPDU_AGREEMENT_FORMAT_OFFSET)>>4,
363c74ad251Schristos             GET_U_1(ptr + offset + SPB_BPDU_AGREEMENT_FORMAT_OFFSET)&0x00ff,
364c74ad251Schristos             GET_U_1(ptr + offset + SPB_BPDU_AGREEMENT_CON_OFFSET)>>4,
365c74ad251Schristos             GET_U_1(ptr + offset + SPB_BPDU_AGREEMENT_CON_OFFSET)&0x00ff,
366c74ad251Schristos             GET_BE_U_2(ptr + offset + SPB_BPDU_AGREEMENT_EDGE_OFFSET),
367c74ad251Schristos             GET_BE_U_4(ptr + offset + SPB_BPDU_AGREEMENT_DIGEST_OFFSET),
368c74ad251Schristos             GET_BE_U_4(ptr + offset + SPB_BPDU_AGREEMENT_DIGEST_OFFSET + 4),
369c74ad251Schristos             GET_BE_U_4(ptr + offset + SPB_BPDU_AGREEMENT_DIGEST_OFFSET + 8),
370c74ad251Schristos             GET_BE_U_4(ptr + offset + SPB_BPDU_AGREEMENT_DIGEST_OFFSET + 12),
371c74ad251Schristos             GET_BE_U_4(ptr + offset + SPB_BPDU_AGREEMENT_DIGEST_OFFSET + 16));
372870189d2Schristos }
373870189d2Schristos 
374870189d2Schristos /*
375870189d2Schristos  * Print 802.1d / 802.1w / 802.1q (mstp) / 802.1aq (spb) packets.
3760f74e101Schristos  */
3770f74e101Schristos void
378b3a00663Schristos stp_print(netdissect_options *ndo, const u_char *p, u_int length)
3790f74e101Schristos {
3800f74e101Schristos     const struct stp_bpdu_ *stp_bpdu;
381c74ad251Schristos     u_int                  protocol_version;
382c74ad251Schristos     u_int                  bpdu_type;
383870189d2Schristos     u_int                  mstp_len;
384870189d2Schristos     u_int                  spb_len;
3850f74e101Schristos 
386c74ad251Schristos     ndo->ndo_protocol = "stp";
387fdccd7e4Schristos     stp_bpdu = (const struct stp_bpdu_*)p;
3880f74e101Schristos 
3890f74e101Schristos     /* Minimum STP Frame size. */
3900f74e101Schristos     if (length < 4)
391c74ad251Schristos         goto invalid;
3920f74e101Schristos 
393c74ad251Schristos     if (GET_BE_U_2(stp_bpdu->protocol_id)) {
394c74ad251Schristos         ND_PRINT("unknown STP version, length %u", length);
3950f74e101Schristos         return;
3960f74e101Schristos     }
3970f74e101Schristos 
398c74ad251Schristos     protocol_version = GET_U_1(stp_bpdu->protocol_version);
399c74ad251Schristos     ND_PRINT("STP %s", tok2str(stp_proto_values, "Unknown STP protocol (0x%02x)",
400c74ad251Schristos                          protocol_version));
4010f74e101Schristos 
402c74ad251Schristos     switch (protocol_version) {
4030f74e101Schristos     case STP_PROTO_REGULAR:
4040f74e101Schristos     case STP_PROTO_RAPID:
4050f74e101Schristos     case STP_PROTO_MSTP:
406870189d2Schristos     case STP_PROTO_SPB:
4070f74e101Schristos         break;
4080f74e101Schristos     default:
4090f74e101Schristos         return;
4100f74e101Schristos     }
4110f74e101Schristos 
412c74ad251Schristos     bpdu_type = GET_U_1(stp_bpdu->bpdu_type);
413c74ad251Schristos     ND_PRINT(", %s", tok2str(stp_bpdu_type_values, "Unknown BPDU Type (0x%02x)",
414c74ad251Schristos                            bpdu_type));
4150f74e101Schristos 
416c74ad251Schristos     switch (bpdu_type) {
4170f74e101Schristos     case STP_BPDU_TYPE_CONFIG:
4180f74e101Schristos         if (length < sizeof(struct stp_bpdu_) - 1) {
419c74ad251Schristos             goto invalid;
4200f74e101Schristos         }
421c74ad251Schristos         stp_print_config_bpdu(ndo, stp_bpdu, length);
4220f74e101Schristos         break;
4230f74e101Schristos 
4240f74e101Schristos     case STP_BPDU_TYPE_RSTP:
425c74ad251Schristos         if (protocol_version == STP_PROTO_RAPID) {
4260f74e101Schristos             if (length < sizeof(struct stp_bpdu_)) {
427c74ad251Schristos                 goto invalid;
4280f74e101Schristos             }
429c74ad251Schristos             stp_print_config_bpdu(ndo, stp_bpdu, length);
430c74ad251Schristos         } else if (protocol_version == STP_PROTO_MSTP ||
431c74ad251Schristos                    protocol_version == STP_PROTO_SPB) {
4320f74e101Schristos             if (length < STP_BPDU_MSTP_MIN_LEN) {
433c74ad251Schristos                 goto invalid;
4340f74e101Schristos             }
435870189d2Schristos 
436c74ad251Schristos             if (GET_U_1(stp_bpdu->v1_length) != 0) {
4370f74e101Schristos                 /* FIX ME: Emit a message here ? */
438c74ad251Schristos                 goto invalid;
4390f74e101Schristos             }
440870189d2Schristos 
4410f74e101Schristos             /* Validate v3 length */
442c74ad251Schristos             mstp_len = GET_BE_U_2(p + MST_BPDU_VER3_LEN_OFFSET);
4430f74e101Schristos             mstp_len += 2;  /* length encoding itself is 2 bytes */
4440f74e101Schristos             if (length < (sizeof(struct stp_bpdu_) + mstp_len)) {
445c74ad251Schristos                 goto invalid;
4460f74e101Schristos             }
447c74ad251Schristos             stp_print_mstp_bpdu(ndo, stp_bpdu, length);
448870189d2Schristos 
449*26ba0b50Schristos             if (protocol_version == STP_PROTO_SPB) {
450870189d2Schristos               /* Validate v4 length */
451c74ad251Schristos               spb_len = GET_BE_U_2(p + MST_BPDU_VER3_LEN_OFFSET + mstp_len);
452870189d2Schristos               spb_len += 2;
453870189d2Schristos               if (length < (sizeof(struct stp_bpdu_) + mstp_len + spb_len) ||
454870189d2Schristos                   spb_len < SPB_BPDU_MIN_LEN) {
455c74ad251Schristos                 goto invalid;
456870189d2Schristos               }
457c74ad251Schristos               stp_print_spb_bpdu(ndo, stp_bpdu, (sizeof(struct stp_bpdu_) + mstp_len));
458870189d2Schristos             }
4590f74e101Schristos         }
4600f74e101Schristos         break;
4610f74e101Schristos 
4620f74e101Schristos     case STP_BPDU_TYPE_TOPO_CHANGE:
4630f74e101Schristos         /* always empty message - just break out */
4640f74e101Schristos         break;
4650f74e101Schristos 
4660f74e101Schristos     default:
4670f74e101Schristos         break;
4680f74e101Schristos     }
4690f74e101Schristos     return;
4700f74e101Schristos 
471c74ad251Schristos invalid:
472c74ad251Schristos     nd_print_invalid(ndo);
473c74ad251Schristos }
474