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