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