/************************************************************************
* 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.
**********************************************************************/
/* Port Role Transitions state machine : 17.24 */
#include "base.h"
#include "stpm.h"
#define STATES { \
CHOOSE(INIT_PORT), \
CHOOSE(BLOCK_PORT), \
CHOOSE(BLOCKED_PORT), \
CHOOSE(BACKUP_PORT), \
CHOOSE(ROOT_PROPOSED), \
CHOOSE(ROOT_AGREED), \
CHOOSE(REROOT), \
CHOOSE(ROOT_PORT), \
CHOOSE(REROOTED), \
CHOOSE(ROOT_LEARN), \
CHOOSE(ROOT_FORWARD), \
CHOOSE(DESIGNATED_PROPOSE), \
CHOOSE(DESIGNATED_SYNCED), \
CHOOSE(DESIGNATED_RETIRED), \
CHOOSE(DESIGNATED_PORT), \
CHOOSE(DESIGNATED_LISTEN), \
CHOOSE(DESIGNATED_LEARN), \
CHOOSE(DESIGNATED_FORWARD) \
}
#define GET_STATE_NAME STP_roletrns_get_state_name
#include "choose.h"
static void
setSyncBridge (STATE_MACH_T *this)
{
register PORT_T* port;
for (port = this->owner.port->owner->ports; port; port = port->next) {
port->sync = True; /* in ROOT_PROPOSED (setSyncBridge) */
}
}
static void
setReRootBridge (STATE_MACH_T *this)
{
register PORT_T* port;
for (port = this->owner.port->owner->ports; port; port = port->next) {
port->reRoot = True; /* In setReRootBridge */
}
}
static Bool
compute_all_synced (PORT_T* this)
{
register PORT_T* port;
for (port = this->owner->ports; port; port = port->next) {
if (port->port_index == this->port_index) continue;
if (! port->synced) {
return False;
}
}
return True;
}
static Bool
compute_re_rooted (PORT_T* this)
{
register PORT_T* port;
for (port = this->owner->ports; port; port = port->next) {
if (port->port_index == this->port_index) continue;
if (port->rrWhile) {
return False;
}
}
return True;
}
void
STP_roletrns_enter_state (STATE_MACH_T* this)
{
register PORT_T* port = this->owner.port;
register STPM_T* stpm;
stpm = port->owner;
switch (this->State) {
case BEGIN:
case INIT_PORT:
#if 0 /* due 802.1y Z.4 */
port->role = DisabledPort;
#else
port->role = port->selectedRole = DisabledPort;
port->reselect = True;
#endif
port->synced = False; /* in INIT */
port->sync = True; /* in INIT */
port->reRoot = True; /* in INIT_PORT */
port->rrWhile = stpm->rootTimes.ForwardDelay;
port->fdWhile = stpm->rootTimes.ForwardDelay;
port->rbWhile = 0;
#ifdef STP_DBG
if (this->debug)
STP_port_trace_flags ("after init", port);
#endif
break;
case BLOCK_PORT:
port->role = port->selectedRole;
port->learn =
port->forward = False;
break;
case BLOCKED_PORT:
port->fdWhile = stpm->rootTimes.ForwardDelay;
port->synced = True; /* In BLOCKED_PORT */
port->rrWhile = 0;
port->sync = port->reRoot = False; /* BLOCKED_PORT */
break;
case BACKUP_PORT:
port->rbWhile = 2 * stpm->rootTimes.HelloTime;
break;
/* 17.23.2 */
case ROOT_PROPOSED:
setSyncBridge (this);
port->proposed = False;
#ifdef STP_DBG
if (this->debug)
STP_port_trace_flags ("ROOT_PROPOSED", port);
#endif
break;
case ROOT_AGREED:
port->proposed = port->sync = False; /* in ROOT_AGREED */
port->synced = True; /* In ROOT_AGREED */
port->newInfo = True;
#ifdef STP_DBG
if (this->debug)
STP_port_trace_flags ("ROOT_AGREED", port);
#endif
break;
case REROOT:
setReRootBridge (this);
#ifdef STP_DBG
if (this->debug)
STP_port_trace_flags ("REROOT", port);
#endif
break;
case ROOT_PORT:
port->role = RootPort;
port->rrWhile = stpm->rootTimes.ForwardDelay;
#ifdef STP_DBG
if (this->debug)
STP_port_trace_flags ("ROOT_PORT", port);
#endif
break;
case REROOTED:
port->reRoot = False; /* In REROOTED */
#ifdef STP_DBG
if (this->debug)
STP_port_trace_flags ("REROOTED", port);
#endif
break;
case ROOT_LEARN:
port->fdWhile = stpm->rootTimes.ForwardDelay;
port->learn = True;
#ifdef STP_DBG
if (this->debug)
STP_port_trace_flags ("ROOT_LEARN", port);
#endif
break;
case ROOT_FORWARD:
port->fdWhile = 0;
port->forward = True;
#ifdef STP_DBG
if (this->debug)
STP_port_trace_flags ("ROOT_FORWARD", port);
#endif
break;
/* 17.23.3 */
case DESIGNATED_PROPOSE:
port->proposing = True; /* in DESIGNATED_PROPOSE */
port->newInfo = True;
#ifdef STP_DBG
if (this->debug)
STP_port_trace_flags ("DESIGNATED_PROPOSE", port);
#endif
break;
case DESIGNATED_SYNCED:
port->rrWhile = 0;
port->synced = True; /* DESIGNATED_SYNCED */
port->sync = False; /* DESIGNATED_SYNCED */
#ifdef STP_DBG
if (this->debug)
STP_port_trace_flags ("DESIGNATED_SYNCED", port);
#endif
break;
case DESIGNATED_RETIRED:
port->reRoot = False; /* DESIGNATED_RETIRED */
#ifdef STP_DBG
if (this->debug)
STP_port_trace_flags ("DESIGNATED_RETIRED", port);
#endif
break;
case DESIGNATED_PORT:
port->role = DesignatedPort;
#ifdef STP_DBG
if (this->debug)
STP_port_trace_flags ("DESIGNATED_PORT", port);
#endif
break;
case DESIGNATED_LISTEN:
port->learn = port->forward = False;
port->fdWhile = stpm->rootTimes.ForwardDelay;
#ifdef STP_DBG
if (this->debug)
STP_port_trace_flags ("DESIGNATED_LISTEN", port);
#endif
break;
case DESIGNATED_LEARN:
port->learn = True;
port->fdWhile = stpm->rootTimes.ForwardDelay;
#ifdef STP_DBG
if (this->debug)
STP_port_trace_flags ("DESIGNATED_LEARN", port);
#endif
break;
case DESIGNATED_FORWARD:
port->forward = True;
port->fdWhile = 0;
#ifdef STP_DBG
if (this->debug)
STP_port_trace_flags ("DESIGNATED_FORWARD", port);
#endif
break;
};
}
Bool
STP_roletrns_check_conditions (STATE_MACH_T* this)
{
register PORT_T *port = this->owner.port;
register STPM_T *stpm;
Bool allSynced;
Bool allReRooted;
stpm = port->owner;
if (BEGIN == this->State) {
return STP_hop_2_state (this, INIT_PORT);
}
if (port->role != port->selectedRole &&
port->selected &&
! port->updtInfo) {
switch (port->selectedRole) {
case DisabledPort:
case AlternatePort:
case BackupPort:
#if 0 /* def STP_DBG */
if (this->debug) {
stp_trace ("hop to BLOCK_PORT role=%d selectedRole=%d",
(int) port->role, (int) port->selectedRole);
}
#endif
return STP_hop_2_state (this, BLOCK_PORT);
case RootPort:
return STP_hop_2_state (this, ROOT_PORT);
case DesignatedPort:
return STP_hop_2_state (this, DESIGNATED_PORT);
default:
return False;
}
}
switch (this->State) {
/* 17.23.1 */
case INIT_PORT:
return STP_hop_2_state (this, BLOCK_PORT);
case BLOCK_PORT:
if (!port->selected || port->updtInfo) break;
if (!port->learning && !port->forwarding) {
return STP_hop_2_state (this, BLOCKED_PORT);
}
break;
case BLOCKED_PORT:
if (!port->selected || port->updtInfo) break;
if (port->fdWhile != stpm->rootTimes.ForwardDelay ||
port->sync ||
port->reRoot ||
!port->synced) {
return STP_hop_2_state (this, BLOCKED_PORT);
}
if (port->rbWhile != 2 * stpm->rootTimes.HelloTime &&
port->role == BackupPort) {
return STP_hop_2_state (this, BACKUP_PORT);
}
break;
case BACKUP_PORT:
return STP_hop_2_state (this, BLOCKED_PORT);
/* 17.23.2 */
case ROOT_PROPOSED:
return STP_hop_2_state (this, ROOT_PORT);
case ROOT_AGREED:
return STP_hop_2_state (this, ROOT_PORT);
case REROOT:
return STP_hop_2_state (this, ROOT_PORT);
case ROOT_PORT:
if (!port->selected || port->updtInfo) break;
if (!port->forward && !port->reRoot) {
return STP_hop_2_state (this, REROOT);
}
allSynced = compute_all_synced (port);
if ((port->proposed && allSynced) ||
(!port->synced && allSynced)) {
return STP_hop_2_state (this, ROOT_AGREED);
}
if (port->proposed && !port->synced) {
return STP_hop_2_state (this, ROOT_PROPOSED);
}
allReRooted = compute_re_rooted (port);
if ((!port->fdWhile ||
((allReRooted && !port->rbWhile) && stpm->ForceVersion >=2)) &&
port->learn && !port->forward) {
return STP_hop_2_state (this, ROOT_FORWARD);
}
if ((!port->fdWhile ||
((allReRooted && !port->rbWhile) && stpm->ForceVersion >=2)) &&
!port->learn) {
return STP_hop_2_state (this, ROOT_LEARN);
}
if (port->reRoot && port->forward) {
return STP_hop_2_state (this, REROOTED);
}
if (port->rrWhile != stpm->rootTimes.ForwardDelay) {
return STP_hop_2_state (this, ROOT_PORT);
}
break;
case REROOTED:
return STP_hop_2_state (this, ROOT_PORT);
case ROOT_LEARN:
return STP_hop_2_state (this, ROOT_PORT);
case ROOT_FORWARD:
return STP_hop_2_state (this, ROOT_PORT);
/* 17.23.3 */
case DESIGNATED_PROPOSE:
return STP_hop_2_state (this, DESIGNATED_PORT);
case DESIGNATED_SYNCED:
return STP_hop_2_state (this, DESIGNATED_PORT);
case DESIGNATED_RETIRED:
return STP_hop_2_state (this, DESIGNATED_PORT);
case DESIGNATED_PORT:
if (!port->selected || port->updtInfo) break;
if (!port->forward && !port->agreed && !port->proposing && !port->operEdge) {
return STP_hop_2_state (this, DESIGNATED_PROPOSE);
}
if (!port->rrWhile && port->reRoot) {
return STP_hop_2_state (this, DESIGNATED_RETIRED);
}
if (!port->learning && !port->forwarding && !port->synced) {
return STP_hop_2_state (this, DESIGNATED_SYNCED);
}
if (port->agreed && !port->synced) {
return STP_hop_2_state (this, DESIGNATED_SYNCED);
}
if (port->operEdge && !port->synced) {
return STP_hop_2_state (this, DESIGNATED_SYNCED);
}
if (port->sync && port->synced) {
return STP_hop_2_state (this, DESIGNATED_SYNCED);
}
if ((!port->fdWhile || port->agreed || port->operEdge) &&
(!port->rrWhile || !port->reRoot) &&
!port->sync &&
(port->learn && !port->forward)) {
return STP_hop_2_state (this, DESIGNATED_FORWARD);
}
if ((!port->fdWhile || port->agreed || port->operEdge) &&
(!port->rrWhile || !port->reRoot) &&
!port->sync && !port->learn) {
return STP_hop_2_state (this, DESIGNATED_LEARN);
}
if (((port->sync && !port->synced) ||
(port->reRoot && port->rrWhile)) &&
!port->operEdge && (port->learn || port->forward)) {
return STP_hop_2_state (this, DESIGNATED_LISTEN);
}
break;
case DESIGNATED_LISTEN:
return STP_hop_2_state (this, DESIGNATED_PORT);
case DESIGNATED_LEARN:
return STP_hop_2_state (this, DESIGNATED_PORT);
case DESIGNATED_FORWARD:
return STP_hop_2_state (this, DESIGNATED_PORT);
};
return False;
}