/************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* STP machine instance : bridge per VLAN: 17.17 */ #include "base.h" #include "stpm.h" #include "stp_to.h" /* for STP_OUT_flush_lt */ static STPM_T *bridges = NULL; static int _stp_stpm_init_machine (STATE_MACH_T* this) { this->State = BEGIN; (*(this->concreteEnterState)) (this); return 0; } static int _stp_stpm_iterate_machines (STPM_T* this, int (*iter_callb) (STATE_MACH_T*), Bool exit_on_non_zero_ret) { register STATE_MACH_T* stater; register PORT_T* port; int iret, mret = 0; /* state machines per bridge */ for (stater = this->machines; stater; stater = stater->next) { iret = (*iter_callb) (stater); if (exit_on_non_zero_ret && iret) return iret; else mret += iret; } /* state machines per port */ for (port = this->ports; port; port = port->next) { for (stater = port->machines; stater; stater = stater->next) { iret = (*iter_callb) (stater); if (exit_on_non_zero_ret && iret) return iret; else mret += iret; } } return mret; } void _stp_stpm_init_data (STPM_T* this) { STP_VECT_create (&this->rootPrio, &this->BrId, 0, &this->BrId, 0, 0); this->BrTimes.MessageAge = 0; STP_copy_times (&this->rootTimes, &this->BrTimes); } static unsigned char _check_topoch (STPM_T* this) { register PORT_T* port; for (port = this->ports; port; port = port->next) { if (port->tcWhile) { return 1; } } return 0; } void STP_stpm_one_second (STPM_T* param) { STPM_T* this = (STPM_T*) param; register PORT_T* port; register int iii; if (STP_ENABLED != this->admin_state) return; for (port = this->ports; port; port = port->next) { for (iii = 0; iii < TIMERS_NUMBER; iii++) { if (*(port->timers[iii]) > 0) { (*port->timers[iii])--; } } port->uptime++; } (void) STP_stpm_update (this); this->Topo_Change = _check_topoch (this); if (this->Topo_Change) { this->Topo_Change_Count++; this->timeSince_Topo_Change = 0; } else { this->Topo_Change_Count = 0; this->timeSince_Topo_Change++; } } STPM_T* STP_stpm_create (int vlan_id, char* name) { STPM_T* this; STP_NEW_IN_LIST(this, STPM_T, bridges, "stp instance"); this->admin_state = STP_DISABLED; this->vlan_id = vlan_id; if (name) { STP_STRDUP(this->name, name, "stp bridge name"); } this->machines = NULL; this->ports = NULL; STP_STATE_MACH_IN_LIST(rolesel); #ifdef STP_DBG /* this->rolesel->debug = 2; */ #endif return this; } int STP_stpm_enable (STPM_T* this, UID_STP_MODE_T admin_state) { int rc = 0; if (admin_state == this->admin_state) { /* nothing to do :) */ return 0; } if (STP_ENABLED == admin_state) { if (this->ports) rc = STP_stpm_start (this); this->admin_state = admin_state; } else { this->admin_state = admin_state; STP_stpm_stop (this); } return rc; } void STP_stpm_delete (STPM_T* this) { register STPM_T* tmp; register STPM_T* prev; register STATE_MACH_T* stater; register PORT_T* port; register void* pv; (void) STP_stpm_enable (this, STP_DISABLED); for (stater = this->machines; stater; ) { pv = (void*) stater->next; STP_state_mach_delete (stater); this->machines = stater = (STATE_MACH_T*) pv; } for (port = this->ports; port; ) { pv = (void*) port->next; STP_port_delete (port); this->ports = port = (PORT_T*) pv; } prev = NULL; for (tmp = bridges; tmp; tmp = tmp->next) { if (tmp->vlan_id == this->vlan_id) { if (prev) { prev->next = this->next; } else { bridges = this->next; } if (this->name) STP_FREE(this->name, "stp bridge name"); STP_FREE(this, "stp instance"); break; } prev = tmp; } } int STP_stpm_start (STPM_T* this) { register PORT_T* port; if (! this->ports) { /* there are not any ports :( */ return STP_There_Are_No_Ports; } if (! STP_compute_bridge_id (this)) {/* can't compute bridge id ? :( */ return STP_Cannot_Compute_Bridge_Prio; } /* check, that the stpm has unique bridge Id */ if (0 != STP_stpm_check_bridge_priority (this)) { /* there is an enabled bridge with same ID :( */ return STP_Invalid_Bridge_Priority; } _stp_stpm_init_data (this); for (port = this->ports; port; port = port->next) { STP_port_init (port, this, True); } #ifndef STRONGLY_SPEC_802_1W /* A. see comment near STRONGLY_SPEC_802_1W in topoch.c */ /* B. port=0 here means: delete for all ports */ #ifdef STP_DBG stp_trace("%s (all, start stpm)", "clearFDB"); #endif STP_OUT_flush_lt (0, this->vlan_id, LT_FLASH_ONLY_THE_PORT, "start stpm"); #endif (void) _stp_stpm_iterate_machines (this, _stp_stpm_init_machine, False); (void) STP_stpm_update (this); return 0; } /* ARGSUSED */ void STP_stpm_stop (STPM_T* this) { } int STP_stpm_update (STPM_T* this) /* returns number of loops */ { register Bool need_state_change; register int number_of_loops = 0; need_state_change = False; for (;;) {/* loop until not need changes */ need_state_change = _stp_stpm_iterate_machines (this, STP_check_condition, True); if (! need_state_change) break; number_of_loops++; /* here we know, that at least one stater must be updated (it has changed state) */ number_of_loops += _stp_stpm_iterate_machines (this, STP_change_state, False); } return number_of_loops; } BRIDGE_ID * STP_compute_bridge_id (STPM_T* this) { register PORT_T* port; register PORT_T* min_num_port = NULL; int port_index = 0; for (port = this->ports; port; port = port->next) { if (! port_index || port->port_index < port_index) { min_num_port = port; port_index = port->port_index; } } if (! min_num_port) return NULL; /* IMHO, it may not be */ STP_OUT_get_port_mac (min_num_port->port_index, this->BrId.addr); return &this->BrId; } STPM_T* STP_stpm_get_the_list (void) { return bridges; } void STP_stpm_update_after_bridge_management (STPM_T* this) { register PORT_T* port; for (port = this->ports; port; port = port->next) { port->reselect = True; port->selected = False; } } int STP_stpm_check_bridge_priority (STPM_T* this) { register STPM_T* oth; for (oth = bridges; oth; oth = oth->next) { if (STP_ENABLED == oth->admin_state && oth != this && ! STP_VECT_compare_bridge_id (&this->BrId, &oth->BrId)) { return STP_Invalid_Bridge_Priority; } } return 0; } const char* STP_stpm_get_port_name_by_id (STPM_T* this, PORT_ID port_id) { register PORT_T* port; for (port = this->ports; port; port = port->next) { if (port_id == port->port_id) { return port->port_name; } } return "Undef?"; }