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 #include "base.h"
24 #include "stpm.h"
25 #include "stp_vectors.h"
26
27 /* The Port Information State Machine : 17.21 */
28
29 #define STATES { \
30 CHOOSE(DISABLED), \
31 CHOOSE(ENABLED), \
32 CHOOSE(AGED), \
33 CHOOSE(UPDATE), \
34 CHOOSE(CURRENT), \
35 CHOOSE(RECEIVE), \
36 CHOOSE(SUPERIOR), \
37 CHOOSE(REPEAT), \
38 CHOOSE(AGREEMENT) \
39 }
40
41 #define GET_STATE_NAME STP_info_get_state_name
42 #include "choose.h"
43
44 #if 0 /* for debug */
45 void
46 _stp_dump (char* title, unsigned char* buff, int len)
47 {
48 register int iii;
49
50 stp_trace ("\n%s:", title);
51 for (iii = 0; iii < len; iii++) {
52 if (! (iii % 24)) stp_trace ("\n%6d:", iii);
53 if (! (iii % 8)) stp_trace (" ");
54 stp_trace ("%02lx", (unsigned long) buff[iii]);
55 }
56 stp_trace ("\n");
57 }
58 #endif
59
60 static RCVD_MSG_T
rcvBpdu(STATE_MACH_T * this)61 rcvBpdu (STATE_MACH_T* this)
62 {/* 17.19.8 */
63 int bridcmp;
64 register PORT_T* port = this->owner.port;
65
66 if (port->msgBpduType == BPDU_TOPO_CHANGE_TYPE) {
67 #ifdef STP_DBG
68 if (this->debug) {
69 stp_trace ("%s", "rcvBpdu: OtherMsg:BPDU_TOPO_CHANGE_TYPE");
70 }
71 #endif
72 return OtherMsg;
73 }
74
75 port->msgPortRole = RSTP_PORT_ROLE_UNKN;
76
77 if (BPDU_RSTP == port->msgBpduType) {
78 port->msgPortRole = (port->msgFlags & PORT_ROLE_MASK) >> PORT_ROLE_OFFS;
79 }
80
81 if (RSTP_PORT_ROLE_DESGN == port->msgPortRole ||
82 BPDU_CONFIG_TYPE == port->msgBpduType) {
83 bridcmp = STP_VECT_compare_vector (&port->msgPrio, &port->portPrio);
84
85 if (bridcmp < 0 ||
86 (! STP_VECT_compare_bridge_id (&port->msgPrio.design_bridge,
87 &port->portPrio.design_bridge) &&
88 port->msgPrio.design_port == port->portPrio.design_port &&
89 STP_compare_times (&port->msgTimes, &port->portTimes))) {
90 #ifdef STP_DBG
91 if (this->debug) {
92 stp_trace ("rcvBpdu: SuperiorDesignateMsg:bridcmp=%d", (int) bridcmp);
93 }
94 #endif
95 return SuperiorDesignateMsg;
96 }
97 }
98
99 if (BPDU_CONFIG_TYPE == port->msgBpduType ||
100 RSTP_PORT_ROLE_DESGN == port->msgPortRole) {
101 if (! STP_VECT_compare_vector (&port->msgPrio,
102 &port->portPrio) &&
103 ! STP_compare_times (&port->msgTimes, &port->portTimes)) {
104 #ifdef STP_DBG
105 if (this->debug) {
106 stp_trace ("%s", "rcvBpdu: RepeatedDesignateMsg");
107 }
108 #endif
109 return RepeatedDesignateMsg;
110 }
111 }
112
113 if (RSTP_PORT_ROLE_ROOT == port->msgBpduType &&
114 port->operPointToPointMac &&
115 ! STP_VECT_compare_bridge_id (&port->msgPrio.design_bridge,
116 &port->portPrio.design_bridge) &&
117 AGREEMENT_BIT & port->msgFlags) {
118 #ifdef STP_DBG
119 if (this->debug) {
120 stp_trace ("%s", "rcvBpdu: ConfirmedRootMsg");
121 }
122 #endif
123 return ConfirmedRootMsg;
124 }
125
126 #ifdef STP_DBG
127 if (this->debug) {
128 if (RSTP_PORT_ROLE_ROOT == port->msgBpduType) {
129 if (!port->operPointToPointMac) {
130 stp_trace("rcvBpdu: OtherMsg: not point-to-point MAC");
131 } else if (STP_VECT_compare_bridge_id (&port->msgPrio.design_bridge,
132 &port->portPrio.design_bridge)) {
133 STP_VECT_br_id_print("rcvBpdu: OtherMsg: msgPrio", &port->msgPrio.design_bridge, True);
134 STP_VECT_br_id_print("rcvBpdu: portPrio", &port->portPrio.design_bridge, True);
135 } else {
136 stp_trace("rcvBpdu: OtherMsg: agreement bit not set");
137 }
138 } else {
139 stp_trace ("rcvBpdu: OtherMsg: type %d", port->msgBpduType);
140 }
141 }
142 #endif
143 return OtherMsg;
144 }
145
146 /* ARGSUSED */
147 static Bool
recordProposed(STATE_MACH_T * this,char * reason)148 recordProposed (STATE_MACH_T* this, char* reason)
149 {/* 17.19.9 */
150 register PORT_T* port = this->owner.port;
151
152 if (RSTP_PORT_ROLE_DESGN == port->msgPortRole &&
153 (PROPOSAL_BIT & port->msgFlags) &&
154 port->operPointToPointMac) {
155 return True;
156 }
157 return False;
158 }
159
160 static void
setTcFlags(STATE_MACH_T * this)161 setTcFlags (STATE_MACH_T* this)
162 {/* 17.19.13 */
163 register PORT_T* port = this->owner.port;
164
165 if (BPDU_TOPO_CHANGE_TYPE == port->msgBpduType) {
166 #ifdef STP_DBG
167 if (this->debug) {
168 stp_trace ("port %s rx rcvdTcn", port->port_name);
169 }
170 #endif
171 port->rcvdTcn = True;
172 } else {
173 if (TOPOLOGY_CHANGE_BIT & port->msgFlags) {
174 #ifdef STP_DBG
175 if (this->debug) {
176 stp_trace ("(%s-%s) rx rcvdTc 0X%lx",
177 port->owner->name, port->port_name,
178 (unsigned long) port->msgFlags);
179 }
180 #endif
181 port->rcvdTc = True;
182 }
183 if (TOPOLOGY_CHANGE_ACK_BIT & port->msgFlags) {
184 #ifdef STP_DBG
185 if (this->debug) {
186 stp_trace ("port %s rx rcvdTcAck 0X%lx",
187 port->port_name,
188 (unsigned long) port->msgFlags);
189 }
190 #endif
191 port->rcvdTcAck = True;
192 }
193 }
194 }
195
196 static void
updtBPDUVersion(STATE_MACH_T * this)197 updtBPDUVersion (STATE_MACH_T* this)
198 {/* 17.19.18 */
199 register PORT_T* port = this->owner.port;
200
201 if (BPDU_TOPO_CHANGE_TYPE == port->msgBpduType) {
202 port->rcvdSTP = True;
203 }
204
205 if (port->msgBpduVersion < 2) {
206 port->rcvdSTP = True;
207 }
208
209 if (BPDU_RSTP == port->msgBpduType) {
210 /* port->port->owner->ForceVersion >= NORMAL_RSTP
211 we have checked in STP_info_rx_bpdu */
212 port->rcvdRSTP = True;
213 }
214 }
215
216 static void
updtRcvdInfoWhile(STATE_MACH_T * this)217 updtRcvdInfoWhile (STATE_MACH_T* this)
218 {/* 17.19.19 */
219 register int eff_age, dm, dt;
220 register int hello3;
221 register PORT_T* port = this->owner.port;
222
223 eff_age = ( + port->portTimes.MaxAge) / 16;
224 if (eff_age < 1) eff_age = 1;
225 eff_age += port->portTimes.MessageAge;
226
227 if (eff_age <= port->portTimes.MaxAge) {
228 hello3 = 3 * port->portTimes.HelloTime;
229 dm = port->portTimes.MaxAge - eff_age;
230 if (dm > hello3)
231 dt = hello3;
232 else
233 dt = dm;
234 port->rcvdInfoWhile = dt;
235 /****
236 stp_trace ("ma=%d eff_age=%d dm=%d dt=%d p=%s",
237 (int) port->portTimes.MessageAge,
238 (int) eff_age, (int) dm, (int) dt, port->port_name);
239 ****/
240 } else {
241 port->rcvdInfoWhile = 0;
242 /****/
243 #ifdef STP_DBG
244 /*if (this->debug) */
245 {
246 stp_trace ("port %s: MaxAge=%d MessageAge=%d HelloTime=%d rcvdInfoWhile=null !",
247 port->port_name,
248 (int) port->portTimes.MaxAge,
249 (int) port->portTimes.MessageAge,
250 (int) port->portTimes.HelloTime);
251 }
252 #endif
253 /****/
254 }
255 }
256
257
258 /* ARGSUSED */
259 void
STP_info_rx_bpdu(PORT_T * port,struct stp_bpdu_t * bpdu,size_t len)260 STP_info_rx_bpdu (PORT_T* port, struct stp_bpdu_t* bpdu, size_t len)
261 {
262 #if 0
263 _stp_dump ("\nall BPDU", ((unsigned char*) bpdu) - 12, len + 12);
264 _stp_dump ("ETH_HEADER", (unsigned char*) &bpdu->eth, 5);
265 _stp_dump ("BPDU_HEADER", (unsigned char*) &bpdu->hdr, 4);
266 stp_trace ("protocol=%02x%02x version=%02x bpdu_type=%02x\n",
267 bpdu->hdr.protocol[0], bpdu->hdr.protocol[1],
268 bpdu->hdr.version, bpdu->hdr.bpdu_type);
269
270 _stp_dump ("\nBPDU_BODY", (unsigned char*) &bpdu->body, sizeof (BPDU_BODY_T) + 2);
271 stp_trace ("flags=%02x\n", bpdu->body.flags);
272 _stp_dump ("root_id", bpdu->body.root_id, 8);
273 _stp_dump ("root_path_cost", bpdu->body.root_path_cost, 4);
274 _stp_dump ("bridge_id", bpdu->body.bridge_id, 8);
275 _stp_dump ("port_id", bpdu->body.port_id, 2);
276 _stp_dump ("message_age", bpdu->body.message_age, 2);
277 _stp_dump ("max_age", bpdu->body.max_age, 2);
278 _stp_dump ("hello_time", bpdu->body.hello_time, 2);
279 _stp_dump ("forward_delay", bpdu->body.forward_delay, 2);
280 _stp_dump ("ver_1_len", bpdu->ver_1_len, 2);
281 #endif
282
283 /* check bpdu type */
284 switch (bpdu->hdr.bpdu_type) {
285 case BPDU_CONFIG_TYPE:
286 port->rx_cfg_bpdu_cnt++;
287 #ifdef STP_DBG
288 if (port->info->debug)
289 stp_trace ("CfgBpdu on port %s", port->port_name);
290 #endif
291 if (port->admin_non_stp) return;
292 port->rcvdBpdu = True;
293 break;
294 case BPDU_TOPO_CHANGE_TYPE:
295 port->rx_tcn_bpdu_cnt++;
296 #ifdef STP_DBG
297 if (port->info->debug)
298 stp_trace ("TcnBpdu on port %s", port->port_name);
299 #endif
300 if (port->admin_non_stp) return;
301 port->rcvdBpdu = True;
302 port->msgBpduVersion = bpdu->hdr.version;
303 port->msgBpduType = bpdu->hdr.bpdu_type;
304 return;
305 default:
306 stp_trace ("RX undef bpdu type=%d", (int) bpdu->hdr.bpdu_type);
307 return;
308 case BPDU_RSTP:
309 port->rx_rstp_bpdu_cnt++;
310 if (port->admin_non_stp) return;
311 if (port->owner->ForceVersion >= NORMAL_RSTP) {
312 port->rcvdBpdu = True;
313 } else {
314 return;
315 }
316 #ifdef STP_DBG
317 if (port->info->debug)
318 stp_trace ("BPDU_RSTP on port %s", port->port_name);
319 #endif
320 break;
321 }
322
323 port->msgBpduVersion = bpdu->hdr.version;
324 port->msgBpduType = bpdu->hdr.bpdu_type;
325 port->msgFlags = bpdu->body.flags;
326
327 /* 17.18.11 */
328 STP_VECT_get_vector (&bpdu->body, &port->msgPrio);
329 port->msgPrio.bridge_port = port->port_id;
330
331 /* 17.18.12 */
332 STP_get_times (&bpdu->body, &port->msgTimes);
333
334 /* 17.18.25, 17.18.26 : see setTcFlags() */
335 }
336
STP_info_enter_state(STATE_MACH_T * this)337 void STP_info_enter_state (STATE_MACH_T* this)
338 {
339 register PORT_T* port = this->owner.port;
340
341 switch (this->State) {
342 case BEGIN:
343 port->rcvdMsg = OtherMsg;
344 port->msgBpduType = (unsigned char)-1;
345 port->msgPortRole = RSTP_PORT_ROLE_UNKN;
346 port->msgFlags = 0;
347
348 /* clear port statistics */
349 port->rx_cfg_bpdu_cnt =
350 port->rx_rstp_bpdu_cnt =
351 port->rx_tcn_bpdu_cnt = 0;
352 /* FALLTHRU */
353 case DISABLED:
354 port->rcvdBpdu = port->rcvdRSTP = port->rcvdSTP = False;
355 port->updtInfo = port->proposing = False; /* In DISABLED */
356 port->agreed = port->proposed = False;
357 port->rcvdInfoWhile = 0;
358 port->infoIs = Disabled;
359 port->reselect = True;
360 port->selected = False;
361 break;
362 case ENABLED: /* IEEE 802.1y, 17.21, Z.14 */
363 STP_VECT_copy (&port->portPrio, &port->designPrio);
364 STP_copy_times (&port->portTimes, &port->designTimes);
365 break;
366 case AGED:
367 port->infoIs = Aged;
368 port->reselect = True;
369 port->selected = False;
370 break;
371 case UPDATE:
372 STP_VECT_copy (&port->portPrio, &port->designPrio);
373 STP_copy_times (&port->portTimes, &port->designTimes);
374 port->updtInfo = False;
375 port->agreed = port->synced = False; /* In UPDATE */
376 port->proposed = port->proposing = False; /* in UPDATE */
377 port->infoIs = Mine;
378 port->newInfo = True;
379 #ifdef STP_DBG
380 if (this->debug) {
381 STP_VECT_br_id_print ("updated: portPrio.design_bridge",
382 &port->portPrio.design_bridge, True);
383 STP_VECT_br_id_print ("updated: portPrio.root_bridge",
384 &port->portPrio.root_bridge, True);
385 }
386 #endif
387 break;
388 case CURRENT:
389 break;
390 case RECEIVE:
391 port->rcvdMsg = rcvBpdu (this);
392 updtBPDUVersion (this);
393 setTcFlags (this);
394 port->rcvdBpdu = False;
395 break;
396 case SUPERIOR:
397 STP_VECT_copy (&port->portPrio, &port->msgPrio);
398 STP_copy_times (&port->portTimes, &port->msgTimes);
399 updtRcvdInfoWhile (this);
400 #if 1 /* due 802.1y, Z.7 */
401 port->agreed = False; /* deleted due 802.y in SUPERIOR */
402 port->synced = False; /* due 802.y deleted in SUPERIOR */
403 #endif
404 port->proposing = False; /* in SUPERIOR */
405 port->proposed = recordProposed (this, "SUPERIOR");
406 port->infoIs = Received;
407 port->reselect = True;
408 port->selected = False;
409 #ifdef STP_DBG
410 if (this->debug) {
411 STP_VECT_br_id_print ("stored: portPrio.design_bridge",
412 &port->portPrio.design_bridge, True);
413 STP_VECT_br_id_print ("stored: portPrio.root_bridge",
414 &port->portPrio.root_bridge, True);
415 stp_trace ("proposed=%d on port %s",
416 (int) port->proposed, port->port_name);
417 }
418 #endif
419 break;
420 case REPEAT:
421 port->proposed = recordProposed (this, "REPEAT");
422 updtRcvdInfoWhile (this);
423 break;
424 case AGREEMENT:
425 #ifdef STP_DBG
426 if (port->roletrns->debug) {
427 stp_trace ("(%s-%s) rx AGREEMENT flag !",
428 port->owner->name, port->port_name);
429 }
430 #endif
431
432 port->agreed = True;
433 port->proposing = False; /* In AGREEMENT */
434 break;
435 }
436
437 }
438
STP_info_check_conditions(STATE_MACH_T * this)439 Bool STP_info_check_conditions (STATE_MACH_T* this)
440 {
441 register PORT_T* port = this->owner.port;
442
443 if ((! port->portEnabled && port->infoIs != Disabled) || BEGIN == this->State) {
444 return STP_hop_2_state (this, DISABLED);
445 }
446
447 switch (this->State) {
448 case DISABLED:
449 if (port->updtInfo) {
450 return STP_hop_2_state (this, DISABLED);
451 }
452 if (port->portEnabled && port->selected) {
453 return STP_hop_2_state (this, ENABLED);
454 }
455 if (port->rcvdBpdu) {
456 return STP_hop_2_state (this, DISABLED);
457 }
458 break;
459 case ENABLED: /* IEEE 802.1y, 17.21, Z.14 */
460 return STP_hop_2_state (this, AGED);
461 case AGED:
462 if (port->selected && port->updtInfo) {
463 return STP_hop_2_state (this, UPDATE);
464 }
465 break;
466 case UPDATE:
467 return STP_hop_2_state (this, CURRENT);
468 case CURRENT:
469 if (port->selected && port->updtInfo) {
470 return STP_hop_2_state (this, UPDATE);
471 }
472
473 if (Received == port->infoIs &&
474 ! port->rcvdInfoWhile &&
475 ! port->updtInfo &&
476 ! port->rcvdBpdu) {
477 return STP_hop_2_state (this, AGED);
478 }
479 if (port->rcvdBpdu && !port->updtInfo) {
480 return STP_hop_2_state (this, RECEIVE);
481 }
482 break;
483 case RECEIVE:
484 switch (port->rcvdMsg) {
485 case SuperiorDesignateMsg:
486 return STP_hop_2_state (this, SUPERIOR);
487 case RepeatedDesignateMsg:
488 return STP_hop_2_state (this, REPEAT);
489 case ConfirmedRootMsg:
490 return STP_hop_2_state (this, AGREEMENT);
491 default:
492 return STP_hop_2_state (this, CURRENT);
493 }
494 case SUPERIOR:
495 return STP_hop_2_state (this, CURRENT);
496 case REPEAT:
497 return STP_hop_2_state (this, CURRENT);
498 case AGREEMENT:
499 return STP_hop_2_state (this, CURRENT);
500 }
501
502 return False;
503 }
504