/************************************************************************
* 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?";
}