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