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 /* Port Role Selection state machine : 17.22 */ 24 25 #include "base.h" 26 #include "stpm.h" 27 #include "stp_vectors.h" 28 29 #define STATES { \ 30 CHOOSE(INIT_BRIDGE), \ 31 CHOOSE(ROLE_SELECTION) \ 32 } 33 34 #define GET_STATE_NAME STP_rolesel_get_state_name 35 #include "choose.h" 36 37 #if 0 38 void stp_dbg_break_point (PORT_T * port, STPM_T* stpm) 39 { 40 } 41 #endif 42 43 static Bool 44 _is_backup_port (PORT_T* port, STPM_T* this) 45 { 46 if (!STP_VECT_compare_bridge_id 47 (&port->portPrio.design_bridge, &this->BrId)) { 48 #if 0 /* def STP_DBG */ 49 if (port->info->debug) { 50 STP_VECT_br_id_print ("portPrio.design_bridge", 51 &port->portPrio.design_bridge, True); 52 STP_VECT_br_id_print (" this->BrId", 53 &this->BrId, True); 54 } 55 stp_dbg_break_point (port, this); 56 #endif 57 return True; 58 } else { 59 return False; 60 } 61 } 62 63 /* ARGSUSED */ 64 static void 65 setRoleSelected (char* reason, STPM_T* stpm, PORT_T* port, 66 PORT_ROLE_T newRole) 67 { 68 #ifdef STP_DBG 69 char* new_role_name; 70 #endif 71 72 port->selectedRole = newRole; 73 74 if (newRole == port->role) 75 return; 76 77 switch (newRole) { 78 case DisabledPort: 79 #ifdef STP_DBG 80 new_role_name = "Disabled"; 81 #endif 82 break; 83 case AlternatePort: 84 #ifdef STP_DBG 85 new_role_name = "Alternate"; 86 #endif 87 break; 88 case BackupPort: 89 #ifdef STP_DBG 90 new_role_name = "Backup"; 91 #endif 92 break; 93 case RootPort: 94 #ifdef STP_DBG 95 new_role_name = "Root"; 96 #endif 97 break; 98 case DesignatedPort: 99 #ifdef STP_DBG 100 new_role_name = "Designated"; 101 #endif 102 break; 103 case NonStpPort: 104 #ifdef STP_DBG 105 new_role_name = "NonStp"; 106 #endif 107 port->role = newRole; 108 break; 109 default: 110 #ifdef STP_DBG 111 stp_trace ("%s-%s:port %s => Unknown (%d ?)", 112 reason, stpm->name, port->port_name, (int) newRole); 113 #else 114 abort(); 115 #endif 116 return; 117 } 118 119 #ifdef STP_DBG 120 if (port->roletrns->debug) 121 stp_trace ("%s(%s-%s) => %s", 122 reason, stpm->name, port->port_name, new_role_name); 123 #endif 124 } 125 126 static void 127 updtRoleDisableBridge (STPM_T* this) 128 { /* 17.10.20 */ 129 register PORT_T *port; 130 131 for (port = this->ports; port; port = port->next) { 132 port->selectedRole = DisabledPort; 133 } 134 } 135 136 static void 137 clearReselectBridge (STPM_T* this) 138 { /* 17.19.1 */ 139 register PORT_T *port; 140 141 for (port = this->ports; port; port = port->next) { 142 port->reselect = False; 143 } 144 } 145 146 static void 147 updtRootPrio (STATE_MACH_T* this) 148 { 149 PRIO_VECTOR_T rootPathPrio; /* 17.4.2.2 */ 150 register PORT_T *port; 151 register STPM_T *stpm; 152 register unsigned int dm; 153 154 stpm = this->owner.stpm; 155 156 for (port = stpm->ports; port; port = port->next) { 157 if (port->admin_non_stp) { 158 continue; 159 } 160 161 if (Disabled == port->infoIs) 162 continue; 163 if (Aged == port->infoIs) 164 continue; 165 if (Mine == port->infoIs) { 166 #if 0 /* def STP_DBG */ 167 stp_dbg_break_point (port); /* for debugger break point */ 168 #endif 169 continue; 170 } 171 172 STP_VECT_copy (&rootPathPrio, &port->portPrio); 173 rootPathPrio.root_path_cost += port->operPCost; 174 175 if (STP_VECT_compare_vector (&rootPathPrio, &stpm->rootPrio) < 0) { 176 STP_VECT_copy (&stpm->rootPrio, &rootPathPrio); 177 STP_copy_times (&stpm->rootTimes, &port->portTimes); 178 dm = (8 + stpm->rootTimes.MaxAge) / 16; 179 if (!dm) 180 dm = 1; 181 stpm->rootTimes.MessageAge += dm; 182 #ifdef STP_DBG 183 if (port->roletrns->debug) 184 stp_trace ("updtRootPrio: dm=%d rootTimes.MessageAge=%d on port %s", 185 (int) dm, (int) stpm->rootTimes.MessageAge, 186 port->port_name); 187 #endif 188 } 189 } 190 } 191 192 static void 193 updtRolesBridge (STATE_MACH_T* this) 194 { /* 17.19.21 */ 195 register PORT_T* port; 196 register STPM_T* stpm; 197 #ifdef STP_DBG 198 PORT_ID old_root_port; /* for tracing of root port changing */ 199 #endif 200 201 stpm = this->owner.stpm; 202 #ifdef STP_DBG 203 old_root_port = stpm->rootPortId; 204 #endif 205 206 STP_VECT_create (&stpm->rootPrio, &stpm->BrId, 0, &stpm->BrId, 0, 0); 207 STP_copy_times (&stpm->rootTimes, &stpm->BrTimes); 208 stpm->rootPortId = 0; 209 210 updtRootPrio (this); 211 212 for (port = stpm->ports; port; port = port->next) { 213 if (port->admin_non_stp) { 214 continue; 215 } 216 STP_VECT_create (&port->designPrio, 217 &stpm->rootPrio.root_bridge, 218 stpm->rootPrio.root_path_cost, 219 &stpm->BrId, port->port_id, port->port_id); 220 STP_copy_times (&port->designTimes, &stpm->rootTimes); 221 222 #if 0 223 #ifdef STP_DBG 224 if (port->roletrns->debug) { 225 STP_VECT_br_id_print ("ch:designPrio.design_bridge", 226 &port->designPrio.design_bridge, True); 227 } 228 #endif 229 #endif 230 } 231 232 stpm->rootPortId = stpm->rootPrio.bridge_port; 233 234 #ifdef STP_DBG 235 if (old_root_port != stpm->rootPortId) { 236 if (! stpm->rootPortId) { 237 stp_trace ("bridge %s became root", stpm->name); 238 } else { 239 stp_trace ("bridge %s new root port: %s", 240 stpm->name, 241 STP_stpm_get_port_name_by_id (stpm, stpm->rootPortId)); 242 } 243 } 244 #endif 245 246 for (port = stpm->ports; port; port = port->next) { 247 if (port->admin_non_stp) { 248 setRoleSelected ("Non", stpm, port, NonStpPort); 249 port->forward = port->learn = True; 250 continue; 251 } 252 253 switch (port->infoIs) { 254 case Disabled: 255 setRoleSelected ("Dis", stpm, port, DisabledPort); 256 break; 257 case Aged: 258 setRoleSelected ("Age", stpm, port, DesignatedPort); 259 port->updtInfo = True; 260 break; 261 case Mine: 262 setRoleSelected ("Mine", stpm, port, DesignatedPort); 263 if (0 != STP_VECT_compare_vector (&port->portPrio, 264 &port->designPrio) || 265 0 != STP_compare_times (&port->portTimes, 266 &port->designTimes)) { 267 port->updtInfo = True; 268 } 269 break; 270 case Received: 271 if (stpm->rootPortId == port->port_id) { 272 setRoleSelected ("Rec", stpm, port, RootPort); 273 } else if (STP_VECT_compare_vector (&port->designPrio, &port->portPrio) < 0) { 274 /* Note: this important piece has been inserted after 275 * discussion with Mick Sieman and reading 802.1y Z1 */ 276 setRoleSelected ("Rec", stpm, port, DesignatedPort); 277 port->updtInfo = True; 278 break; 279 } else { 280 if (_is_backup_port (port, stpm)) { 281 setRoleSelected ("rec", stpm, port, BackupPort); 282 } else { 283 setRoleSelected ("rec", stpm, port, AlternatePort); 284 } 285 } 286 port->updtInfo = False; 287 break; 288 default: 289 stp_trace ("undef infoIs=%d", (int) port->infoIs); 290 break; 291 } 292 } 293 294 } 295 296 297 static Bool 298 setSelectedBridge (STPM_T* this) 299 { 300 register PORT_T* port; 301 302 for (port = this->ports; port; port = port->next) { 303 if (port->reselect) { 304 #ifdef STP_DBG 305 stp_trace ("setSelectedBridge: TRUE=reselect on port %s", port->port_name); 306 #endif 307 return False; 308 } 309 } 310 311 for (port = this->ports; port; port = port->next) { 312 port->selected = True; 313 } 314 315 return True; 316 } 317 318 void 319 STP_rolesel_enter_state (STATE_MACH_T* this) 320 { 321 STPM_T* stpm; 322 323 stpm = this->owner.stpm; 324 325 switch (this->State) { 326 case BEGIN: 327 case INIT_BRIDGE: 328 updtRoleDisableBridge (stpm); 329 break; 330 case ROLE_SELECTION: 331 clearReselectBridge (stpm); 332 updtRolesBridge (this); 333 (void) setSelectedBridge (stpm); 334 break; 335 } 336 } 337 338 Bool 339 STP_rolesel_check_conditions (STATE_MACH_T* s) 340 { 341 STPM_T* stpm; 342 register PORT_T* port; 343 344 /* 345 * This doesn't look right. Why should we hop state twice in a single check 346 * condition call? It means we can never perform the enter-state action for 347 * INIT_BRIDGE. 348 */ 349 #ifdef carlsonj_removed 350 if (BEGIN == s->State) { 351 (void) STP_hop_2_state (s, INIT_BRIDGE); 352 } 353 #endif 354 355 switch (s->State) { 356 case BEGIN: 357 return STP_hop_2_state (s, INIT_BRIDGE); 358 case INIT_BRIDGE: 359 return STP_hop_2_state (s, ROLE_SELECTION); 360 case ROLE_SELECTION: 361 stpm = s->owner.stpm; 362 for (port = stpm->ports; port; port = port->next) { 363 if (port->reselect) { 364 /* stp_trace ("reselect on port %s", port->port_name); */ 365 return STP_hop_2_state (s, ROLE_SELECTION); 366 } 367 } 368 break; 369 } 370 371 return False; 372 } 373 374 void 375 STP_rolesel_update_stpm (STPM_T* this) 376 { 377 register PORT_T* port; 378 PRIO_VECTOR_T rootPathPrio; /* 17.4.2.2 */ 379 380 stp_trace ("%s", "??? STP_rolesel_update_stpm ???"); 381 STP_VECT_create (&rootPathPrio, &this->BrId, 0, &this->BrId, 0, 0); 382 383 if (!this->rootPortId || 384 STP_VECT_compare_vector (&rootPathPrio, &this->rootPrio) < 0) { 385 STP_VECT_copy (&this->rootPrio, &rootPathPrio); 386 } 387 388 for (port = this->ports; port; port = port->next) { 389 STP_VECT_create (&port->designPrio, 390 &this->rootPrio.root_bridge, 391 this->rootPrio.root_path_cost, 392 &this->BrId, port->port_id, port->port_id); 393 if (Received != port->infoIs || this->rootPortId == port->port_id) { 394 STP_VECT_copy (&port->portPrio, &port->designPrio); 395 } 396 port->reselect = True; 397 port->selected = False; 398 } 399 } 400 401