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 /* STP machine instance : bridge per VLAN: 17.17 */ 24 25 #include "base.h" 26 #include "stpm.h" 27 #include "stp_to.h" /* for STP_OUT_flush_lt */ 28 29 static STPM_T *bridges = NULL; 30 31 static int 32 _stp_stpm_init_machine (STATE_MACH_T* this) 33 { 34 this->State = BEGIN; 35 (*(this->concreteEnterState)) (this); 36 return 0; 37 } 38 39 static int 40 _stp_stpm_iterate_machines (STPM_T* this, 41 int (*iter_callb) (STATE_MACH_T*), 42 Bool exit_on_non_zero_ret) 43 { 44 register STATE_MACH_T* stater; 45 register PORT_T* port; 46 int iret, mret = 0; 47 48 /* state machines per bridge */ 49 for (stater = this->machines; stater; stater = stater->next) { 50 iret = (*iter_callb) (stater); 51 if (exit_on_non_zero_ret && iret) 52 return iret; 53 else 54 mret += iret; 55 } 56 57 /* state machines per port */ 58 for (port = this->ports; port; port = port->next) { 59 for (stater = port->machines; stater; stater = stater->next) { 60 iret = (*iter_callb) (stater); 61 if (exit_on_non_zero_ret && iret) 62 return iret; 63 else 64 mret += iret; 65 } 66 } 67 68 return mret; 69 } 70 71 void 72 _stp_stpm_init_data (STPM_T* this) 73 { 74 STP_VECT_create (&this->rootPrio, 75 &this->BrId, 76 0, 77 &this->BrId, 78 0, 0); 79 80 this->BrTimes.MessageAge = 0; 81 82 STP_copy_times (&this->rootTimes, &this->BrTimes); 83 } 84 85 static unsigned char 86 _check_topoch (STPM_T* this) 87 { 88 register PORT_T* port; 89 90 for (port = this->ports; port; port = port->next) { 91 if (port->tcWhile) { 92 return 1; 93 } 94 } 95 return 0; 96 } 97 98 void 99 STP_stpm_one_second (STPM_T* param) 100 { 101 STPM_T* this = (STPM_T*) param; 102 register PORT_T* port; 103 register int iii; 104 105 if (STP_ENABLED != this->admin_state) return; 106 107 for (port = this->ports; port; port = port->next) { 108 for (iii = 0; iii < TIMERS_NUMBER; iii++) { 109 if (*(port->timers[iii]) > 0) { 110 (*port->timers[iii])--; 111 } 112 } 113 port->uptime++; 114 } 115 116 (void) STP_stpm_update (this); 117 this->Topo_Change = _check_topoch (this); 118 if (this->Topo_Change) { 119 this->Topo_Change_Count++; 120 this->timeSince_Topo_Change = 0; 121 } else { 122 this->Topo_Change_Count = 0; 123 this->timeSince_Topo_Change++; 124 } 125 } 126 127 STPM_T* 128 STP_stpm_create (int vlan_id, char* name) 129 { 130 STPM_T* this; 131 132 STP_NEW_IN_LIST(this, STPM_T, bridges, "stp instance"); 133 134 this->admin_state = STP_DISABLED; 135 136 this->vlan_id = vlan_id; 137 if (name) { 138 STP_STRDUP(this->name, name, "stp bridge name"); 139 } 140 141 this->machines = NULL; 142 this->ports = NULL; 143 144 STP_STATE_MACH_IN_LIST(rolesel); 145 146 #ifdef STP_DBG 147 /* this->rolesel->debug = 2; */ 148 #endif 149 150 return this; 151 } 152 153 int 154 STP_stpm_enable (STPM_T* this, UID_STP_MODE_T admin_state) 155 { 156 int rc = 0; 157 158 if (admin_state == this->admin_state) { 159 /* nothing to do :) */ 160 return 0; 161 } 162 163 if (STP_ENABLED == admin_state) { 164 if (this->ports) 165 rc = STP_stpm_start (this); 166 this->admin_state = admin_state; 167 } else { 168 this->admin_state = admin_state; 169 STP_stpm_stop (this); 170 } 171 172 return rc; 173 } 174 175 void 176 STP_stpm_delete (STPM_T* this) 177 { 178 register STPM_T* tmp; 179 register STPM_T* prev; 180 register STATE_MACH_T* stater; 181 register PORT_T* port; 182 register void* pv; 183 184 (void) STP_stpm_enable (this, STP_DISABLED); 185 186 for (stater = this->machines; stater; ) { 187 pv = (void*) stater->next; 188 STP_state_mach_delete (stater); 189 this->machines = stater = (STATE_MACH_T*) pv; 190 } 191 192 for (port = this->ports; port; ) { 193 pv = (void*) port->next; 194 STP_port_delete (port); 195 this->ports = port = (PORT_T*) pv; 196 } 197 198 prev = NULL; 199 for (tmp = bridges; tmp; tmp = tmp->next) { 200 if (tmp->vlan_id == this->vlan_id) { 201 if (prev) { 202 prev->next = this->next; 203 } else { 204 bridges = this->next; 205 } 206 207 if (this->name) 208 STP_FREE(this->name, "stp bridge name"); 209 STP_FREE(this, "stp instance"); 210 break; 211 } 212 prev = tmp; 213 } 214 } 215 216 int 217 STP_stpm_start (STPM_T* this) 218 { 219 register PORT_T* port; 220 221 if (! this->ports) { /* there are not any ports :( */ 222 return STP_There_Are_No_Ports; 223 } 224 225 if (! STP_compute_bridge_id (this)) {/* can't compute bridge id ? :( */ 226 return STP_Cannot_Compute_Bridge_Prio; 227 } 228 229 /* check, that the stpm has unique bridge Id */ 230 if (0 != STP_stpm_check_bridge_priority (this)) { 231 /* there is an enabled bridge with same ID :( */ 232 return STP_Invalid_Bridge_Priority; 233 } 234 235 _stp_stpm_init_data (this); 236 237 for (port = this->ports; port; port = port->next) { 238 STP_port_init (port, this, True); 239 } 240 241 #ifndef STRONGLY_SPEC_802_1W 242 /* A. see comment near STRONGLY_SPEC_802_1W in topoch.c */ 243 /* B. port=0 here means: delete for all ports */ 244 #ifdef STP_DBG 245 stp_trace("%s (all, start stpm)", 246 "clearFDB"); 247 #endif 248 249 STP_OUT_flush_lt (0, this->vlan_id, LT_FLASH_ONLY_THE_PORT, "start stpm"); 250 #endif 251 252 (void) _stp_stpm_iterate_machines (this, _stp_stpm_init_machine, False); 253 (void) STP_stpm_update (this); 254 255 return 0; 256 } 257 258 /* ARGSUSED */ 259 void 260 STP_stpm_stop (STPM_T* this) 261 { 262 } 263 264 int 265 STP_stpm_update (STPM_T* this) /* returns number of loops */ 266 { 267 register Bool need_state_change; 268 register int number_of_loops = 0; 269 270 need_state_change = False; 271 272 for (;;) {/* loop until not need changes */ 273 need_state_change = _stp_stpm_iterate_machines (this, 274 STP_check_condition, 275 True); 276 if (! need_state_change) break; 277 278 number_of_loops++; 279 /* here we know, that at least one stater must be 280 updated (it has changed state) */ 281 number_of_loops += _stp_stpm_iterate_machines (this, 282 STP_change_state, 283 False); 284 285 } 286 287 return number_of_loops; 288 } 289 290 BRIDGE_ID * 291 STP_compute_bridge_id (STPM_T* this) 292 { 293 register PORT_T* port; 294 register PORT_T* min_num_port = NULL; 295 int port_index = 0; 296 297 for (port = this->ports; port; port = port->next) { 298 if (! port_index || port->port_index < port_index) { 299 min_num_port = port; 300 port_index = port->port_index; 301 } 302 } 303 304 if (! min_num_port) return NULL; /* IMHO, it may not be */ 305 306 STP_OUT_get_port_mac (min_num_port->port_index, this->BrId.addr); 307 308 return &this->BrId; 309 } 310 311 STPM_T* 312 STP_stpm_get_the_list (void) 313 { 314 return bridges; 315 } 316 317 void 318 STP_stpm_update_after_bridge_management (STPM_T* this) 319 { 320 register PORT_T* port; 321 322 for (port = this->ports; port; port = port->next) { 323 port->reselect = True; 324 port->selected = False; 325 } 326 } 327 328 int 329 STP_stpm_check_bridge_priority (STPM_T* this) 330 { 331 register STPM_T* oth; 332 333 for (oth = bridges; oth; oth = oth->next) { 334 if (STP_ENABLED == oth->admin_state && oth != this && 335 ! STP_VECT_compare_bridge_id (&this->BrId, &oth->BrId)) { 336 return STP_Invalid_Bridge_Priority; 337 } 338 } 339 340 return 0; 341 } 342 343 const char* 344 STP_stpm_get_port_name_by_id (STPM_T* this, PORT_ID port_id) 345 { 346 register PORT_T* port; 347 348 for (port = this->ports; port; port = port->next) { 349 if (port_id == port->port_id) { 350 return port->port_name; 351 } 352 } 353 354 return "Undef?"; 355 } 356 357 358 359 360 361