/************************************************************************ * 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. **********************************************************************/ #include "base.h" #include "stpm.h" #include "stp_vectors.h" /* The Port Information State Machine : 17.21 */ #define STATES { \ CHOOSE(DISABLED), \ CHOOSE(ENABLED), \ CHOOSE(AGED), \ CHOOSE(UPDATE), \ CHOOSE(CURRENT), \ CHOOSE(RECEIVE), \ CHOOSE(SUPERIOR), \ CHOOSE(REPEAT), \ CHOOSE(AGREEMENT) \ } #define GET_STATE_NAME STP_info_get_state_name #include "choose.h" #if 0 /* for debug */ void _stp_dump (char* title, unsigned char* buff, int len) { register int iii; stp_trace ("\n%s:", title); for (iii = 0; iii < len; iii++) { if (! (iii % 24)) stp_trace ("\n%6d:", iii); if (! (iii % 8)) stp_trace (" "); stp_trace ("%02lx", (unsigned long) buff[iii]); } stp_trace ("\n"); } #endif static RCVD_MSG_T rcvBpdu (STATE_MACH_T* this) {/* 17.19.8 */ int bridcmp; register PORT_T* port = this->owner.port; if (port->msgBpduType == BPDU_TOPO_CHANGE_TYPE) { #ifdef STP_DBG if (this->debug) { stp_trace ("%s", "rcvBpdu: OtherMsg:BPDU_TOPO_CHANGE_TYPE"); } #endif return OtherMsg; } port->msgPortRole = RSTP_PORT_ROLE_UNKN; if (BPDU_RSTP == port->msgBpduType) { port->msgPortRole = (port->msgFlags & PORT_ROLE_MASK) >> PORT_ROLE_OFFS; } if (RSTP_PORT_ROLE_DESGN == port->msgPortRole || BPDU_CONFIG_TYPE == port->msgBpduType) { bridcmp = STP_VECT_compare_vector (&port->msgPrio, &port->portPrio); if (bridcmp < 0 || (! STP_VECT_compare_bridge_id (&port->msgPrio.design_bridge, &port->portPrio.design_bridge) && port->msgPrio.design_port == port->portPrio.design_port && STP_compare_times (&port->msgTimes, &port->portTimes))) { #ifdef STP_DBG if (this->debug) { stp_trace ("rcvBpdu: SuperiorDesignateMsg:bridcmp=%d", (int) bridcmp); } #endif return SuperiorDesignateMsg; } } if (BPDU_CONFIG_TYPE == port->msgBpduType || RSTP_PORT_ROLE_DESGN == port->msgPortRole) { if (! STP_VECT_compare_vector (&port->msgPrio, &port->portPrio) && ! STP_compare_times (&port->msgTimes, &port->portTimes)) { #ifdef STP_DBG if (this->debug) { stp_trace ("%s", "rcvBpdu: RepeatedDesignateMsg"); } #endif return RepeatedDesignateMsg; } } if (RSTP_PORT_ROLE_ROOT == port->msgBpduType && port->operPointToPointMac && ! STP_VECT_compare_bridge_id (&port->msgPrio.design_bridge, &port->portPrio.design_bridge) && AGREEMENT_BIT & port->msgFlags) { #ifdef STP_DBG if (this->debug) { stp_trace ("%s", "rcvBpdu: ConfirmedRootMsg"); } #endif return ConfirmedRootMsg; } #ifdef STP_DBG if (this->debug) { if (RSTP_PORT_ROLE_ROOT == port->msgBpduType) { if (!port->operPointToPointMac) { stp_trace("rcvBpdu: OtherMsg: not point-to-point MAC"); } else if (STP_VECT_compare_bridge_id (&port->msgPrio.design_bridge, &port->portPrio.design_bridge)) { STP_VECT_br_id_print("rcvBpdu: OtherMsg: msgPrio", &port->msgPrio.design_bridge, True); STP_VECT_br_id_print("rcvBpdu: portPrio", &port->portPrio.design_bridge, True); } else { stp_trace("rcvBpdu: OtherMsg: agreement bit not set"); } } else { stp_trace ("rcvBpdu: OtherMsg: type %d", port->msgBpduType); } } #endif return OtherMsg; } /* ARGSUSED */ static Bool recordProposed (STATE_MACH_T* this, char* reason) {/* 17.19.9 */ register PORT_T* port = this->owner.port; if (RSTP_PORT_ROLE_DESGN == port->msgPortRole && (PROPOSAL_BIT & port->msgFlags) && port->operPointToPointMac) { return True; } return False; } static void setTcFlags (STATE_MACH_T* this) {/* 17.19.13 */ register PORT_T* port = this->owner.port; if (BPDU_TOPO_CHANGE_TYPE == port->msgBpduType) { #ifdef STP_DBG if (this->debug) { stp_trace ("port %s rx rcvdTcn", port->port_name); } #endif port->rcvdTcn = True; } else { if (TOPOLOGY_CHANGE_BIT & port->msgFlags) { #ifdef STP_DBG if (this->debug) { stp_trace ("(%s-%s) rx rcvdTc 0X%lx", port->owner->name, port->port_name, (unsigned long) port->msgFlags); } #endif port->rcvdTc = True; } if (TOPOLOGY_CHANGE_ACK_BIT & port->msgFlags) { #ifdef STP_DBG if (this->debug) { stp_trace ("port %s rx rcvdTcAck 0X%lx", port->port_name, (unsigned long) port->msgFlags); } #endif port->rcvdTcAck = True; } } } static void updtBPDUVersion (STATE_MACH_T* this) {/* 17.19.18 */ register PORT_T* port = this->owner.port; if (BPDU_TOPO_CHANGE_TYPE == port->msgBpduType) { port->rcvdSTP = True; } if (port->msgBpduVersion < 2) { port->rcvdSTP = True; } if (BPDU_RSTP == port->msgBpduType) { /* port->port->owner->ForceVersion >= NORMAL_RSTP we have checked in STP_info_rx_bpdu */ port->rcvdRSTP = True; } } static void updtRcvdInfoWhile (STATE_MACH_T* this) {/* 17.19.19 */ register int eff_age, dm, dt; register int hello3; register PORT_T* port = this->owner.port; eff_age = ( + port->portTimes.MaxAge) / 16; if (eff_age < 1) eff_age = 1; eff_age += port->portTimes.MessageAge; if (eff_age <= port->portTimes.MaxAge) { hello3 = 3 * port->portTimes.HelloTime; dm = port->portTimes.MaxAge - eff_age; if (dm > hello3) dt = hello3; else dt = dm; port->rcvdInfoWhile = dt; /**** stp_trace ("ma=%d eff_age=%d dm=%d dt=%d p=%s", (int) port->portTimes.MessageAge, (int) eff_age, (int) dm, (int) dt, port->port_name); ****/ } else { port->rcvdInfoWhile = 0; /****/ #ifdef STP_DBG /*if (this->debug) */ { stp_trace ("port %s: MaxAge=%d MessageAge=%d HelloTime=%d rcvdInfoWhile=null !", port->port_name, (int) port->portTimes.MaxAge, (int) port->portTimes.MessageAge, (int) port->portTimes.HelloTime); } #endif /****/ } } /* ARGSUSED */ void STP_info_rx_bpdu (PORT_T* port, struct stp_bpdu_t* bpdu, size_t len) { #if 0 _stp_dump ("\nall BPDU", ((unsigned char*) bpdu) - 12, len + 12); _stp_dump ("ETH_HEADER", (unsigned char*) &bpdu->eth, 5); _stp_dump ("BPDU_HEADER", (unsigned char*) &bpdu->hdr, 4); stp_trace ("protocol=%02x%02x version=%02x bpdu_type=%02x\n", bpdu->hdr.protocol[0], bpdu->hdr.protocol[1], bpdu->hdr.version, bpdu->hdr.bpdu_type); _stp_dump ("\nBPDU_BODY", (unsigned char*) &bpdu->body, sizeof (BPDU_BODY_T) + 2); stp_trace ("flags=%02x\n", bpdu->body.flags); _stp_dump ("root_id", bpdu->body.root_id, 8); _stp_dump ("root_path_cost", bpdu->body.root_path_cost, 4); _stp_dump ("bridge_id", bpdu->body.bridge_id, 8); _stp_dump ("port_id", bpdu->body.port_id, 2); _stp_dump ("message_age", bpdu->body.message_age, 2); _stp_dump ("max_age", bpdu->body.max_age, 2); _stp_dump ("hello_time", bpdu->body.hello_time, 2); _stp_dump ("forward_delay", bpdu->body.forward_delay, 2); _stp_dump ("ver_1_len", bpdu->ver_1_len, 2); #endif /* check bpdu type */ switch (bpdu->hdr.bpdu_type) { case BPDU_CONFIG_TYPE: port->rx_cfg_bpdu_cnt++; #ifdef STP_DBG if (port->info->debug) stp_trace ("CfgBpdu on port %s", port->port_name); #endif if (port->admin_non_stp) return; port->rcvdBpdu = True; break; case BPDU_TOPO_CHANGE_TYPE: port->rx_tcn_bpdu_cnt++; #ifdef STP_DBG if (port->info->debug) stp_trace ("TcnBpdu on port %s", port->port_name); #endif if (port->admin_non_stp) return; port->rcvdBpdu = True; port->msgBpduVersion = bpdu->hdr.version; port->msgBpduType = bpdu->hdr.bpdu_type; return; default: stp_trace ("RX undef bpdu type=%d", (int) bpdu->hdr.bpdu_type); return; case BPDU_RSTP: port->rx_rstp_bpdu_cnt++; if (port->admin_non_stp) return; if (port->owner->ForceVersion >= NORMAL_RSTP) { port->rcvdBpdu = True; } else { return; } #ifdef STP_DBG if (port->info->debug) stp_trace ("BPDU_RSTP on port %s", port->port_name); #endif break; } port->msgBpduVersion = bpdu->hdr.version; port->msgBpduType = bpdu->hdr.bpdu_type; port->msgFlags = bpdu->body.flags; /* 17.18.11 */ STP_VECT_get_vector (&bpdu->body, &port->msgPrio); port->msgPrio.bridge_port = port->port_id; /* 17.18.12 */ STP_get_times (&bpdu->body, &port->msgTimes); /* 17.18.25, 17.18.26 : see setTcFlags() */ } void STP_info_enter_state (STATE_MACH_T* this) { register PORT_T* port = this->owner.port; switch (this->State) { case BEGIN: port->rcvdMsg = OtherMsg; port->msgBpduType = (unsigned char)-1; port->msgPortRole = RSTP_PORT_ROLE_UNKN; port->msgFlags = 0; /* clear port statistics */ port->rx_cfg_bpdu_cnt = port->rx_rstp_bpdu_cnt = port->rx_tcn_bpdu_cnt = 0; /* FALLTHRU */ case DISABLED: port->rcvdBpdu = port->rcvdRSTP = port->rcvdSTP = False; port->updtInfo = port->proposing = False; /* In DISABLED */ port->agreed = port->proposed = False; port->rcvdInfoWhile = 0; port->infoIs = Disabled; port->reselect = True; port->selected = False; break; case ENABLED: /* IEEE 802.1y, 17.21, Z.14 */ STP_VECT_copy (&port->portPrio, &port->designPrio); STP_copy_times (&port->portTimes, &port->designTimes); break; case AGED: port->infoIs = Aged; port->reselect = True; port->selected = False; break; case UPDATE: STP_VECT_copy (&port->portPrio, &port->designPrio); STP_copy_times (&port->portTimes, &port->designTimes); port->updtInfo = False; port->agreed = port->synced = False; /* In UPDATE */ port->proposed = port->proposing = False; /* in UPDATE */ port->infoIs = Mine; port->newInfo = True; #ifdef STP_DBG if (this->debug) { STP_VECT_br_id_print ("updated: portPrio.design_bridge", &port->portPrio.design_bridge, True); STP_VECT_br_id_print ("updated: portPrio.root_bridge", &port->portPrio.root_bridge, True); } #endif break; case CURRENT: break; case RECEIVE: port->rcvdMsg = rcvBpdu (this); updtBPDUVersion (this); setTcFlags (this); port->rcvdBpdu = False; break; case SUPERIOR: STP_VECT_copy (&port->portPrio, &port->msgPrio); STP_copy_times (&port->portTimes, &port->msgTimes); updtRcvdInfoWhile (this); #if 1 /* due 802.1y, Z.7 */ port->agreed = False; /* deleted due 802.y in SUPERIOR */ port->synced = False; /* due 802.y deleted in SUPERIOR */ #endif port->proposing = False; /* in SUPERIOR */ port->proposed = recordProposed (this, "SUPERIOR"); port->infoIs = Received; port->reselect = True; port->selected = False; #ifdef STP_DBG if (this->debug) { STP_VECT_br_id_print ("stored: portPrio.design_bridge", &port->portPrio.design_bridge, True); STP_VECT_br_id_print ("stored: portPrio.root_bridge", &port->portPrio.root_bridge, True); stp_trace ("proposed=%d on port %s", (int) port->proposed, port->port_name); } #endif break; case REPEAT: port->proposed = recordProposed (this, "REPEAT"); updtRcvdInfoWhile (this); break; case AGREEMENT: #ifdef STP_DBG if (port->roletrns->debug) { stp_trace ("(%s-%s) rx AGREEMENT flag !", port->owner->name, port->port_name); } #endif port->agreed = True; port->proposing = False; /* In AGREEMENT */ break; } } Bool STP_info_check_conditions (STATE_MACH_T* this) { register PORT_T* port = this->owner.port; if ((! port->portEnabled && port->infoIs != Disabled) || BEGIN == this->State) { return STP_hop_2_state (this, DISABLED); } switch (this->State) { case DISABLED: if (port->updtInfo) { return STP_hop_2_state (this, DISABLED); } if (port->portEnabled && port->selected) { return STP_hop_2_state (this, ENABLED); } if (port->rcvdBpdu) { return STP_hop_2_state (this, DISABLED); } break; case ENABLED: /* IEEE 802.1y, 17.21, Z.14 */ return STP_hop_2_state (this, AGED); case AGED: if (port->selected && port->updtInfo) { return STP_hop_2_state (this, UPDATE); } break; case UPDATE: return STP_hop_2_state (this, CURRENT); case CURRENT: if (port->selected && port->updtInfo) { return STP_hop_2_state (this, UPDATE); } if (Received == port->infoIs && ! port->rcvdInfoWhile && ! port->updtInfo && ! port->rcvdBpdu) { return STP_hop_2_state (this, AGED); } if (port->rcvdBpdu && !port->updtInfo) { return STP_hop_2_state (this, RECEIVE); } break; case RECEIVE: switch (port->rcvdMsg) { case SuperiorDesignateMsg: return STP_hop_2_state (this, SUPERIOR); case RepeatedDesignateMsg: return STP_hop_2_state (this, REPEAT); case ConfirmedRootMsg: return STP_hop_2_state (this, AGREEMENT); default: return STP_hop_2_state (this, CURRENT); } case SUPERIOR: return STP_hop_2_state (this, CURRENT); case REPEAT: return STP_hop_2_state (this, CURRENT); case AGREEMENT: return STP_hop_2_state (this, CURRENT); } return False; }