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