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 /* Port Role Transitions state machine : 17.24 */
24
25 #include "base.h"
26
27 #include "stpm.h"
28
29 #define STATES { \
30 CHOOSE(INIT_PORT), \
31 CHOOSE(BLOCK_PORT), \
32 CHOOSE(BLOCKED_PORT), \
33 CHOOSE(BACKUP_PORT), \
34 CHOOSE(ROOT_PROPOSED), \
35 CHOOSE(ROOT_AGREED), \
36 CHOOSE(REROOT), \
37 CHOOSE(ROOT_PORT), \
38 CHOOSE(REROOTED), \
39 CHOOSE(ROOT_LEARN), \
40 CHOOSE(ROOT_FORWARD), \
41 CHOOSE(DESIGNATED_PROPOSE), \
42 CHOOSE(DESIGNATED_SYNCED), \
43 CHOOSE(DESIGNATED_RETIRED), \
44 CHOOSE(DESIGNATED_PORT), \
45 CHOOSE(DESIGNATED_LISTEN), \
46 CHOOSE(DESIGNATED_LEARN), \
47 CHOOSE(DESIGNATED_FORWARD) \
48 }
49
50 #define GET_STATE_NAME STP_roletrns_get_state_name
51 #include "choose.h"
52
53 static void
setSyncBridge(STATE_MACH_T * this)54 setSyncBridge (STATE_MACH_T *this)
55 {
56 register PORT_T* port;
57
58 for (port = this->owner.port->owner->ports; port; port = port->next) {
59 port->sync = True; /* in ROOT_PROPOSED (setSyncBridge) */
60 }
61 }
62
63 static void
setReRootBridge(STATE_MACH_T * this)64 setReRootBridge (STATE_MACH_T *this)
65 {
66 register PORT_T* port;
67
68 for (port = this->owner.port->owner->ports; port; port = port->next) {
69 port->reRoot = True; /* In setReRootBridge */
70 }
71 }
72
73 static Bool
compute_all_synced(PORT_T * this)74 compute_all_synced (PORT_T* this)
75 {
76 register PORT_T* port;
77
78 for (port = this->owner->ports; port; port = port->next) {
79 if (port->port_index == this->port_index) continue;
80 if (! port->synced) {
81 return False;
82 }
83 }
84
85 return True;
86 }
87
88 static Bool
compute_re_rooted(PORT_T * this)89 compute_re_rooted (PORT_T* this)
90 {
91 register PORT_T* port;
92
93 for (port = this->owner->ports; port; port = port->next) {
94 if (port->port_index == this->port_index) continue;
95 if (port->rrWhile) {
96 return False;
97 }
98 }
99 return True;
100 }
101
102 void
STP_roletrns_enter_state(STATE_MACH_T * this)103 STP_roletrns_enter_state (STATE_MACH_T* this)
104 {
105 register PORT_T* port = this->owner.port;
106 register STPM_T* stpm;
107
108 stpm = port->owner;
109
110 switch (this->State) {
111 case BEGIN:
112 case INIT_PORT:
113 #if 0 /* due 802.1y Z.4 */
114 port->role = DisabledPort;
115 #else
116 port->role = port->selectedRole = DisabledPort;
117 port->reselect = True;
118 #endif
119 port->synced = False; /* in INIT */
120 port->sync = True; /* in INIT */
121 port->reRoot = True; /* in INIT_PORT */
122 port->rrWhile = stpm->rootTimes.ForwardDelay;
123 port->fdWhile = stpm->rootTimes.ForwardDelay;
124 port->rbWhile = 0;
125 #ifdef STP_DBG
126 if (this->debug)
127 STP_port_trace_flags ("after init", port);
128 #endif
129 break;
130 case BLOCK_PORT:
131 port->role = port->selectedRole;
132 port->learn =
133 port->forward = False;
134 break;
135 case BLOCKED_PORT:
136 port->fdWhile = stpm->rootTimes.ForwardDelay;
137 port->synced = True; /* In BLOCKED_PORT */
138 port->rrWhile = 0;
139 port->sync = port->reRoot = False; /* BLOCKED_PORT */
140 break;
141 case BACKUP_PORT:
142 port->rbWhile = 2 * stpm->rootTimes.HelloTime;
143 break;
144
145 /* 17.23.2 */
146 case ROOT_PROPOSED:
147 setSyncBridge (this);
148 port->proposed = False;
149 #ifdef STP_DBG
150 if (this->debug)
151 STP_port_trace_flags ("ROOT_PROPOSED", port);
152 #endif
153 break;
154 case ROOT_AGREED:
155 port->proposed = port->sync = False; /* in ROOT_AGREED */
156 port->synced = True; /* In ROOT_AGREED */
157 port->newInfo = True;
158 #ifdef STP_DBG
159 if (this->debug)
160 STP_port_trace_flags ("ROOT_AGREED", port);
161 #endif
162 break;
163 case REROOT:
164 setReRootBridge (this);
165 #ifdef STP_DBG
166 if (this->debug)
167 STP_port_trace_flags ("REROOT", port);
168 #endif
169 break;
170 case ROOT_PORT:
171 port->role = RootPort;
172 port->rrWhile = stpm->rootTimes.ForwardDelay;
173 #ifdef STP_DBG
174 if (this->debug)
175 STP_port_trace_flags ("ROOT_PORT", port);
176 #endif
177 break;
178 case REROOTED:
179 port->reRoot = False; /* In REROOTED */
180 #ifdef STP_DBG
181 if (this->debug)
182 STP_port_trace_flags ("REROOTED", port);
183 #endif
184 break;
185 case ROOT_LEARN:
186 port->fdWhile = stpm->rootTimes.ForwardDelay;
187 port->learn = True;
188 #ifdef STP_DBG
189 if (this->debug)
190 STP_port_trace_flags ("ROOT_LEARN", port);
191 #endif
192 break;
193 case ROOT_FORWARD:
194 port->fdWhile = 0;
195 port->forward = True;
196 #ifdef STP_DBG
197 if (this->debug)
198 STP_port_trace_flags ("ROOT_FORWARD", port);
199 #endif
200 break;
201
202 /* 17.23.3 */
203 case DESIGNATED_PROPOSE:
204 port->proposing = True; /* in DESIGNATED_PROPOSE */
205 port->newInfo = True;
206 #ifdef STP_DBG
207 if (this->debug)
208 STP_port_trace_flags ("DESIGNATED_PROPOSE", port);
209 #endif
210 break;
211 case DESIGNATED_SYNCED:
212 port->rrWhile = 0;
213 port->synced = True; /* DESIGNATED_SYNCED */
214 port->sync = False; /* DESIGNATED_SYNCED */
215 #ifdef STP_DBG
216 if (this->debug)
217 STP_port_trace_flags ("DESIGNATED_SYNCED", port);
218 #endif
219 break;
220 case DESIGNATED_RETIRED:
221 port->reRoot = False; /* DESIGNATED_RETIRED */
222 #ifdef STP_DBG
223 if (this->debug)
224 STP_port_trace_flags ("DESIGNATED_RETIRED", port);
225 #endif
226 break;
227 case DESIGNATED_PORT:
228 port->role = DesignatedPort;
229 #ifdef STP_DBG
230 if (this->debug)
231 STP_port_trace_flags ("DESIGNATED_PORT", port);
232 #endif
233 break;
234 case DESIGNATED_LISTEN:
235 port->learn = port->forward = False;
236 port->fdWhile = stpm->rootTimes.ForwardDelay;
237 #ifdef STP_DBG
238 if (this->debug)
239 STP_port_trace_flags ("DESIGNATED_LISTEN", port);
240 #endif
241 break;
242 case DESIGNATED_LEARN:
243 port->learn = True;
244 port->fdWhile = stpm->rootTimes.ForwardDelay;
245 #ifdef STP_DBG
246 if (this->debug)
247 STP_port_trace_flags ("DESIGNATED_LEARN", port);
248 #endif
249 break;
250 case DESIGNATED_FORWARD:
251 port->forward = True;
252 port->fdWhile = 0;
253 #ifdef STP_DBG
254 if (this->debug)
255 STP_port_trace_flags ("DESIGNATED_FORWARD", port);
256 #endif
257 break;
258 };
259 }
260
261 Bool
STP_roletrns_check_conditions(STATE_MACH_T * this)262 STP_roletrns_check_conditions (STATE_MACH_T* this)
263 {
264 register PORT_T *port = this->owner.port;
265 register STPM_T *stpm;
266 Bool allSynced;
267 Bool allReRooted;
268
269 stpm = port->owner;
270
271 if (BEGIN == this->State) {
272 return STP_hop_2_state (this, INIT_PORT);
273 }
274
275 if (port->role != port->selectedRole &&
276 port->selected &&
277 ! port->updtInfo) {
278 switch (port->selectedRole) {
279 case DisabledPort:
280 case AlternatePort:
281 case BackupPort:
282 #if 0 /* def STP_DBG */
283 if (this->debug) {
284 stp_trace ("hop to BLOCK_PORT role=%d selectedRole=%d",
285 (int) port->role, (int) port->selectedRole);
286 }
287 #endif
288 return STP_hop_2_state (this, BLOCK_PORT);
289 case RootPort:
290 return STP_hop_2_state (this, ROOT_PORT);
291 case DesignatedPort:
292 return STP_hop_2_state (this, DESIGNATED_PORT);
293 default:
294 return False;
295 }
296 }
297
298 switch (this->State) {
299 /* 17.23.1 */
300 case INIT_PORT:
301 return STP_hop_2_state (this, BLOCK_PORT);
302 case BLOCK_PORT:
303 if (!port->selected || port->updtInfo) break;
304 if (!port->learning && !port->forwarding) {
305 return STP_hop_2_state (this, BLOCKED_PORT);
306 }
307 break;
308 case BLOCKED_PORT:
309 if (!port->selected || port->updtInfo) break;
310 if (port->fdWhile != stpm->rootTimes.ForwardDelay ||
311 port->sync ||
312 port->reRoot ||
313 !port->synced) {
314 return STP_hop_2_state (this, BLOCKED_PORT);
315 }
316 if (port->rbWhile != 2 * stpm->rootTimes.HelloTime &&
317 port->role == BackupPort) {
318 return STP_hop_2_state (this, BACKUP_PORT);
319 }
320 break;
321 case BACKUP_PORT:
322 return STP_hop_2_state (this, BLOCKED_PORT);
323
324 /* 17.23.2 */
325 case ROOT_PROPOSED:
326 return STP_hop_2_state (this, ROOT_PORT);
327 case ROOT_AGREED:
328 return STP_hop_2_state (this, ROOT_PORT);
329 case REROOT:
330 return STP_hop_2_state (this, ROOT_PORT);
331 case ROOT_PORT:
332 if (!port->selected || port->updtInfo) break;
333 if (!port->forward && !port->reRoot) {
334 return STP_hop_2_state (this, REROOT);
335 }
336 allSynced = compute_all_synced (port);
337 if ((port->proposed && allSynced) ||
338 (!port->synced && allSynced)) {
339 return STP_hop_2_state (this, ROOT_AGREED);
340 }
341 if (port->proposed && !port->synced) {
342 return STP_hop_2_state (this, ROOT_PROPOSED);
343 }
344
345 allReRooted = compute_re_rooted (port);
346 if ((!port->fdWhile ||
347 ((allReRooted && !port->rbWhile) && stpm->ForceVersion >=2)) &&
348 port->learn && !port->forward) {
349 return STP_hop_2_state (this, ROOT_FORWARD);
350 }
351 if ((!port->fdWhile ||
352 ((allReRooted && !port->rbWhile) && stpm->ForceVersion >=2)) &&
353 !port->learn) {
354 return STP_hop_2_state (this, ROOT_LEARN);
355 }
356
357 if (port->reRoot && port->forward) {
358 return STP_hop_2_state (this, REROOTED);
359 }
360 if (port->rrWhile != stpm->rootTimes.ForwardDelay) {
361 return STP_hop_2_state (this, ROOT_PORT);
362 }
363 break;
364 case REROOTED:
365 return STP_hop_2_state (this, ROOT_PORT);
366 case ROOT_LEARN:
367 return STP_hop_2_state (this, ROOT_PORT);
368 case ROOT_FORWARD:
369 return STP_hop_2_state (this, ROOT_PORT);
370
371 /* 17.23.3 */
372 case DESIGNATED_PROPOSE:
373 return STP_hop_2_state (this, DESIGNATED_PORT);
374 case DESIGNATED_SYNCED:
375 return STP_hop_2_state (this, DESIGNATED_PORT);
376 case DESIGNATED_RETIRED:
377 return STP_hop_2_state (this, DESIGNATED_PORT);
378 case DESIGNATED_PORT:
379 if (!port->selected || port->updtInfo) break;
380
381 if (!port->forward && !port->agreed && !port->proposing && !port->operEdge) {
382 return STP_hop_2_state (this, DESIGNATED_PROPOSE);
383 }
384
385 if (!port->rrWhile && port->reRoot) {
386 return STP_hop_2_state (this, DESIGNATED_RETIRED);
387 }
388
389 if (!port->learning && !port->forwarding && !port->synced) {
390 return STP_hop_2_state (this, DESIGNATED_SYNCED);
391 }
392
393 if (port->agreed && !port->synced) {
394 return STP_hop_2_state (this, DESIGNATED_SYNCED);
395 }
396 if (port->operEdge && !port->synced) {
397 return STP_hop_2_state (this, DESIGNATED_SYNCED);
398 }
399 if (port->sync && port->synced) {
400 return STP_hop_2_state (this, DESIGNATED_SYNCED);
401 }
402
403 if ((!port->fdWhile || port->agreed || port->operEdge) &&
404 (!port->rrWhile || !port->reRoot) &&
405 !port->sync &&
406 (port->learn && !port->forward)) {
407 return STP_hop_2_state (this, DESIGNATED_FORWARD);
408 }
409 if ((!port->fdWhile || port->agreed || port->operEdge) &&
410 (!port->rrWhile || !port->reRoot) &&
411 !port->sync && !port->learn) {
412 return STP_hop_2_state (this, DESIGNATED_LEARN);
413 }
414 if (((port->sync && !port->synced) ||
415 (port->reRoot && port->rrWhile)) &&
416 !port->operEdge && (port->learn || port->forward)) {
417 return STP_hop_2_state (this, DESIGNATED_LISTEN);
418 }
419 break;
420 case DESIGNATED_LISTEN:
421 return STP_hop_2_state (this, DESIGNATED_PORT);
422 case DESIGNATED_LEARN:
423 return STP_hop_2_state (this, DESIGNATED_PORT);
424 case DESIGNATED_FORWARD:
425 return STP_hop_2_state (this, DESIGNATED_PORT);
426 };
427
428 return False;
429 }
430
431
432