OpenSolaris_b135/lib/librstp/common/rolesel.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. 
 **********************************************************************/

/* Port Role Selection state machine : 17.22 */

#include "base.h"
#include "stpm.h"
#include "stp_vectors.h"

#define STATES { \
  CHOOSE(INIT_BRIDGE),      \
  CHOOSE(ROLE_SELECTION)   \
}

#define GET_STATE_NAME STP_rolesel_get_state_name
#include "choose.h"

#if 0
void stp_dbg_break_point (PORT_T * port, STPM_T* stpm)
{
}
#endif

static Bool
_is_backup_port (PORT_T* port, STPM_T* this)
{
  if (!STP_VECT_compare_bridge_id
      (&port->portPrio.design_bridge, &this->BrId)) {
#if 0 /* def STP_DBG */
    if (port->info->debug) {
      STP_VECT_br_id_print ("portPrio.design_bridge",
                            &port->portPrio.design_bridge, True);
      STP_VECT_br_id_print ("            this->BrId",
                            &this->BrId, True);
    }
    stp_dbg_break_point (port, this);
#endif
    return True;
  } else {
    return False;
  }
}

/* ARGSUSED */
static void
setRoleSelected (char* reason, STPM_T* stpm, PORT_T* port,
                PORT_ROLE_T newRole)
{
#ifdef STP_DBG
  char* new_role_name;
#endif

  port->selectedRole = newRole;

  if (newRole == port->role)
    return;

  switch (newRole) {
    case DisabledPort:
#ifdef STP_DBG
      new_role_name = "Disabled";
#endif
      break;
    case AlternatePort:
#ifdef STP_DBG
      new_role_name = "Alternate";
#endif
      break;
    case BackupPort:
#ifdef STP_DBG
      new_role_name = "Backup";
#endif
      break;
    case RootPort:
#ifdef STP_DBG
      new_role_name = "Root";
#endif
      break;
    case DesignatedPort:
#ifdef STP_DBG
      new_role_name = "Designated";
#endif
      break;
    case NonStpPort:
#ifdef STP_DBG
      new_role_name = "NonStp";
#endif
      port->role = newRole;
      break;
    default:
#ifdef STP_DBG
      stp_trace ("%s-%s:port %s => Unknown (%d ?)",
                 reason, stpm->name, port->port_name, (int) newRole);
#else
      abort();
#endif
      return;
  }

#ifdef STP_DBG
  if (port->roletrns->debug)
    stp_trace ("%s(%s-%s) => %s",
               reason, stpm->name, port->port_name, new_role_name);
#endif
}

static void
updtRoleDisableBridge (STPM_T* this)
{               /* 17.10.20 */
  register PORT_T *port;

  for (port = this->ports; port; port = port->next) {
    port->selectedRole = DisabledPort;
  }
}

static void
clearReselectBridge (STPM_T* this)
{               /* 17.19.1 */
  register PORT_T *port;

  for (port = this->ports; port; port = port->next) {
    port->reselect = False;
  }
}

static void
updtRootPrio (STATE_MACH_T* this)
{
  PRIO_VECTOR_T rootPathPrio;   /* 17.4.2.2 */
  register PORT_T *port;
  register STPM_T *stpm;
  register unsigned int dm;

  stpm = this->owner.stpm;

  for (port = stpm->ports; port; port = port->next) {
    if (port->admin_non_stp) {
      continue;
    }

    if (Disabled == port->infoIs)
      continue;
    if (Aged == port->infoIs)
      continue;
    if (Mine == port->infoIs) {
#if 0 /* def STP_DBG */
      stp_dbg_break_point (port); /* for debugger break point */
#endif
      continue;
    }

    STP_VECT_copy (&rootPathPrio, &port->portPrio);
    rootPathPrio.root_path_cost += port->operPCost;

    if (STP_VECT_compare_vector (&rootPathPrio, &stpm->rootPrio) < 0) {
      STP_VECT_copy (&stpm->rootPrio, &rootPathPrio);
      STP_copy_times (&stpm->rootTimes, &port->portTimes);
      dm = (8 +  stpm->rootTimes.MaxAge) / 16;
      if (!dm)
        dm = 1;
      stpm->rootTimes.MessageAge += dm;
#ifdef STP_DBG
      if (port->roletrns->debug)
          stp_trace ("updtRootPrio: dm=%d rootTimes.MessageAge=%d on port %s",
                 (int) dm, (int) stpm->rootTimes.MessageAge,
                 port->port_name);
#endif
    }
  }
}

static void
updtRolesBridge (STATE_MACH_T* this)
{               /* 17.19.21 */
  register PORT_T* port;
  register STPM_T* stpm;
#ifdef STP_DBG
  PORT_ID old_root_port; /* for tracing of root port changing */
#endif

  stpm = this->owner.stpm;
#ifdef STP_DBG
  old_root_port = stpm->rootPortId;
#endif

  STP_VECT_create (&stpm->rootPrio, &stpm->BrId, 0, &stpm->BrId, 0, 0);
  STP_copy_times (&stpm->rootTimes, &stpm->BrTimes);
  stpm->rootPortId = 0;

  updtRootPrio (this);

  for (port = stpm->ports; port; port = port->next) {
    if (port->admin_non_stp) {
      continue;
    }
    STP_VECT_create (&port->designPrio,
             &stpm->rootPrio.root_bridge,
             stpm->rootPrio.root_path_cost,
             &stpm->BrId, port->port_id, port->port_id);
    STP_copy_times (&port->designTimes, &stpm->rootTimes);

#if 0
#ifdef STP_DBG
    if (port->roletrns->debug) {
      STP_VECT_br_id_print ("ch:designPrio.design_bridge",
                            &port->designPrio.design_bridge, True);
    }
#endif
#endif
  }

  stpm->rootPortId = stpm->rootPrio.bridge_port;

#ifdef STP_DBG
  if (old_root_port != stpm->rootPortId) {
    if (! stpm->rootPortId) {
      stp_trace ("bridge %s became root", stpm->name);
    } else {
      stp_trace ("bridge %s new root port: %s",
        stpm->name,
        STP_stpm_get_port_name_by_id (stpm, stpm->rootPortId));
    }
  }
#endif

  for (port = stpm->ports; port; port = port->next) {
    if (port->admin_non_stp) {
      setRoleSelected ("Non", stpm, port, NonStpPort);
      port->forward = port->learn = True;
      continue;
    }

    switch (port->infoIs) {
      case Disabled:
        setRoleSelected ("Dis", stpm, port, DisabledPort);
        break;
      case Aged:
        setRoleSelected ("Age", stpm, port, DesignatedPort);
        port->updtInfo = True;
        break;
      case Mine:
        setRoleSelected ("Mine", stpm, port, DesignatedPort);
        if (0 != STP_VECT_compare_vector (&port->portPrio,
                      &port->designPrio) ||
            0 != STP_compare_times (&port->portTimes,
                  &port->designTimes)) {
            port->updtInfo = True;
        }
        break;
      case Received:
        if (stpm->rootPortId == port->port_id) {
          setRoleSelected ("Rec", stpm, port, RootPort);
        } else if (STP_VECT_compare_vector (&port->designPrio, &port->portPrio) < 0) {
          /* Note: this important piece has been inserted after
           * discussion with Mick Sieman and reading 802.1y Z1 */
          setRoleSelected ("Rec", stpm, port, DesignatedPort);
          port->updtInfo = True;
          break;
        } else {
          if (_is_backup_port (port, stpm)) {
            setRoleSelected ("rec", stpm, port, BackupPort);
          } else {
            setRoleSelected ("rec", stpm, port, AlternatePort);
          }
        }
        port->updtInfo = False;
        break;
      default:
        stp_trace ("undef infoIs=%d", (int) port->infoIs);
        break;
    }
  }

}


static Bool
setSelectedBridge (STPM_T* this)
{
  register PORT_T* port;

  for (port = this->ports; port; port = port->next) {
    if (port->reselect) {
#ifdef STP_DBG
      stp_trace ("setSelectedBridge: TRUE=reselect on port %s", port->port_name);
#endif
      return False;
    }
  }

  for (port = this->ports; port; port = port->next) {
    port->selected = True;
  }

  return True;
}

void
STP_rolesel_enter_state (STATE_MACH_T* this)
{
  STPM_T* stpm;

  stpm = this->owner.stpm;

  switch (this->State) {
    case BEGIN:
    case INIT_BRIDGE:
      updtRoleDisableBridge (stpm);
      break;
    case ROLE_SELECTION:
      clearReselectBridge (stpm);
      updtRolesBridge (this);
      (void) setSelectedBridge (stpm);
      break;
  }
}

Bool
STP_rolesel_check_conditions (STATE_MACH_T* s)
{
  STPM_T* stpm;
  register PORT_T* port;

  /*
   * This doesn't look right.  Why should we hop state twice in a single check
   * condition call?  It means we can never perform the enter-state action for
   * INIT_BRIDGE.
   */
#ifdef carlsonj_removed
  if (BEGIN == s->State) {
    (void) STP_hop_2_state (s, INIT_BRIDGE);
  }
#endif

  switch (s->State) {
    case BEGIN:
      return STP_hop_2_state (s, INIT_BRIDGE);
    case INIT_BRIDGE:
      return STP_hop_2_state (s, ROLE_SELECTION);
    case ROLE_SELECTION:
      stpm = s->owner.stpm;
      for (port = stpm->ports; port; port = port->next) {
        if (port->reselect) {
          /* stp_trace ("reselect on port %s", port->port_name); */
          return STP_hop_2_state (s, ROLE_SELECTION);
        }
      }
      break;
  }

  return False;
}

void
STP_rolesel_update_stpm (STPM_T* this)
{
  register PORT_T* port;
  PRIO_VECTOR_T rootPathPrio;   /* 17.4.2.2 */

  stp_trace ("%s", "??? STP_rolesel_update_stpm ???");
  STP_VECT_create (&rootPathPrio, &this->BrId, 0, &this->BrId, 0, 0);

  if (!this->rootPortId ||
      STP_VECT_compare_vector (&rootPathPrio, &this->rootPrio) < 0) {
    STP_VECT_copy (&this->rootPrio, &rootPathPrio);
  }

  for (port = this->ports; port; port = port->next) {
    STP_VECT_create (&port->designPrio,
             &this->rootPrio.root_bridge,
             this->rootPrio.root_path_cost,
             &this->BrId, port->port_id, port->port_id);
    if (Received != port->infoIs || this->rootPortId == port->port_id) {
      STP_VECT_copy (&port->portPrio, &port->designPrio);
    }
    port->reselect = True;
    port->selected = False;
  }
}