/************************************************************************ * 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. **********************************************************************/ /* Topolgy Change state machine : 17.25 */ #include "base.h" #include "stpm.h" #include "stp_to.h" /* for STP_OUT_flush_lt */ #define STATES { \ CHOOSE(INIT), \ CHOOSE(INACTIVE), \ CHOOSE(TCACTIVE), \ CHOOSE(DETECTED), \ CHOOSE(NOTIFIED_TC), \ CHOOSE(PROPAGATING), \ CHOOSE(ACKNOWLEDGED), \ CHOOSE(NOTIFIED_TCN) \ } #define GET_STATE_NAME STP_topoch_get_state_name #include "choose.h" #ifndef STRONGLY_SPEC_802_1W /* * In many kinds of hardware the function * STP_OUT_flush_lt is a) is very hard and b) cannot * delete learning emtries per port. The alternate * method may be used: we don't care operEdge flag here, * but clean learning table once for TopologyChange * for all ports, except the received port. I am ready to discuss :( * See below word STRONGLY_SPEC_802_1W */ #else static Bool flush (STATE_MACH_T *this, char* reason) /* 17.19.9 */ { register PORT_T* port = this->owner.port; Bool bret; if (port->operEdge) return True; if (this->debug) { stp_trace("%s (%s, %s, %s, '%s')", "flush", port->port_name, port->owner->name, LT_FLASH_ONLY_THE_PORT == type ? "this port" : "other ports", reason); } bret = STP_OUT_flush_lt (port->port_index, port->owner->vlan_id, LT_FLASH_ONLY_THE_PORT, reason); } #endif static void setTcPropBridge (STATE_MACH_T* this, char* reason) /* 17.19.14 */ { register PORT_T* port = this->owner.port; register PORT_T* tmp; for (tmp = port->owner->ports; tmp; tmp = tmp->next) { if (tmp->port_index != port->port_index) tmp->tcProp = True; } #ifndef STRONGLY_SPEC_802_1W #ifdef STP_DBG if (this->debug) { stp_trace("%s (%s, %s, %s, '%s')", "clearFDB", port->port_name, port->owner->name, "other ports", reason); } #endif STP_OUT_flush_lt (port->port_index, port->owner->vlan_id, LT_FLASH_ALL_PORTS_EXCLUDE_THIS, reason); #endif } static unsigned int newTcWhile (STATE_MACH_T* this) /* 17.19.7 */ { register PORT_T* port = this->owner.port; if (port->sendRSTP && port->operPointToPointMac) { return 2 * port->owner->rootTimes.HelloTime; } return port->owner->rootTimes.MaxAge; } void STP_topoch_enter_state (STATE_MACH_T* this) { register PORT_T* port = this->owner.port; switch (this->State) { case BEGIN: case INIT: #ifdef STRONGLY_SPEC_802_1W flush (this, "topoch INIT"); #endif port->tcWhile = 0; port->tc = port->tcProp = port->tcAck = False; break; case INACTIVE: port->rcvdTc = port->rcvdTcn = port->rcvdTcAck = port->tc = port->tcProp = False; break; case TCACTIVE: break; case DETECTED: port->tcWhile = newTcWhile (this); #ifdef STP_DBG if (this->debug) stp_trace("DETECTED: tcWhile=%d on port %s", port->tcWhile, port->port_name); #endif setTcPropBridge (this, "DETECTED"); port->tc = False; break; case NOTIFIED_TC: port->rcvdTcn = port->rcvdTc = False; if (port->role == DesignatedPort) { port->tcAck = True; } setTcPropBridge (this, "NOTIFIED_TC"); break; case PROPAGATING: port->tcWhile = newTcWhile (this); #ifdef STP_DBG if (this->debug) stp_trace("PROPAGATING: tcWhile=%d on port %s", port->tcWhile, port->port_name); #endif #ifdef STRONGLY_SPEC_802_1W flush (this, "topoch PROPAGATING"); #endif port->tcProp = False; break; case ACKNOWLEDGED: port->tcWhile = 0; #ifdef STP_DBG if (this->debug) stp_trace("ACKNOWLEDGED: tcWhile=%d on port %s", port->tcWhile, port->port_name); #endif port->rcvdTcAck = False; break; case NOTIFIED_TCN: port->tcWhile = newTcWhile (this); #ifdef STP_DBG if (this->debug) stp_trace("NOTIFIED_TCN: tcWhile=%d on port %s", port->tcWhile, port->port_name); #endif break; }; } Bool STP_topoch_check_conditions (STATE_MACH_T* this) { register PORT_T* port = this->owner.port; if (BEGIN == this->State) { return STP_hop_2_state (this, INIT); } switch (this->State) { case INIT: return STP_hop_2_state (this, INACTIVE); case INACTIVE: if (port->role == RootPort || port->role == DesignatedPort) return STP_hop_2_state (this, TCACTIVE); if (port->rcvdTc || port->rcvdTcn || port->rcvdTcAck || port->tc || port->tcProp) return STP_hop_2_state (this, INACTIVE); break; case TCACTIVE: if (port->role != RootPort && (port->role != DesignatedPort)) return STP_hop_2_state (this, INIT); if (port->tc) return STP_hop_2_state (this, DETECTED); if (port->rcvdTcn) return STP_hop_2_state (this, NOTIFIED_TCN); if (port->rcvdTc) return STP_hop_2_state (this, NOTIFIED_TC); if (port->tcProp && !port->operEdge) return STP_hop_2_state (this, PROPAGATING); if (port->rcvdTcAck) return STP_hop_2_state (this, ACKNOWLEDGED); break; case DETECTED: return STP_hop_2_state (this, TCACTIVE); case NOTIFIED_TC: return STP_hop_2_state (this, TCACTIVE); case PROPAGATING: return STP_hop_2_state (this, TCACTIVE); case ACKNOWLEDGED: return STP_hop_2_state (this, TCACTIVE); case NOTIFIED_TCN: return STP_hop_2_state (this, NOTIFIED_TC); }; return False; }