summaryrefslogtreecommitdiff
path: root/usr/src/lib/librstp/common/rolesel.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/librstp/common/rolesel.c')
-rw-r--r--usr/src/lib/librstp/common/rolesel.c400
1 files changed, 400 insertions, 0 deletions
diff --git a/usr/src/lib/librstp/common/rolesel.c b/usr/src/lib/librstp/common/rolesel.c
new file mode 100644
index 0000000000..871622d354
--- /dev/null
+++ b/usr/src/lib/librstp/common/rolesel.c
@@ -0,0 +1,400 @@
+/************************************************************************
+ * 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;
+ }
+}
+