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