1 /************************************************************************ 2 * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) 3 * Copyright (C) 2001-2003 Optical Access 4 * Author: Alex Rozin 5 * 6 * This file is part of RSTP library. 7 * 8 * RSTP library is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU Lesser General Public License as published by the 10 * Free Software Foundation; version 2.1 11 * 12 * RSTP library is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 15 * General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public License 18 * along with RSTP library; see the file COPYING. If not, write to the Free 19 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 20 * 02111-1307, USA. 21 **********************************************************************/ 22 23 #include "base.h" 24 #include "stpm.h" 25 #include "stp_vectors.h" 26 27 /* The Port Information State Machine : 17.21 */ 28 29 #define STATES { \ 30 CHOOSE(DISABLED), \ 31 CHOOSE(ENABLED), \ 32 CHOOSE(AGED), \ 33 CHOOSE(UPDATE), \ 34 CHOOSE(CURRENT), \ 35 CHOOSE(RECEIVE), \ 36 CHOOSE(SUPERIOR), \ 37 CHOOSE(REPEAT), \ 38 CHOOSE(AGREEMENT) \ 39 } 40 41 #define GET_STATE_NAME STP_info_get_state_name 42 #include "choose.h" 43 44 #if 0 /* for debug */ 45 void 46 _stp_dump (char* title, unsigned char* buff, int len) 47 { 48 register int iii; 49 50 stp_trace ("\n%s:", title); 51 for (iii = 0; iii < len; iii++) { 52 if (! (iii % 24)) stp_trace ("\n%6d:", iii); 53 if (! (iii % 8)) stp_trace (" "); 54 stp_trace ("%02lx", (unsigned long) buff[iii]); 55 } 56 stp_trace ("\n"); 57 } 58 #endif 59 60 static RCVD_MSG_T 61 rcvBpdu (STATE_MACH_T* this) 62 {/* 17.19.8 */ 63 int bridcmp; 64 register PORT_T* port = this->owner.port; 65 66 if (port->msgBpduType == BPDU_TOPO_CHANGE_TYPE) { 67 #ifdef STP_DBG 68 if (this->debug) { 69 stp_trace ("%s", "rcvBpdu: OtherMsg:BPDU_TOPO_CHANGE_TYPE"); 70 } 71 #endif 72 return OtherMsg; 73 } 74 75 port->msgPortRole = RSTP_PORT_ROLE_UNKN; 76 77 if (BPDU_RSTP == port->msgBpduType) { 78 port->msgPortRole = (port->msgFlags & PORT_ROLE_MASK) >> PORT_ROLE_OFFS; 79 } 80 81 if (RSTP_PORT_ROLE_DESGN == port->msgPortRole || 82 BPDU_CONFIG_TYPE == port->msgBpduType) { 83 bridcmp = STP_VECT_compare_vector (&port->msgPrio, &port->portPrio); 84 85 if (bridcmp < 0 || 86 (! STP_VECT_compare_bridge_id (&port->msgPrio.design_bridge, 87 &port->portPrio.design_bridge) && 88 port->msgPrio.design_port == port->portPrio.design_port && 89 STP_compare_times (&port->msgTimes, &port->portTimes))) { 90 #ifdef STP_DBG 91 if (this->debug) { 92 stp_trace ("rcvBpdu: SuperiorDesignateMsg:bridcmp=%d", (int) bridcmp); 93 } 94 #endif 95 return SuperiorDesignateMsg; 96 } 97 } 98 99 if (BPDU_CONFIG_TYPE == port->msgBpduType || 100 RSTP_PORT_ROLE_DESGN == port->msgPortRole) { 101 if (! STP_VECT_compare_vector (&port->msgPrio, 102 &port->portPrio) && 103 ! STP_compare_times (&port->msgTimes, &port->portTimes)) { 104 #ifdef STP_DBG 105 if (this->debug) { 106 stp_trace ("%s", "rcvBpdu: RepeatedDesignateMsg"); 107 } 108 #endif 109 return RepeatedDesignateMsg; 110 } 111 } 112 113 if (RSTP_PORT_ROLE_ROOT == port->msgBpduType && 114 port->operPointToPointMac && 115 ! STP_VECT_compare_bridge_id (&port->msgPrio.design_bridge, 116 &port->portPrio.design_bridge) && 117 AGREEMENT_BIT & port->msgFlags) { 118 #ifdef STP_DBG 119 if (this->debug) { 120 stp_trace ("%s", "rcvBpdu: ConfirmedRootMsg"); 121 } 122 #endif 123 return ConfirmedRootMsg; 124 } 125 126 #ifdef STP_DBG 127 if (this->debug) { 128 if (RSTP_PORT_ROLE_ROOT == port->msgBpduType) { 129 if (!port->operPointToPointMac) { 130 stp_trace("rcvBpdu: OtherMsg: not point-to-point MAC"); 131 } else if (STP_VECT_compare_bridge_id (&port->msgPrio.design_bridge, 132 &port->portPrio.design_bridge)) { 133 STP_VECT_br_id_print("rcvBpdu: OtherMsg: msgPrio", &port->msgPrio.design_bridge, True); 134 STP_VECT_br_id_print("rcvBpdu: portPrio", &port->portPrio.design_bridge, True); 135 } else { 136 stp_trace("rcvBpdu: OtherMsg: agreement bit not set"); 137 } 138 } else { 139 stp_trace ("rcvBpdu: OtherMsg: type %d", port->msgBpduType); 140 } 141 } 142 #endif 143 return OtherMsg; 144 } 145 146 /* ARGSUSED */ 147 static Bool 148 recordProposed (STATE_MACH_T* this, char* reason) 149 {/* 17.19.9 */ 150 register PORT_T* port = this->owner.port; 151 152 if (RSTP_PORT_ROLE_DESGN == port->msgPortRole && 153 (PROPOSAL_BIT & port->msgFlags) && 154 port->operPointToPointMac) { 155 return True; 156 } 157 return False; 158 } 159 160 static void 161 setTcFlags (STATE_MACH_T* this) 162 {/* 17.19.13 */ 163 register PORT_T* port = this->owner.port; 164 165 if (BPDU_TOPO_CHANGE_TYPE == port->msgBpduType) { 166 #ifdef STP_DBG 167 if (this->debug) { 168 stp_trace ("port %s rx rcvdTcn", port->port_name); 169 } 170 #endif 171 port->rcvdTcn = True; 172 } else { 173 if (TOPOLOGY_CHANGE_BIT & port->msgFlags) { 174 #ifdef STP_DBG 175 if (this->debug) { 176 stp_trace ("(%s-%s) rx rcvdTc 0X%lx", 177 port->owner->name, port->port_name, 178 (unsigned long) port->msgFlags); 179 } 180 #endif 181 port->rcvdTc = True; 182 } 183 if (TOPOLOGY_CHANGE_ACK_BIT & port->msgFlags) { 184 #ifdef STP_DBG 185 if (this->debug) { 186 stp_trace ("port %s rx rcvdTcAck 0X%lx", 187 port->port_name, 188 (unsigned long) port->msgFlags); 189 } 190 #endif 191 port->rcvdTcAck = True; 192 } 193 } 194 } 195 196 static void 197 updtBPDUVersion (STATE_MACH_T* this) 198 {/* 17.19.18 */ 199 register PORT_T* port = this->owner.port; 200 201 if (BPDU_TOPO_CHANGE_TYPE == port->msgBpduType) { 202 port->rcvdSTP = True; 203 } 204 205 if (port->msgBpduVersion < 2) { 206 port->rcvdSTP = True; 207 } 208 209 if (BPDU_RSTP == port->msgBpduType) { 210 /* port->port->owner->ForceVersion >= NORMAL_RSTP 211 we have checked in STP_info_rx_bpdu */ 212 port->rcvdRSTP = True; 213 } 214 } 215 216 static void 217 updtRcvdInfoWhile (STATE_MACH_T* this) 218 {/* 17.19.19 */ 219 register int eff_age, dm, dt; 220 register int hello3; 221 register PORT_T* port = this->owner.port; 222 223 eff_age = ( + port->portTimes.MaxAge) / 16; 224 if (eff_age < 1) eff_age = 1; 225 eff_age += port->portTimes.MessageAge; 226 227 if (eff_age <= port->portTimes.MaxAge) { 228 hello3 = 3 * port->portTimes.HelloTime; 229 dm = port->portTimes.MaxAge - eff_age; 230 if (dm > hello3) 231 dt = hello3; 232 else 233 dt = dm; 234 port->rcvdInfoWhile = dt; 235 /**** 236 stp_trace ("ma=%d eff_age=%d dm=%d dt=%d p=%s", 237 (int) port->portTimes.MessageAge, 238 (int) eff_age, (int) dm, (int) dt, port->port_name); 239 ****/ 240 } else { 241 port->rcvdInfoWhile = 0; 242 /****/ 243 #ifdef STP_DBG 244 /*if (this->debug) */ 245 { 246 stp_trace ("port %s: MaxAge=%d MessageAge=%d HelloTime=%d rcvdInfoWhile=null !", 247 port->port_name, 248 (int) port->portTimes.MaxAge, 249 (int) port->portTimes.MessageAge, 250 (int) port->portTimes.HelloTime); 251 } 252 #endif 253 /****/ 254 } 255 } 256 257 258 /* ARGSUSED */ 259 void 260 STP_info_rx_bpdu (PORT_T* port, struct stp_bpdu_t* bpdu, size_t len) 261 { 262 #if 0 263 _stp_dump ("\nall BPDU", ((unsigned char*) bpdu) - 12, len + 12); 264 _stp_dump ("ETH_HEADER", (unsigned char*) &bpdu->eth, 5); 265 _stp_dump ("BPDU_HEADER", (unsigned char*) &bpdu->hdr, 4); 266 stp_trace ("protocol=%02x%02x version=%02x bpdu_type=%02x\n", 267 bpdu->hdr.protocol[0], bpdu->hdr.protocol[1], 268 bpdu->hdr.version, bpdu->hdr.bpdu_type); 269 270 _stp_dump ("\nBPDU_BODY", (unsigned char*) &bpdu->body, sizeof (BPDU_BODY_T) + 2); 271 stp_trace ("flags=%02x\n", bpdu->body.flags); 272 _stp_dump ("root_id", bpdu->body.root_id, 8); 273 _stp_dump ("root_path_cost", bpdu->body.root_path_cost, 4); 274 _stp_dump ("bridge_id", bpdu->body.bridge_id, 8); 275 _stp_dump ("port_id", bpdu->body.port_id, 2); 276 _stp_dump ("message_age", bpdu->body.message_age, 2); 277 _stp_dump ("max_age", bpdu->body.max_age, 2); 278 _stp_dump ("hello_time", bpdu->body.hello_time, 2); 279 _stp_dump ("forward_delay", bpdu->body.forward_delay, 2); 280 _stp_dump ("ver_1_len", bpdu->ver_1_len, 2); 281 #endif 282 283 /* check bpdu type */ 284 switch (bpdu->hdr.bpdu_type) { 285 case BPDU_CONFIG_TYPE: 286 port->rx_cfg_bpdu_cnt++; 287 #ifdef STP_DBG 288 if (port->info->debug) 289 stp_trace ("CfgBpdu on port %s", port->port_name); 290 #endif 291 if (port->admin_non_stp) return; 292 port->rcvdBpdu = True; 293 break; 294 case BPDU_TOPO_CHANGE_TYPE: 295 port->rx_tcn_bpdu_cnt++; 296 #ifdef STP_DBG 297 if (port->info->debug) 298 stp_trace ("TcnBpdu on port %s", port->port_name); 299 #endif 300 if (port->admin_non_stp) return; 301 port->rcvdBpdu = True; 302 port->msgBpduVersion = bpdu->hdr.version; 303 port->msgBpduType = bpdu->hdr.bpdu_type; 304 return; 305 default: 306 stp_trace ("RX undef bpdu type=%d", (int) bpdu->hdr.bpdu_type); 307 return; 308 case BPDU_RSTP: 309 port->rx_rstp_bpdu_cnt++; 310 if (port->admin_non_stp) return; 311 if (port->owner->ForceVersion >= NORMAL_RSTP) { 312 port->rcvdBpdu = True; 313 } else { 314 return; 315 } 316 #ifdef STP_DBG 317 if (port->info->debug) 318 stp_trace ("BPDU_RSTP on port %s", port->port_name); 319 #endif 320 break; 321 } 322 323 port->msgBpduVersion = bpdu->hdr.version; 324 port->msgBpduType = bpdu->hdr.bpdu_type; 325 port->msgFlags = bpdu->body.flags; 326 327 /* 17.18.11 */ 328 STP_VECT_get_vector (&bpdu->body, &port->msgPrio); 329 port->msgPrio.bridge_port = port->port_id; 330 331 /* 17.18.12 */ 332 STP_get_times (&bpdu->body, &port->msgTimes); 333 334 /* 17.18.25, 17.18.26 : see setTcFlags() */ 335 } 336 337 void STP_info_enter_state (STATE_MACH_T* this) 338 { 339 register PORT_T* port = this->owner.port; 340 341 switch (this->State) { 342 case BEGIN: 343 port->rcvdMsg = OtherMsg; 344 port->msgBpduType = (unsigned char)-1; 345 port->msgPortRole = RSTP_PORT_ROLE_UNKN; 346 port->msgFlags = 0; 347 348 /* clear port statistics */ 349 port->rx_cfg_bpdu_cnt = 350 port->rx_rstp_bpdu_cnt = 351 port->rx_tcn_bpdu_cnt = 0; 352 /* FALLTHRU */ 353 case DISABLED: 354 port->rcvdBpdu = port->rcvdRSTP = port->rcvdSTP = False; 355 port->updtInfo = port->proposing = False; /* In DISABLED */ 356 port->agreed = port->proposed = False; 357 port->rcvdInfoWhile = 0; 358 port->infoIs = Disabled; 359 port->reselect = True; 360 port->selected = False; 361 break; 362 case ENABLED: /* IEEE 802.1y, 17.21, Z.14 */ 363 STP_VECT_copy (&port->portPrio, &port->designPrio); 364 STP_copy_times (&port->portTimes, &port->designTimes); 365 break; 366 case AGED: 367 port->infoIs = Aged; 368 port->reselect = True; 369 port->selected = False; 370 break; 371 case UPDATE: 372 STP_VECT_copy (&port->portPrio, &port->designPrio); 373 STP_copy_times (&port->portTimes, &port->designTimes); 374 port->updtInfo = False; 375 port->agreed = port->synced = False; /* In UPDATE */ 376 port->proposed = port->proposing = False; /* in UPDATE */ 377 port->infoIs = Mine; 378 port->newInfo = True; 379 #ifdef STP_DBG 380 if (this->debug) { 381 STP_VECT_br_id_print ("updated: portPrio.design_bridge", 382 &port->portPrio.design_bridge, True); 383 STP_VECT_br_id_print ("updated: portPrio.root_bridge", 384 &port->portPrio.root_bridge, True); 385 } 386 #endif 387 break; 388 case CURRENT: 389 break; 390 case RECEIVE: 391 port->rcvdMsg = rcvBpdu (this); 392 updtBPDUVersion (this); 393 setTcFlags (this); 394 port->rcvdBpdu = False; 395 break; 396 case SUPERIOR: 397 STP_VECT_copy (&port->portPrio, &port->msgPrio); 398 STP_copy_times (&port->portTimes, &port->msgTimes); 399 updtRcvdInfoWhile (this); 400 #if 1 /* due 802.1y, Z.7 */ 401 port->agreed = False; /* deleted due 802.y in SUPERIOR */ 402 port->synced = False; /* due 802.y deleted in SUPERIOR */ 403 #endif 404 port->proposing = False; /* in SUPERIOR */ 405 port->proposed = recordProposed (this, "SUPERIOR"); 406 port->infoIs = Received; 407 port->reselect = True; 408 port->selected = False; 409 #ifdef STP_DBG 410 if (this->debug) { 411 STP_VECT_br_id_print ("stored: portPrio.design_bridge", 412 &port->portPrio.design_bridge, True); 413 STP_VECT_br_id_print ("stored: portPrio.root_bridge", 414 &port->portPrio.root_bridge, True); 415 stp_trace ("proposed=%d on port %s", 416 (int) port->proposed, port->port_name); 417 } 418 #endif 419 break; 420 case REPEAT: 421 port->proposed = recordProposed (this, "REPEAT"); 422 updtRcvdInfoWhile (this); 423 break; 424 case AGREEMENT: 425 #ifdef STP_DBG 426 if (port->roletrns->debug) { 427 stp_trace ("(%s-%s) rx AGREEMENT flag !", 428 port->owner->name, port->port_name); 429 } 430 #endif 431 432 port->agreed = True; 433 port->proposing = False; /* In AGREEMENT */ 434 break; 435 } 436 437 } 438 439 Bool STP_info_check_conditions (STATE_MACH_T* this) 440 { 441 register PORT_T* port = this->owner.port; 442 443 if ((! port->portEnabled && port->infoIs != Disabled) || BEGIN == this->State) { 444 return STP_hop_2_state (this, DISABLED); 445 } 446 447 switch (this->State) { 448 case DISABLED: 449 if (port->updtInfo) { 450 return STP_hop_2_state (this, DISABLED); 451 } 452 if (port->portEnabled && port->selected) { 453 return STP_hop_2_state (this, ENABLED); 454 } 455 if (port->rcvdBpdu) { 456 return STP_hop_2_state (this, DISABLED); 457 } 458 break; 459 case ENABLED: /* IEEE 802.1y, 17.21, Z.14 */ 460 return STP_hop_2_state (this, AGED); 461 case AGED: 462 if (port->selected && port->updtInfo) { 463 return STP_hop_2_state (this, UPDATE); 464 } 465 break; 466 case UPDATE: 467 return STP_hop_2_state (this, CURRENT); 468 case CURRENT: 469 if (port->selected && port->updtInfo) { 470 return STP_hop_2_state (this, UPDATE); 471 } 472 473 if (Received == port->infoIs && 474 ! port->rcvdInfoWhile && 475 ! port->updtInfo && 476 ! port->rcvdBpdu) { 477 return STP_hop_2_state (this, AGED); 478 } 479 if (port->rcvdBpdu && !port->updtInfo) { 480 return STP_hop_2_state (this, RECEIVE); 481 } 482 break; 483 case RECEIVE: 484 switch (port->rcvdMsg) { 485 case SuperiorDesignateMsg: 486 return STP_hop_2_state (this, SUPERIOR); 487 case RepeatedDesignateMsg: 488 return STP_hop_2_state (this, REPEAT); 489 case ConfirmedRootMsg: 490 return STP_hop_2_state (this, AGREEMENT); 491 default: 492 return STP_hop_2_state (this, CURRENT); 493 } 494 case SUPERIOR: 495 return STP_hop_2_state (this, CURRENT); 496 case REPEAT: 497 return STP_hop_2_state (this, CURRENT); 498 case AGREEMENT: 499 return STP_hop_2_state (this, CURRENT); 500 } 501 502 return False; 503 } 504