OpenSolaris_b135/lib/librstp/common/stpm.c

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