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