xref: /illumos-gate/usr/src/lib/librstp/common/topoch.c (revision 4eaa471005973e11a6110b69fe990530b3b95a38)
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 /* Topolgy Change state machine : 17.25 */
24 
25 #include "base.h"
26 #include "stpm.h"
27 #include "stp_to.h" /* for STP_OUT_flush_lt */
28 
29 #define STATES { \
30   CHOOSE(INIT),             \
31   CHOOSE(INACTIVE),         \
32   CHOOSE(TCACTIVE),         \
33   CHOOSE(DETECTED),         \
34   CHOOSE(NOTIFIED_TC),          \
35   CHOOSE(PROPAGATING),          \
36   CHOOSE(ACKNOWLEDGED),         \
37   CHOOSE(NOTIFIED_TCN)         \
38 }
39 
40 #define GET_STATE_NAME STP_topoch_get_state_name
41 #include "choose.h"
42 
43 #ifndef STRONGLY_SPEC_802_1W
44 /*
45  * In many kinds of hardware the function
46  * STP_OUT_flush_lt is a) is very hard and b) cannot
47  * delete learning emtries per port. The alternate
48  * method may be used: we don't care operEdge flag here,
49  * but clean learning table once for TopologyChange
50  * for all ports, except the received port. I am ready to discuss :(
51  * See below word STRONGLY_SPEC_802_1W
52  */
53 #else
54 static Bool
55 flush (STATE_MACH_T *this, char* reason) /* 17.19.9 */
56 {
57   register PORT_T* port = this->owner.port;
58   Bool bret;
59 
60   if (port->operEdge) return True;
61   if (this->debug) {
62     stp_trace("%s (%s, %s, %s, '%s')",
63         "flush", port->port_name, port->owner->name,
64         LT_FLASH_ONLY_THE_PORT == type ? "this port" : "other ports",
65         reason);
66   }
67 
68   bret = STP_OUT_flush_lt (port->port_index, port->owner->vlan_id,
69                            LT_FLASH_ONLY_THE_PORT, reason);
70 }
71 #endif
72 
73 static void
74 setTcPropBridge (STATE_MACH_T* this, char* reason) /* 17.19.14 */
75 {
76   register PORT_T* port = this->owner.port;
77   register PORT_T* tmp;
78 
79   for (tmp = port->owner->ports; tmp; tmp = tmp->next) {
80     if (tmp->port_index != port->port_index)
81       tmp->tcProp = True;
82   }
83 
84 #ifndef STRONGLY_SPEC_802_1W
85 #ifdef STP_DBG
86   if (this->debug) {
87     stp_trace("%s (%s, %s, %s, '%s')",
88         "clearFDB", port->port_name, port->owner->name,
89         "other ports", reason);
90   }
91 #endif
92 
93   STP_OUT_flush_lt (port->port_index, port->owner->vlan_id,
94                     LT_FLASH_ALL_PORTS_EXCLUDE_THIS, reason);
95 #endif
96 }
97 
98 static unsigned int
99 newTcWhile (STATE_MACH_T* this) /* 17.19.7 */
100 {
101   register PORT_T* port = this->owner.port;
102 
103   if (port->sendRSTP && port->operPointToPointMac) {
104     return 2 * port->owner->rootTimes.HelloTime;
105   }
106   return port->owner->rootTimes.MaxAge;
107 }
108 
109 void
110 STP_topoch_enter_state (STATE_MACH_T* this)
111 {
112   register PORT_T*      port = this->owner.port;
113 
114   switch (this->State) {
115     case BEGIN:
116     case INIT:
117 #ifdef STRONGLY_SPEC_802_1W
118       flush (this, "topoch INIT");
119 #endif
120       port->tcWhile = 0;
121       port->tc =
122       port->tcProp =
123       port->tcAck = False;
124       break;
125     case INACTIVE:
126       port->rcvdTc =
127       port->rcvdTcn =
128       port->rcvdTcAck = port->tc = port->tcProp = False;
129       break;
130     case TCACTIVE:
131       break;
132     case DETECTED:
133       port->tcWhile = newTcWhile (this);
134 #ifdef STP_DBG
135   if (this->debug)
136     stp_trace("DETECTED: tcWhile=%d on port %s",
137         port->tcWhile, port->port_name);
138 #endif
139       setTcPropBridge (this, "DETECTED");
140       port->tc = False;
141       break;
142     case NOTIFIED_TC:
143       port->rcvdTcn = port->rcvdTc = False;
144       if (port->role == DesignatedPort) {
145         port->tcAck = True;
146       }
147       setTcPropBridge (this, "NOTIFIED_TC");
148       break;
149     case PROPAGATING:
150       port->tcWhile = newTcWhile (this);
151 #ifdef STP_DBG
152   if (this->debug)
153     stp_trace("PROPAGATING: tcWhile=%d on port %s",
154         port->tcWhile, port->port_name);
155 #endif
156 #ifdef STRONGLY_SPEC_802_1W
157       flush (this, "topoch PROPAGATING");
158 #endif
159       port->tcProp = False;
160       break;
161     case ACKNOWLEDGED:
162       port->tcWhile = 0;
163 #ifdef STP_DBG
164   if (this->debug)
165     stp_trace("ACKNOWLEDGED: tcWhile=%d on port %s",
166         port->tcWhile, port->port_name);
167 #endif
168       port->rcvdTcAck = False;
169       break;
170     case NOTIFIED_TCN:
171       port->tcWhile = newTcWhile (this);
172 #ifdef STP_DBG
173   if (this->debug)
174     stp_trace("NOTIFIED_TCN: tcWhile=%d on port %s",
175         port->tcWhile, port->port_name);
176 #endif
177       break;
178   };
179 }
180 
181 Bool
182 STP_topoch_check_conditions (STATE_MACH_T* this)
183 {
184   register PORT_T*      port = this->owner.port;
185 
186   if (BEGIN == this->State) {
187     return STP_hop_2_state (this, INIT);
188   }
189 
190   switch (this->State) {
191     case INIT:
192       return STP_hop_2_state (this, INACTIVE);
193     case INACTIVE:
194       if (port->role == RootPort || port->role == DesignatedPort)
195         return STP_hop_2_state (this, TCACTIVE);
196       if (port->rcvdTc || port->rcvdTcn || port->rcvdTcAck ||
197           port->tc || port->tcProp)
198         return STP_hop_2_state (this, INACTIVE);
199       break;
200     case TCACTIVE:
201       if (port->role != RootPort && (port->role != DesignatedPort))
202         return STP_hop_2_state (this, INIT);
203       if (port->tc)
204         return STP_hop_2_state (this, DETECTED);
205       if (port->rcvdTcn)
206         return STP_hop_2_state (this, NOTIFIED_TCN);
207       if (port->rcvdTc)
208         return STP_hop_2_state (this, NOTIFIED_TC);
209       if (port->tcProp && !port->operEdge)
210         return STP_hop_2_state (this, PROPAGATING);
211       if (port->rcvdTcAck)
212         return STP_hop_2_state (this, ACKNOWLEDGED);
213       break;
214     case DETECTED:
215       return STP_hop_2_state (this, TCACTIVE);
216     case NOTIFIED_TC:
217       return STP_hop_2_state (this, TCACTIVE);
218     case PROPAGATING:
219       return STP_hop_2_state (this, TCACTIVE);
220     case ACKNOWLEDGED:
221       return STP_hop_2_state (this, TCACTIVE);
222     case NOTIFIED_TCN:
223       return STP_hop_2_state (this, NOTIFIED_TC);
224   };
225   return False;
226 }
227 
228