xref: /illumos-gate/usr/src/lib/librstp/common/stpm.c (revision 45405cce0657d01714b3d014a0facf3bdce45736)
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  /* STP machine instance : bridge per VLAN: 17.17 */
24 
25 #include "base.h"
26 #include "stpm.h"
27 #include "stp_to.h" /* for STP_OUT_flush_lt */
28 
29 static STPM_T *bridges = NULL;
30 
31 static int
32 _stp_stpm_init_machine (STATE_MACH_T* this)
33 {
34   this->State = BEGIN;
35   (*(this->concreteEnterState)) (this);
36   return 0;
37 }
38 
39 static int
40 _stp_stpm_iterate_machines (STPM_T* this,
41                            int (*iter_callb) (STATE_MACH_T*),
42                            Bool exit_on_non_zero_ret)
43 {
44   register STATE_MACH_T* stater;
45   register PORT_T*       port;
46   int                    iret, mret = 0;
47 
48   /* state machines per bridge */
49   for (stater = this->machines; stater; stater = stater->next) {
50     iret = (*iter_callb) (stater);
51     if (exit_on_non_zero_ret && iret)
52       return iret;
53     else
54       mret += iret;
55   }
56 
57   /* state machines per port */
58   for (port = this->ports; port; port = port->next) {
59     for (stater = port->machines; stater; stater = stater->next) {
60       iret = (*iter_callb) (stater);
61       if (exit_on_non_zero_ret && iret)
62         return iret;
63       else
64         mret += iret;
65     }
66   }
67 
68   return mret;
69 }
70 
71 void
72 _stp_stpm_init_data (STPM_T* this)
73 {
74   STP_VECT_create (&this->rootPrio,
75                    &this->BrId,
76                    0,
77                    &this->BrId,
78                    0, 0);
79 
80   this->BrTimes.MessageAge = 0;
81 
82   STP_copy_times (&this->rootTimes, &this->BrTimes);
83 }
84 
85 static unsigned char
86 _check_topoch (STPM_T* this)
87 {
88   register PORT_T*  port;
89 
90   for (port = this->ports; port; port = port->next) {
91     if (port->tcWhile) {
92       return 1;
93     }
94   }
95   return 0;
96 }
97 
98 void
99 STP_stpm_one_second (STPM_T* param)
100 {
101   STPM_T*           this = (STPM_T*) param;
102   register PORT_T*  port;
103   register int      iii;
104 
105   if (STP_ENABLED != this->admin_state) return;
106 
107   for (port = this->ports; port; port = port->next) {
108     for (iii = 0; iii < TIMERS_NUMBER; iii++) {
109       if (*(port->timers[iii]) > 0) {
110         (*port->timers[iii])--;
111       }
112     }
113     port->uptime++;
114   }
115 
116   (void) STP_stpm_update (this);
117   this->Topo_Change = _check_topoch (this);
118   if (this->Topo_Change) {
119     this->Topo_Change_Count++;
120     this->timeSince_Topo_Change = 0;
121   } else {
122     this->Topo_Change_Count = 0;
123     this->timeSince_Topo_Change++;
124   }
125 }
126 
127 STPM_T*
128 STP_stpm_create (int vlan_id, char* name)
129 {
130   STPM_T* this;
131 
132   STP_NEW_IN_LIST(this, STPM_T, bridges, "stp instance");
133 
134   this->admin_state = STP_DISABLED;
135 
136   this->vlan_id = vlan_id;
137   if (name) {
138     STP_STRDUP(this->name, name, "stp bridge name");
139   }
140 
141   this->machines = NULL;
142   this->ports = NULL;
143 
144   STP_STATE_MACH_IN_LIST(rolesel);
145 
146 #ifdef STP_DBG
147   /* this->rolesel->debug = 2;  */
148 #endif
149 
150   return this;
151 }
152 
153 int
154 STP_stpm_enable (STPM_T* this, UID_STP_MODE_T admin_state)
155 {
156   int rc = 0;
157 
158   if (admin_state == this->admin_state) {
159     /* nothing to do :) */
160     return 0;
161   }
162 
163   if (STP_ENABLED == admin_state) {
164     if (this->ports)
165       rc = STP_stpm_start (this);
166     this->admin_state = admin_state;
167   } else {
168     this->admin_state = admin_state;
169     STP_stpm_stop (this);
170   }
171 
172   return rc;
173 }
174 
175 void
176 STP_stpm_delete (STPM_T* this)
177 {
178   register STPM_T*       tmp;
179   register STPM_T*       prev;
180   register STATE_MACH_T* stater;
181   register PORT_T*       port;
182   register void*         pv;
183 
184   (void) STP_stpm_enable (this, STP_DISABLED);
185 
186   for (stater = this->machines; stater; ) {
187     pv = (void*) stater->next;
188     STP_state_mach_delete (stater);
189     this->machines = stater = (STATE_MACH_T*) pv;
190   }
191 
192   for (port = this->ports; port; ) {
193     pv = (void*) port->next;
194     STP_port_delete (port);
195     this->ports = port = (PORT_T*) pv;
196   }
197 
198   prev = NULL;
199   for (tmp = bridges; tmp; tmp = tmp->next) {
200     if (tmp->vlan_id == this->vlan_id) {
201       if (prev) {
202         prev->next = this->next;
203       } else {
204         bridges = this->next;
205       }
206 
207       if (this->name)
208         STP_FREE(this->name, "stp bridge name");
209       STP_FREE(this, "stp instance");
210       break;
211     }
212     prev = tmp;
213   }
214 }
215 
216 int
217 STP_stpm_start (STPM_T* this)
218 {
219   register PORT_T* port;
220 
221   if (! this->ports) { /* there are not any ports :( */
222     return STP_There_Are_No_Ports;
223   }
224 
225   if (! STP_compute_bridge_id (this)) {/* can't compute bridge id ? :( */
226     return STP_Cannot_Compute_Bridge_Prio;
227   }
228 
229   /* check, that the stpm has unique bridge Id */
230   if (0 != STP_stpm_check_bridge_priority (this)) {
231     /* there is an enabled bridge with same ID :( */
232     return STP_Invalid_Bridge_Priority;
233   }
234 
235   _stp_stpm_init_data (this);
236 
237   for (port = this->ports; port; port = port->next) {
238     STP_port_init (port, this, True);
239   }
240 
241 #ifndef STRONGLY_SPEC_802_1W
242   /* A. see comment near STRONGLY_SPEC_802_1W in topoch.c */
243   /* B. port=0 here means: delete for all ports */
244 #ifdef STP_DBG
245   stp_trace("%s (all, start stpm)",
246         "clearFDB");
247 #endif
248 
249   STP_OUT_flush_lt (0, this->vlan_id, LT_FLASH_ONLY_THE_PORT, "start stpm");
250 #endif
251 
252   (void) _stp_stpm_iterate_machines (this, _stp_stpm_init_machine, False);
253   (void) STP_stpm_update (this);
254 
255   return 0;
256 }
257 
258 /* ARGSUSED */
259 void
260 STP_stpm_stop (STPM_T* this)
261 {
262 }
263 
264 int
265 STP_stpm_update (STPM_T* this) /* returns number of loops */
266 {
267   register Bool     need_state_change;
268   register int      number_of_loops = 0;
269 
270   need_state_change = False;
271 
272   for (;;) {/* loop until not need changes */
273     need_state_change = _stp_stpm_iterate_machines (this,
274                                                    STP_check_condition,
275                                                    True);
276     if (! need_state_change) break;
277 
278     number_of_loops++;
279     /* here we know, that at least one stater must be
280        updated (it has changed state) */
281     number_of_loops += _stp_stpm_iterate_machines (this,
282                                                   STP_change_state,
283                                                   False);
284 
285   }
286 
287   return number_of_loops;
288 }
289 
290 BRIDGE_ID *
291 STP_compute_bridge_id (STPM_T* this)
292 {
293   register PORT_T* port;
294   register PORT_T* min_num_port = NULL;
295   int              port_index = 0;
296 
297   for (port = this->ports; port; port = port->next) {
298     if (! port_index || port->port_index < port_index) {
299       min_num_port = port;
300       port_index = port->port_index;
301     }
302   }
303 
304   if (! min_num_port) return NULL; /* IMHO, it may not be */
305 
306   STP_OUT_get_port_mac (min_num_port->port_index, this->BrId.addr);
307 
308   return &this->BrId;
309 }
310 
311 STPM_T*
312 STP_stpm_get_the_list (void)
313 {
314   return bridges;
315 }
316 
317 void
318 STP_stpm_update_after_bridge_management (STPM_T* this)
319 {
320   register PORT_T* port;
321 
322   for (port = this->ports; port; port = port->next) {
323     port->reselect = True;
324     port->selected = False;
325   }
326 }
327 
328 int
329 STP_stpm_check_bridge_priority (STPM_T* this)
330 {
331   register STPM_T* oth;
332 
333   for (oth = bridges; oth; oth = oth->next) {
334     if (STP_ENABLED == oth->admin_state && oth != this &&
335         ! STP_VECT_compare_bridge_id (&this->BrId, &oth->BrId)) {
336       return STP_Invalid_Bridge_Priority;
337     }
338   }
339 
340   return 0;
341 }
342 
343 const char*
344 STP_stpm_get_port_name_by_id (STPM_T* this, PORT_ID port_id)
345 {
346   register PORT_T* port;
347 
348   for (port = this->ports; port; port = port->next) {
349     if (port_id == port->port_id) {
350         return port->port_name;
351     }
352   }
353 
354   return "Undef?";
355 }
356 
357 
358 
359 
360 
361