1 /************************************************************************ 2 * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) 3 * Copyright (C) 2001-2003 Optical Access 4 * Author: Alex Rozin 5 * 6 * This file is part of RSTP library. 7 * 8 * RSTP library is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU Lesser General Public License as published by the 10 * Free Software Foundation; version 2.1 11 * 12 * RSTP library is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 15 * General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public License 18 * along with RSTP library; see the file COPYING. If not, write to the Free 19 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 20 * 02111-1307, USA. 21 **********************************************************************/ 22 23 /* Topolgy Change state machine : 17.25 */ 24 25 #include "base.h" 26 #include "stpm.h" 27 #include "stp_to.h" /* for STP_OUT_flush_lt */ 28 29 #define STATES { \ 30 CHOOSE(INIT), \ 31 CHOOSE(INACTIVE), \ 32 CHOOSE(TCACTIVE), \ 33 CHOOSE(DETECTED), \ 34 CHOOSE(NOTIFIED_TC), \ 35 CHOOSE(PROPAGATING), \ 36 CHOOSE(ACKNOWLEDGED), \ 37 CHOOSE(NOTIFIED_TCN) \ 38 } 39 40 #define GET_STATE_NAME STP_topoch_get_state_name 41 #include "choose.h" 42 43 #ifndef STRONGLY_SPEC_802_1W 44 /* 45 * In many kinds of hardware the function 46 * STP_OUT_flush_lt is a) is very hard and b) cannot 47 * delete learning emtries per port. The alternate 48 * method may be used: we don't care operEdge flag here, 49 * but clean learning table once for TopologyChange 50 * for all ports, except the received port. I am ready to discuss :( 51 * See below word STRONGLY_SPEC_802_1W 52 */ 53 #else 54 static Bool 55 flush (STATE_MACH_T *this, char* reason) /* 17.19.9 */ 56 { 57 register PORT_T* port = this->owner.port; 58 Bool bret; 59 60 if (port->operEdge) return True; 61 if (this->debug) { 62 stp_trace("%s (%s, %s, %s, '%s')", 63 "flush", port->port_name, port->owner->name, 64 LT_FLASH_ONLY_THE_PORT == type ? "this port" : "other ports", 65 reason); 66 } 67 68 bret = STP_OUT_flush_lt (port->port_index, port->owner->vlan_id, 69 LT_FLASH_ONLY_THE_PORT, reason); 70 } 71 #endif 72 73 static void 74 setTcPropBridge (STATE_MACH_T* this, char* reason) /* 17.19.14 */ 75 { 76 register PORT_T* port = this->owner.port; 77 register PORT_T* tmp; 78 79 for (tmp = port->owner->ports; tmp; tmp = tmp->next) { 80 if (tmp->port_index != port->port_index) 81 tmp->tcProp = True; 82 } 83 84 #ifndef STRONGLY_SPEC_802_1W 85 #ifdef STP_DBG 86 if (this->debug) { 87 stp_trace("%s (%s, %s, %s, '%s')", 88 "clearFDB", port->port_name, port->owner->name, 89 "other ports", reason); 90 } 91 #endif 92 93 STP_OUT_flush_lt (port->port_index, port->owner->vlan_id, 94 LT_FLASH_ALL_PORTS_EXCLUDE_THIS, reason); 95 #endif 96 } 97 98 static unsigned int 99 newTcWhile (STATE_MACH_T* this) /* 17.19.7 */ 100 { 101 register PORT_T* port = this->owner.port; 102 103 if (port->sendRSTP && port->operPointToPointMac) { 104 return 2 * port->owner->rootTimes.HelloTime; 105 } 106 return port->owner->rootTimes.MaxAge; 107 } 108 109 void 110 STP_topoch_enter_state (STATE_MACH_T* this) 111 { 112 register PORT_T* port = this->owner.port; 113 114 switch (this->State) { 115 case BEGIN: 116 case INIT: 117 #ifdef STRONGLY_SPEC_802_1W 118 flush (this, "topoch INIT"); 119 #endif 120 port->tcWhile = 0; 121 port->tc = 122 port->tcProp = 123 port->tcAck = False; 124 break; 125 case INACTIVE: 126 port->rcvdTc = 127 port->rcvdTcn = 128 port->rcvdTcAck = port->tc = port->tcProp = False; 129 break; 130 case TCACTIVE: 131 break; 132 case DETECTED: 133 port->tcWhile = newTcWhile (this); 134 #ifdef STP_DBG 135 if (this->debug) 136 stp_trace("DETECTED: tcWhile=%d on port %s", 137 port->tcWhile, port->port_name); 138 #endif 139 setTcPropBridge (this, "DETECTED"); 140 port->tc = False; 141 break; 142 case NOTIFIED_TC: 143 port->rcvdTcn = port->rcvdTc = False; 144 if (port->role == DesignatedPort) { 145 port->tcAck = True; 146 } 147 setTcPropBridge (this, "NOTIFIED_TC"); 148 break; 149 case PROPAGATING: 150 port->tcWhile = newTcWhile (this); 151 #ifdef STP_DBG 152 if (this->debug) 153 stp_trace("PROPAGATING: tcWhile=%d on port %s", 154 port->tcWhile, port->port_name); 155 #endif 156 #ifdef STRONGLY_SPEC_802_1W 157 flush (this, "topoch PROPAGATING"); 158 #endif 159 port->tcProp = False; 160 break; 161 case ACKNOWLEDGED: 162 port->tcWhile = 0; 163 #ifdef STP_DBG 164 if (this->debug) 165 stp_trace("ACKNOWLEDGED: tcWhile=%d on port %s", 166 port->tcWhile, port->port_name); 167 #endif 168 port->rcvdTcAck = False; 169 break; 170 case NOTIFIED_TCN: 171 port->tcWhile = newTcWhile (this); 172 #ifdef STP_DBG 173 if (this->debug) 174 stp_trace("NOTIFIED_TCN: tcWhile=%d on port %s", 175 port->tcWhile, port->port_name); 176 #endif 177 break; 178 }; 179 } 180 181 Bool 182 STP_topoch_check_conditions (STATE_MACH_T* this) 183 { 184 register PORT_T* port = this->owner.port; 185 186 if (BEGIN == this->State) { 187 return STP_hop_2_state (this, INIT); 188 } 189 190 switch (this->State) { 191 case INIT: 192 return STP_hop_2_state (this, INACTIVE); 193 case INACTIVE: 194 if (port->role == RootPort || port->role == DesignatedPort) 195 return STP_hop_2_state (this, TCACTIVE); 196 if (port->rcvdTc || port->rcvdTcn || port->rcvdTcAck || 197 port->tc || port->tcProp) 198 return STP_hop_2_state (this, INACTIVE); 199 break; 200 case TCACTIVE: 201 if (port->role != RootPort && (port->role != DesignatedPort)) 202 return STP_hop_2_state (this, INIT); 203 if (port->tc) 204 return STP_hop_2_state (this, DETECTED); 205 if (port->rcvdTcn) 206 return STP_hop_2_state (this, NOTIFIED_TCN); 207 if (port->rcvdTc) 208 return STP_hop_2_state (this, NOTIFIED_TC); 209 if (port->tcProp && !port->operEdge) 210 return STP_hop_2_state (this, PROPAGATING); 211 if (port->rcvdTcAck) 212 return STP_hop_2_state (this, ACKNOWLEDGED); 213 break; 214 case DETECTED: 215 return STP_hop_2_state (this, TCACTIVE); 216 case NOTIFIED_TC: 217 return STP_hop_2_state (this, TCACTIVE); 218 case PROPAGATING: 219 return STP_hop_2_state (this, TCACTIVE); 220 case ACKNOWLEDGED: 221 return STP_hop_2_state (this, TCACTIVE); 222 case NOTIFIED_TCN: 223 return STP_hop_2_state (this, NOTIFIED_TC); 224 }; 225 return False; 226 } 227 228