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 Selection state machine : 17.22 */
24
25 #include "base.h"
26 #include "stpm.h"
27 #include "stp_vectors.h"
28
29 #define STATES { \
30 CHOOSE(INIT_BRIDGE), \
31 CHOOSE(ROLE_SELECTION) \
32 }
33
34 #define GET_STATE_NAME STP_rolesel_get_state_name
35 #include "choose.h"
36
37 #if 0
38 void stp_dbg_break_point (PORT_T * port, STPM_T* stpm)
39 {
40 }
41 #endif
42
43 static Bool
_is_backup_port(PORT_T * port,STPM_T * this)44 _is_backup_port (PORT_T* port, STPM_T* this)
45 {
46 if (!STP_VECT_compare_bridge_id
47 (&port->portPrio.design_bridge, &this->BrId)) {
48 #if 0 /* def STP_DBG */
49 if (port->info->debug) {
50 STP_VECT_br_id_print ("portPrio.design_bridge",
51 &port->portPrio.design_bridge, True);
52 STP_VECT_br_id_print (" this->BrId",
53 &this->BrId, True);
54 }
55 stp_dbg_break_point (port, this);
56 #endif
57 return True;
58 } else {
59 return False;
60 }
61 }
62
63 /* ARGSUSED */
64 static void
setRoleSelected(char * reason,STPM_T * stpm,PORT_T * port,PORT_ROLE_T newRole)65 setRoleSelected (char* reason, STPM_T* stpm, PORT_T* port,
66 PORT_ROLE_T newRole)
67 {
68 #ifdef STP_DBG
69 char* new_role_name;
70 #endif
71
72 port->selectedRole = newRole;
73
74 if (newRole == port->role)
75 return;
76
77 switch (newRole) {
78 case DisabledPort:
79 #ifdef STP_DBG
80 new_role_name = "Disabled";
81 #endif
82 break;
83 case AlternatePort:
84 #ifdef STP_DBG
85 new_role_name = "Alternate";
86 #endif
87 break;
88 case BackupPort:
89 #ifdef STP_DBG
90 new_role_name = "Backup";
91 #endif
92 break;
93 case RootPort:
94 #ifdef STP_DBG
95 new_role_name = "Root";
96 #endif
97 break;
98 case DesignatedPort:
99 #ifdef STP_DBG
100 new_role_name = "Designated";
101 #endif
102 break;
103 case NonStpPort:
104 #ifdef STP_DBG
105 new_role_name = "NonStp";
106 #endif
107 port->role = newRole;
108 break;
109 default:
110 #ifdef STP_DBG
111 stp_trace ("%s-%s:port %s => Unknown (%d ?)",
112 reason, stpm->name, port->port_name, (int) newRole);
113 #else
114 abort();
115 #endif
116 return;
117 }
118
119 #ifdef STP_DBG
120 if (port->roletrns->debug)
121 stp_trace ("%s(%s-%s) => %s",
122 reason, stpm->name, port->port_name, new_role_name);
123 #endif
124 }
125
126 static void
updtRoleDisableBridge(STPM_T * this)127 updtRoleDisableBridge (STPM_T* this)
128 { /* 17.10.20 */
129 register PORT_T *port;
130
131 for (port = this->ports; port; port = port->next) {
132 port->selectedRole = DisabledPort;
133 }
134 }
135
136 static void
clearReselectBridge(STPM_T * this)137 clearReselectBridge (STPM_T* this)
138 { /* 17.19.1 */
139 register PORT_T *port;
140
141 for (port = this->ports; port; port = port->next) {
142 port->reselect = False;
143 }
144 }
145
146 static void
updtRootPrio(STATE_MACH_T * this)147 updtRootPrio (STATE_MACH_T* this)
148 {
149 PRIO_VECTOR_T rootPathPrio; /* 17.4.2.2 */
150 register PORT_T *port;
151 register STPM_T *stpm;
152 register unsigned int dm;
153
154 stpm = this->owner.stpm;
155
156 for (port = stpm->ports; port; port = port->next) {
157 if (port->admin_non_stp) {
158 continue;
159 }
160
161 if (Disabled == port->infoIs)
162 continue;
163 if (Aged == port->infoIs)
164 continue;
165 if (Mine == port->infoIs) {
166 #if 0 /* def STP_DBG */
167 stp_dbg_break_point (port); /* for debugger break point */
168 #endif
169 continue;
170 }
171
172 STP_VECT_copy (&rootPathPrio, &port->portPrio);
173 rootPathPrio.root_path_cost += port->operPCost;
174
175 if (STP_VECT_compare_vector (&rootPathPrio, &stpm->rootPrio) < 0) {
176 STP_VECT_copy (&stpm->rootPrio, &rootPathPrio);
177 STP_copy_times (&stpm->rootTimes, &port->portTimes);
178 dm = (8 + stpm->rootTimes.MaxAge) / 16;
179 if (!dm)
180 dm = 1;
181 stpm->rootTimes.MessageAge += dm;
182 #ifdef STP_DBG
183 if (port->roletrns->debug)
184 stp_trace ("updtRootPrio: dm=%d rootTimes.MessageAge=%d on port %s",
185 (int) dm, (int) stpm->rootTimes.MessageAge,
186 port->port_name);
187 #endif
188 }
189 }
190 }
191
192 static void
updtRolesBridge(STATE_MACH_T * this)193 updtRolesBridge (STATE_MACH_T* this)
194 { /* 17.19.21 */
195 register PORT_T* port;
196 register STPM_T* stpm;
197 #ifdef STP_DBG
198 PORT_ID old_root_port; /* for tracing of root port changing */
199 #endif
200
201 stpm = this->owner.stpm;
202 #ifdef STP_DBG
203 old_root_port = stpm->rootPortId;
204 #endif
205
206 STP_VECT_create (&stpm->rootPrio, &stpm->BrId, 0, &stpm->BrId, 0, 0);
207 STP_copy_times (&stpm->rootTimes, &stpm->BrTimes);
208 stpm->rootPortId = 0;
209
210 updtRootPrio (this);
211
212 for (port = stpm->ports; port; port = port->next) {
213 if (port->admin_non_stp) {
214 continue;
215 }
216 STP_VECT_create (&port->designPrio,
217 &stpm->rootPrio.root_bridge,
218 stpm->rootPrio.root_path_cost,
219 &stpm->BrId, port->port_id, port->port_id);
220 STP_copy_times (&port->designTimes, &stpm->rootTimes);
221
222 #if 0
223 #ifdef STP_DBG
224 if (port->roletrns->debug) {
225 STP_VECT_br_id_print ("ch:designPrio.design_bridge",
226 &port->designPrio.design_bridge, True);
227 }
228 #endif
229 #endif
230 }
231
232 stpm->rootPortId = stpm->rootPrio.bridge_port;
233
234 #ifdef STP_DBG
235 if (old_root_port != stpm->rootPortId) {
236 if (! stpm->rootPortId) {
237 stp_trace ("bridge %s became root", stpm->name);
238 } else {
239 stp_trace ("bridge %s new root port: %s",
240 stpm->name,
241 STP_stpm_get_port_name_by_id (stpm, stpm->rootPortId));
242 }
243 }
244 #endif
245
246 for (port = stpm->ports; port; port = port->next) {
247 if (port->admin_non_stp) {
248 setRoleSelected ("Non", stpm, port, NonStpPort);
249 port->forward = port->learn = True;
250 continue;
251 }
252
253 switch (port->infoIs) {
254 case Disabled:
255 setRoleSelected ("Dis", stpm, port, DisabledPort);
256 break;
257 case Aged:
258 setRoleSelected ("Age", stpm, port, DesignatedPort);
259 port->updtInfo = True;
260 break;
261 case Mine:
262 setRoleSelected ("Mine", stpm, port, DesignatedPort);
263 if (0 != STP_VECT_compare_vector (&port->portPrio,
264 &port->designPrio) ||
265 0 != STP_compare_times (&port->portTimes,
266 &port->designTimes)) {
267 port->updtInfo = True;
268 }
269 break;
270 case Received:
271 if (stpm->rootPortId == port->port_id) {
272 setRoleSelected ("Rec", stpm, port, RootPort);
273 } else if (STP_VECT_compare_vector (&port->designPrio, &port->portPrio) < 0) {
274 /* Note: this important piece has been inserted after
275 * discussion with Mick Sieman and reading 802.1y Z1 */
276 setRoleSelected ("Rec", stpm, port, DesignatedPort);
277 port->updtInfo = True;
278 break;
279 } else {
280 if (_is_backup_port (port, stpm)) {
281 setRoleSelected ("rec", stpm, port, BackupPort);
282 } else {
283 setRoleSelected ("rec", stpm, port, AlternatePort);
284 }
285 }
286 port->updtInfo = False;
287 break;
288 default:
289 stp_trace ("undef infoIs=%d", (int) port->infoIs);
290 break;
291 }
292 }
293
294 }
295
296
297 static Bool
setSelectedBridge(STPM_T * this)298 setSelectedBridge (STPM_T* this)
299 {
300 register PORT_T* port;
301
302 for (port = this->ports; port; port = port->next) {
303 if (port->reselect) {
304 #ifdef STP_DBG
305 stp_trace ("setSelectedBridge: TRUE=reselect on port %s", port->port_name);
306 #endif
307 return False;
308 }
309 }
310
311 for (port = this->ports; port; port = port->next) {
312 port->selected = True;
313 }
314
315 return True;
316 }
317
318 void
STP_rolesel_enter_state(STATE_MACH_T * this)319 STP_rolesel_enter_state (STATE_MACH_T* this)
320 {
321 STPM_T* stpm;
322
323 stpm = this->owner.stpm;
324
325 switch (this->State) {
326 case BEGIN:
327 case INIT_BRIDGE:
328 updtRoleDisableBridge (stpm);
329 break;
330 case ROLE_SELECTION:
331 clearReselectBridge (stpm);
332 updtRolesBridge (this);
333 (void) setSelectedBridge (stpm);
334 break;
335 }
336 }
337
338 Bool
STP_rolesel_check_conditions(STATE_MACH_T * s)339 STP_rolesel_check_conditions (STATE_MACH_T* s)
340 {
341 STPM_T* stpm;
342 register PORT_T* port;
343
344 /*
345 * This doesn't look right. Why should we hop state twice in a single check
346 * condition call? It means we can never perform the enter-state action for
347 * INIT_BRIDGE.
348 */
349 #ifdef carlsonj_removed
350 if (BEGIN == s->State) {
351 (void) STP_hop_2_state (s, INIT_BRIDGE);
352 }
353 #endif
354
355 switch (s->State) {
356 case BEGIN:
357 return STP_hop_2_state (s, INIT_BRIDGE);
358 case INIT_BRIDGE:
359 return STP_hop_2_state (s, ROLE_SELECTION);
360 case ROLE_SELECTION:
361 stpm = s->owner.stpm;
362 for (port = stpm->ports; port; port = port->next) {
363 if (port->reselect) {
364 /* stp_trace ("reselect on port %s", port->port_name); */
365 return STP_hop_2_state (s, ROLE_SELECTION);
366 }
367 }
368 break;
369 }
370
371 return False;
372 }
373
374 void
STP_rolesel_update_stpm(STPM_T * this)375 STP_rolesel_update_stpm (STPM_T* this)
376 {
377 register PORT_T* port;
378 PRIO_VECTOR_T rootPathPrio; /* 17.4.2.2 */
379
380 stp_trace ("%s", "??? STP_rolesel_update_stpm ???");
381 STP_VECT_create (&rootPathPrio, &this->BrId, 0, &this->BrId, 0, 0);
382
383 if (!this->rootPortId ||
384 STP_VECT_compare_vector (&rootPathPrio, &this->rootPrio) < 0) {
385 STP_VECT_copy (&this->rootPrio, &rootPathPrio);
386 }
387
388 for (port = this->ports; port; port = port->next) {
389 STP_VECT_create (&port->designPrio,
390 &this->rootPrio.root_bridge,
391 this->rootPrio.root_path_cost,
392 &this->BrId, port->port_id, port->port_id);
393 if (Received != port->infoIs || this->rootPortId == port->port_id) {
394 STP_VECT_copy (&port->portPrio, &port->designPrio);
395 }
396 port->reselect = True;
397 port->selected = False;
398 }
399 }
400
401