• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ustp/mstp.c

  1 /*
  2  * mstp.c      State machines from IEEE 802.1Q-2005
  3  *
  4  *  This program is free software; you can redistribute it and/or
  5  *  modify it under the terms of the GNU General Public License
  6  *  as published by the Free Software Foundation; either version
  7  *  2 of the License, or (at your option) any later version.
  8  *
  9  * Authors: Vitalii Demianets <dvitasgs@gmail.com>
 10  */
 11 
 12 /* NOTE: The standard messes up Hello_Time timer management.
 13  * The portTimes and msgTimes structures have it, while
 14  * designatedTimes, rootTimes and BridgeTimes do not!
 15  * And there are places, where standard says:
 16  * "portTimes = designatedTimes" (13.32)
 17  * "rootTimes = portTimes" (13.26.23)
 18  * ---- Bad IEEE! ----
 19  * For now I decide: All structures will hold Hello_Time,
 20  * because in 802.1D they do.
 21  * Besides, it is necessary for compatibility with old STP implementations.
 22  */
 23 
 24 /* 802.1Q-2005 does not define but widely use variable name newInfoXst.
 25  * From the 802.1s I can guess that it means:
 26  *   - "newInfo" when tree is CIST;
 27  *   - "newInfoMsti" when tree is not CIST (is MSTI).
 28  * But that is only a guess and I could be wrong here ;)
 29  */
 30 
 31 /* Important coding convention:
 32  *   All functions that have dry_run argument must follow the
 33  *   return value convention:
 34  *      They should return true if state change detected during dry run.
 35  *      Otherwise (!dry_run || !state_change) they return false.
 36  */
 37 
 38 #include <config.h>
 39 
 40 #include <string.h>
 41 #include <netinet/in.h>
 42 #include <linux/if_bridge.h>
 43 #include <asm/byteorder.h>
 44 #include <time.h>
 45 #include <sys/time.h>
 46 
 47 #include "mstp.h"
 48 #include "log.h"
 49 #include "driver.h"
 50 
 51 static void PTSM_tick(port_t *prt);
 52 static bool TCSM_run(per_tree_port_t *ptp, bool dry_run);
 53 static void BDSM_begin(port_t *prt);
 54 static void br_state_machines_begin(bridge_t *br);
 55 static void prt_state_machines_begin(port_t *prt);
 56 static void tree_state_machines_begin(tree_t *tree);
 57 static void br_state_machines_run(bridge_t *br);
 58 static void updtbrAssuRcvdInfoWhile(port_t *prt);
 59 
 60 #define FOREACH_PORT_IN_BRIDGE(port, bridge) \
 61     list_for_each_entry((port), &(bridge)->ports, br_list)
 62 #define FOREACH_TREE_IN_BRIDGE(tree, bridge) \
 63     list_for_each_entry((tree), &(bridge)->trees, bridge_list)
 64 #define FOREACH_PTP_IN_TREE(ptp, tree) \
 65     list_for_each_entry((ptp), &(tree)->ports, tree_list)
 66 #define FOREACH_PTP_IN_PORT(ptp, port) \
 67     list_for_each_entry((ptp), &(port)->trees, port_list)
 68 
 69 /* 17.20.11 of 802.1D */
 70 #define rstpVersion(br) ((br)->ForceProtocolVersion >= protoRSTP)
 71 /* Bridge assurance is operational only when NetworkPort type is configured
 72  * and the operation status is pointToPoint and version is RSTP/MSTP
 73  */
 74 #define assurancePort(prt) ((prt)->NetworkPort && (prt)->operPointToPointMAC \
 75                             && (prt)->sendRSTP)
 76 /*
 77  * Recalculate configuration digest. (13.7)
 78  */
 79 static void RecalcConfigDigest(bridge_t *br)
 80 {
 81     __be16 vid2mstid[MAX_VID + 2];
 82     unsigned char mstp_key[] = HMAC_KEY;
 83     int vid;
 84 
 85     vid2mstid[0] = vid2mstid[MAX_VID + 1] = 0;
 86     for(vid = 1; vid <= MAX_VID; ++vid)
 87         vid2mstid[vid] = br->fid2mstid[br->vid2fid[vid]];
 88 
 89     hmac_md5((void *)vid2mstid, sizeof(vid2mstid), mstp_key, sizeof(mstp_key),
 90              (caddr_t)br->MstConfigId.s.configuration_digest);
 91 }
 92 
 93 /*
 94  * 13.37.1 - Table 13-3
 95  */
 96 static __u32 compute_pcost(int speed)
 97 {
 98   /* speed is in MB/s*/
 99   if(speed > 0)
100     return (speed < 20000000) ? 20000000 / speed : 1;
101   else
102     return MAX_PATH_COST;
103 }
104 
105 static void bridge_default_internal_vars(bridge_t *br)
106 {
107     br->uptime = 0;
108 }
109 
110 static void tree_default_internal_vars(tree_t *tree)
111 {
112     /* 12.8.1.1.3.(b,c,d) */
113     tree->time_since_topology_change = 0;
114     tree->topology_change_count = 0;
115     tree->topology_change = false; /* since all tcWhile are initialized to 0 */
116     strncpy(tree->topology_change_port, "None", IFNAMSIZ);
117     strncpy(tree->last_topology_change_port, "None", IFNAMSIZ);
118 
119     /* The following are initialized in BEGIN state:
120      *  - rootPortId, rootPriority, rootTimes: in Port Role Selection SM
121      */
122 }
123 
124 static void port_default_internal_vars(port_t *prt)
125 {
126     prt->infoInternal = false;
127     prt->rcvdInternal = false;
128     prt->rcvdTcAck = false;
129     prt->rcvdTcn = false;
130     assign(prt->rapidAgeingWhile, 0u);
131     assign(prt->brAssuRcvdInfoWhile, 0u);
132     prt->BaInconsistent = false;
133     prt->num_rx_bpdu_filtered = 0;
134     prt->num_rx_bpdu = 0;
135     prt->num_rx_tcn = 0;
136     prt->num_tx_bpdu = 0;
137     prt->num_tx_tcn = 0;
138     prt->num_trans_fwd = 0;
139     prt->num_trans_blk = 0;
140 
141     /* The following are initialized in BEGIN state:
142      * - mdelayWhile. mcheck, sendRSTP: in Port Protocol Migration SM
143      * - helloWhen, newInfo, newInfoMsti, txCount: in Port Transmit SM
144      * - edgeDelayWhile, rcvdBpdu, rcvdRSTP, rcvdSTP : in Port Receive SM
145      * - operEdge: in Bridge Detection SM
146      * - tcAck: in Topology Change SM
147      */
148 }
149 
150 static void ptp_default_internal_vars(per_tree_port_t *ptp)
151 {
152     ptp->rcvdTc = false;
153     ptp->tcProp = false;
154     ptp->updtInfo = false;
155     ptp->master = false; /* 13.24.5 */
156     ptp->disputed = false;
157     assign(ptp->rcvdInfo, (port_info_t)0);
158     ptp->mastered = false;
159     memset(&ptp->msgPriority, 0, sizeof(ptp->msgPriority));
160     memset(&ptp->msgTimes, 0, sizeof(ptp->msgTimes));
161 
162     /* The following are initialized in BEGIN state:
163      * - rcvdMsg: in Port Receive SM
164      * - fdWhile, rrWhile, rbWhile, role, learn, forward,
165      *   sync, synced, reRoot: in Port Role Transitions SM
166      * - tcWhile, fdbFlush: Topology Change SM
167      * - rcvdInfoWhile, proposed, proposing, agree, agreed,
168      *   infoIs, reselect, selected: Port Information SM
169      * - forwarding, learning: Port State Transition SM
170      * - selectedRole, designatedPriority, designatedTimes: Port Role Selection SM
171      */
172 }
173 
174 static tree_t * create_tree(bridge_t *br, __u8 *macaddr, __be16 MSTID)
175 {
176     /* Initialize all fields except anchor */
177     tree_t *tree = calloc(1, sizeof(*tree));
178     if(!tree)
179     {
180         ERROR_BRNAME(br, "Out of memory");
181         return NULL;
182     }
183     tree->bridge = br;
184     tree->MSTID = MSTID;
185     INIT_LIST_HEAD(&tree->ports);
186 
187     memcpy(tree->BridgeIdentifier.s.mac_address, macaddr, ETH_ALEN);
188     /* 0x8000 = default bridge priority (17.14 of 802.1D) */
189     tree->BridgeIdentifier.s.priority = __constant_cpu_to_be16(0x8000) | MSTID;
190     assign(tree->BridgePriority.RootID, tree->BridgeIdentifier);
191     assign(tree->BridgePriority.RRootID, tree->BridgeIdentifier);
192     assign(tree->BridgePriority.DesignatedBridgeID, tree->BridgeIdentifier);
193     /* 13.23.4 */
194     assign(tree->BridgeTimes.remainingHops, br->MaxHops);
195     assign(tree->BridgeTimes.Forward_Delay, br->Forward_Delay);
196     assign(tree->BridgeTimes.Max_Age, br->Max_Age);
197     assign(tree->BridgeTimes.Message_Age, (__u8)0);
198     assign(tree->BridgeTimes.Hello_Time, br->Hello_Time);
199 
200     tree_default_internal_vars(tree);
201 
202     return tree;
203 }
204 
205 static per_tree_port_t * create_ptp(tree_t *tree, port_t *prt)
206 {
207     /* Initialize all fields except anchors */
208     per_tree_port_t *ptp = calloc(1, sizeof(*ptp));
209     if(!ptp)
210     {
211         ERROR_PRTNAME(prt->bridge, prt, "Out of memory");
212         return NULL;
213     }
214     ptp->port = prt;
215     ptp->tree = tree;
216     ptp->MSTID = tree->MSTID;
217 
218     ptp->state = BR_STATE_DISABLED;
219     /* 0x80 = default port priority (17.14 of 802.1D) */
220     ptp->portId = __constant_cpu_to_be16(0x8000) | prt->port_number;
221     assign(ptp->AdminInternalPortPathCost, 0u);
222     assign(ptp->InternalPortPathCost, compute_pcost(GET_PORT_SPEED(prt)));
223     /* 802.1Q leaves portPriority and portTimes uninitialized */
224     assign(ptp->portPriority, tree->BridgePriority);
225     assign(ptp->portTimes, tree->BridgeTimes);
226 
227     ptp->calledFromFlushRoutine = false;
228 
229     ptp_default_internal_vars(ptp);
230 
231     return ptp;
232 }
233 
234 /* External events */
235 
236 bool MSTP_IN_bridge_create(bridge_t *br, __u8 *macaddr)
237 {
238     tree_t *cist;
239 
240     if (!driver_create_bridge(br, macaddr))
241         return false;
242 
243     /* Initialize all fields except sysdeps and anchor */
244     INIT_LIST_HEAD(&br->ports);
245     INIT_LIST_HEAD(&br->trees);
246     br->bridgeEnabled = false;
247     memset(br->vid2fid, 0, sizeof(br->vid2fid));
248     memset(br->fid2mstid, 0, sizeof(br->fid2mstid));
249     assign(br->MstConfigId.s.selector, (__u8)0);
250     sprintf((char *)br->MstConfigId.s.configuration_name,
251             "%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX",
252             macaddr[0], macaddr[1], macaddr[2],
253             macaddr[3], macaddr[4], macaddr[5]);
254     assign(br->MstConfigId.s.revision_level, __constant_cpu_to_be16(0));
255     RecalcConfigDigest(br); /* set br->MstConfigId.s.configuration_digest */
256     br->ForceProtocolVersion = protoRSTP;
257     assign(br->MaxHops, (__u8)20);       /* 13.37.3 */
258     assign(br->Forward_Delay, (__u8)15); /* 17.14 of 802.1D */
259     assign(br->Max_Age, (__u8)20);       /* 17.14 of 802.1D */
260     assign(br->Transmit_Hold_Count, 6u); /* 17.14 of 802.1D */
261     assign(br->Migrate_Time, 3u); /* 17.14 of 802.1D */
262     assign(br->Ageing_Time, 300u);/* 8.8.3 Table 8-3 */
263     assign(br->Hello_Time, (__u8)2);     /* 17.14 of 802.1D */
264 
265     bridge_default_internal_vars(br);
266 
267     /* Create CIST */
268     if(!(cist = create_tree(br, macaddr, 0)))
269         return false;
270     list_add_tail(&cist->bridge_list, &br->trees);
271 
272     return true;
273 }
274 
275 bool MSTP_IN_port_create_and_add_tail(port_t *prt, __u16 portno)
276 {
277     tree_t *tree;
278     per_tree_port_t *ptp, *nxt;
279     bridge_t *br = prt->bridge;
280 
281     if (!driver_create_port(prt, portno))
282         return false;
283 
284     /* Initialize all fields except sysdeps and bridge */
285     INIT_LIST_HEAD(&prt->trees);
286     prt->port_number = __cpu_to_be16(portno);
287 
288     assign(prt->AdminExternalPortPathCost, 0u);
289     /* Default for operP2P is false because by default AdminP2P
290      * says to auto-detect p2p state, and it is derived from duplex
291      * and initially port is in down state and in this down state
292      * duplex is set to false (half) */
293     prt->AdminP2P = p2pAuto;
294     prt->operPointToPointMAC = false;
295     prt->portEnabled = false;
296     prt->restrictedRole = false; /* 13.25.14 */
297     prt->restrictedTcn = false; /* 13.25.15 */
298     assign(prt->ExternalPortPathCost, MAX_PATH_COST); /* 13.37.1 */
299     prt->AdminEdgePort = false; /* 13.25 */
300     prt->AutoEdge = true;       /* 13.25 */
301     prt->BpduGuardPort = false;
302     prt->BpduGuardError = false;
303     prt->NetworkPort = false;
304     prt->dontTxmtBpdu = false;
305     prt->bpduFilterPort = false;
306     prt->deleted = false;
307 
308     port_default_internal_vars(prt);
309 
310     /* Create PerTreePort structures for all existing trees */
311     FOREACH_TREE_IN_BRIDGE(tree, br)
312     {
313         if(!(ptp = create_ptp(tree, prt)))
314         {
315             /* Remove and free all previously created entries in port's list */
316             list_for_each_entry_safe(ptp, nxt, &prt->trees, port_list)
317             {
318                 list_del(&ptp->port_list);
319                 list_del(&ptp->tree_list);
320                 free(ptp);
321             }
322             return false;
323         }
324         list_add_tail(&ptp->port_list, &prt->trees);
325         list_add_tail(&ptp->tree_list, &tree->ports);
326     }
327 
328     /* Add new port to the tail of the list in the bridge */
329     /* NOTE: if one wants add port NOT to the tail of the list of ports,
330      * one should revise above loop (FOREACH_TREE_IN_BRIDGE)
331      * because it heavily depends on the fact that port is added to the tail.
332      */
333     list_add_tail(&prt->br_list, &br->ports);
334 
335     prt_state_machines_begin(prt);
336     return true;
337 }
338 
339 void MSTP_IN_delete_port(port_t *prt)
340 {
341     per_tree_port_t *ptp, *nxt;
342     bridge_t *br = prt->bridge;
343 
344     driver_delete_port(prt);
345 
346     prt->deleted = true;
347     if(prt->portEnabled)
348     {
349         prt->portEnabled = false;
350         br_state_machines_run(br);
351     }
352 
353     list_for_each_entry_safe(ptp, nxt, &prt->trees, port_list)
354     {
355         list_del(&ptp->port_list);
356         list_del(&ptp->tree_list);
357         free(ptp);
358     }
359 
360     list_del(&prt->br_list);
361     br_state_machines_run(br);
362 }
363 
364 void MSTP_IN_delete_bridge(bridge_t *br)
365 {
366     tree_t *tree, *nxt_tree;
367     port_t *prt, *nxt_prt;
368 
369     driver_delete_bridge(br);
370 
371     br->bridgeEnabled = false;
372 
373     /* We SHOULD first delete all ports and only THEN delete all tree_t
374      * structures as the tree_t structure contains the head for the per-port
375      * list of tree data (tree_t.ports).
376      * If this list_head will be deleted before all the per_tree_ports
377      * bad things will happen ;)
378      */
379 
380     list_for_each_entry_safe(prt, nxt_prt, &br->ports, br_list)
381     {
382         MSTP_IN_delete_port(prt);
383         free(prt);
384     }
385 
386     list_for_each_entry_safe(tree, nxt_tree, &br->trees, bridge_list)
387     {
388         list_del(&tree->bridge_list);
389         free(tree);
390     }
391 }
392 
393 void MSTP_IN_set_bridge_address(bridge_t *br, __u8 *macaddr)
394 {
395     tree_t *tree;
396     bool changed = false;
397 
398     FOREACH_TREE_IN_BRIDGE(tree, br)
399     {
400         if(0 == memcmp(tree->BridgeIdentifier.s.mac_address, macaddr, ETH_ALEN))
401             continue;
402         changed = true;
403         memcpy(tree->BridgeIdentifier.s.mac_address, macaddr, ETH_ALEN);
404         tree->BridgePriority.RootID = tree->BridgePriority.RRootID =
405             tree->BridgePriority.DesignatedBridgeID = tree->BridgeIdentifier;
406     }
407 
408     if(changed)
409         br_state_machines_begin(br);
410 }
411 
412 void MSTP_IN_set_bridge_enable(bridge_t *br, bool up)
413 {
414     port_t *prt;
415     per_tree_port_t *ptp;
416     tree_t *tree;
417 
418     if(br->bridgeEnabled == up)
419         return;
420     br->bridgeEnabled = up;
421 
422     /* Reset all internal states and variables,
423      * except those which are user-configurable */
424     bridge_default_internal_vars(br);
425     FOREACH_TREE_IN_BRIDGE(tree, br)
426     {
427         tree_default_internal_vars(tree);
428     }
429     FOREACH_PORT_IN_BRIDGE(prt, br)
430     {
431         /* NOTE: Don't check prt->deleted here, as it is imposible condition */
432         /* NOTE: In the port_default_internal_vars() rapidAgeingWhile will be
433          *  reset, so we should stop rapid ageing procedure here.
434          */
435         if(prt->rapidAgeingWhile)
436         {
437             MSTP_OUT_set_ageing_time(prt, br->Ageing_Time);
438         }
439         port_default_internal_vars(prt);
440         FOREACH_PTP_IN_PORT(ptp, prt)
441         {
442             if(BR_STATE_DISABLED != ptp->state)
443             {
444                 MSTP_OUT_set_state(ptp, BR_STATE_DISABLED);
445             }
446             ptp_default_internal_vars(ptp);
447         }
448     }
449     br_state_machines_begin(br);
450 }
451 
452 void MSTP_IN_set_port_enable(port_t *prt, bool up, int speed, int duplex)
453 {
454     __u32 computed_pcost, new_ExternalPathCost, new_InternalPathCost;
455     per_tree_port_t *ptp;
456     bool new_p2p;
457     bool changed = false;
458 
459     if(up)
460     {
461         computed_pcost = compute_pcost(speed);
462         new_ExternalPathCost = (0 == prt->AdminExternalPortPathCost) ?
463                                  computed_pcost
464                                : prt->AdminExternalPortPathCost;
465         if(prt->ExternalPortPathCost != new_ExternalPathCost)
466         {
467             assign(prt->ExternalPortPathCost, new_ExternalPathCost);
468             changed = true;
469         }
470         FOREACH_PTP_IN_PORT(ptp, prt)
471         {
472             new_InternalPathCost = (0 == ptp->AdminInternalPortPathCost) ?
473                                     computed_pcost
474                                    : ptp->AdminInternalPortPathCost;
475             if(ptp->InternalPortPathCost != new_InternalPathCost)
476             {
477                 assign(ptp->InternalPortPathCost, new_InternalPathCost);
478                 changed = true;
479             }
480         }
481 
482         switch(prt->AdminP2P)
483         {
484             case p2pForceTrue:
485                 new_p2p = true;
486                 break;
487             case p2pForceFalse:
488                 new_p2p = false;
489                 break;
490             case p2pAuto:
491             default:
492                 new_p2p = !!duplex;
493                 break;
494         }
495         if(prt->operPointToPointMAC != new_p2p)
496         {
497             prt->operPointToPointMAC = new_p2p;
498             changed = true;
499         }
500 
501         if(!prt->portEnabled)
502         {
503             prt->portEnabled = true;
504             prt->BpduGuardError = false;
505             prt->BaInconsistent = false;
506             prt->num_rx_bpdu_filtered = 0;
507             prt->num_rx_bpdu = 0;
508             prt->num_rx_tcn = 0;
509             prt->num_tx_bpdu = 0;
510             prt->num_tx_tcn = 0;
511             changed = true;
512             /* When port is enabled, initialize bridge assurance timer,
513              * so that enough time is given before port is put in
514              * inconsistent state.
515              */
516             updtbrAssuRcvdInfoWhile(prt);
517         }
518     }
519     else
520     {
521         if(prt->portEnabled)
522         {
523             prt->portEnabled = false;
524             changed = true;
525         }
526     }
527 
528     if(changed)
529         br_state_machines_run(prt->bridge);
530 }
531 
532 void MSTP_IN_one_second(bridge_t *br)
533 {
534     port_t *prt;
535     tree_t *tree;
536 
537     ++(br->uptime);
538 
539     if(!br->bridgeEnabled)
540         return;
541 
542     FOREACH_TREE_IN_BRIDGE(tree, br)
543         if(!(tree->topology_change))
544             ++(tree->time_since_topology_change);
545 
546     FOREACH_PORT_IN_BRIDGE(prt, br)
547     {
548         PTSM_tick(prt);
549         /* support for rapid ageing */
550         if(prt->rapidAgeingWhile)
551         {
552             if((--(prt->rapidAgeingWhile)) == 0)
553             {
554                 if(!prt->deleted)
555                     MSTP_OUT_set_ageing_time(prt, br->Ageing_Time);
556             }
557         }
558     }
559 
560     br_state_machines_run(br);
561 }
562 
563 void MSTP_IN_all_fids_flushed(per_tree_port_t *ptp)
564 {
565     bridge_t *br = ptp->port->bridge;
566     ptp->fdbFlush = false;
567     if(!br->bridgeEnabled)
568         return;
569     if(!ptp->calledFromFlushRoutine)
570     {
571         TCSM_run(ptp, false /* actual run */);
572         br_state_machines_run(br);
573     }
574 }
575 
576 /* NOTE: bpdu pointer is unaligned, but it works because
577  * bpdu_t is packed. Don't try to cast bpdu to non-packed type ;)
578  */
579 void MSTP_IN_rx_bpdu(port_t *prt, bpdu_t *bpdu, int size)
580 {
581     int mstis_size;
582     bridge_t *br = prt->bridge;
583 
584     ++(prt->num_rx_bpdu);
585 
586     if(prt->BpduGuardPort)
587     {
588         prt->BpduGuardError = true;
589         ERROR_PRTNAME(br, prt,
590                       "Received BPDU on BPDU Guarded Port - Port Down");
591         MSTP_OUT_shutdown_port(prt);
592         return;
593     }
594 
595     if(prt->bpduFilterPort)
596     {
597         LOG_PRTNAME(br, prt,
598                    "Received BPDU on BPDU Filtered Port - discarded");
599         ++(prt->num_rx_bpdu_filtered);
600         return;
601     }
602 
603     if(!br->bridgeEnabled)
604     {
605         INFO_PRTNAME(br, prt, "Received BPDU while bridge is disabled");
606         return;
607     }
608 
609     if(prt->rcvdBpdu)
610     {
611         ERROR_PRTNAME(br, prt, "Port hasn't processed previous BPDU");
612         return;
613     }
614 
615     /* 14.4 Validation */
616     if((TCN_BPDU_SIZE > size) || (0 != bpdu->protocolIdentifier))
617     {
618 bpdu_validation_failed:
619         INFO_PRTNAME(br, prt, "BPDU validation failed");
620         return;
621     }
622     switch(bpdu->bpduType)
623     {
624         case bpduTypeTCN:
625             /* 14.4.b) */
626             /* Valid TCN BPDU */
627             bpdu->protocolVersion = protoSTP;
628             LOG_PRTNAME(br, prt, "received TCN BPDU");
629             break;
630         case bpduTypeConfig:
631             /* 14.4.a) */
632             if(CONFIG_BPDU_SIZE > size)
633                 goto bpdu_validation_failed;
634             /* Valid Config BPDU */
635             bpdu->protocolVersion = protoSTP;
636             LOG_PRTNAME(br, prt, "received Config BPDU%s",
637                         (bpdu->flags & (1 << offsetTc)) ? ", tcFlag" : ""
638                        );
639             break;
640         case bpduTypeRST:
641             if(protoRSTP == bpdu->protocolVersion)
642             { /* 14.4.c) */
643                 if(RST_BPDU_SIZE > size)
644                     goto bpdu_validation_failed;
645                 /* Valid RST BPDU */
646                 /* bpdu->protocolVersion = protoRSTP; */
647                 LOG_PRTNAME(br, prt, "received RST BPDU%s",
648                             (bpdu->flags & (1 << offsetTc)) ? ", tcFlag" : ""
649                            );
650                 break;
651             }
652             if(protoMSTP > bpdu->protocolVersion)
653                 goto bpdu_validation_failed;
654             /* Yes, 802.1Q-2005 says here to check if it contains
655              * "35 or more octets", not 36! (see 14.4.d).1) )
656              * That's why I check size against CONFIG_BPDU_SIZE
657              * and not RST_BPDU_SIZE.
658              */
659             if(CONFIG_BPDU_SIZE > size)
660                 goto bpdu_validation_failed;
661             mstis_size = __be16_to_cpu(bpdu->version3_len)
662                          - MST_BPDU_VER3LEN_WO_MSTI_MSGS;
663             if((MST_BPDU_SIZE_WO_MSTI_MSGS > size) || (0 != bpdu->version1_len)
664                || (0 > mstis_size)
665                || ((MAX_STANDARD_MSTIS * sizeof(msti_configuration_message_t))
666                    < mstis_size)
667                || (0 != (mstis_size % sizeof(msti_configuration_message_t)))
668               )
669             { /* 14.4.d) */
670                 /* Valid RST BPDU */
671                 bpdu->protocolVersion = protoRSTP;
672                 LOG_PRTNAME(br, prt, "received RST BPDU");
673                 break;
674             }
675             /* 14.4.e) */
676             /* Valid MST BPDU */
677             bpdu->protocolVersion = protoMSTP;
678             prt->rcvdBpduNumOfMstis = mstis_size
679                                       / sizeof(msti_configuration_message_t);
680             LOG_PRTNAME(br, prt, "received MST BPDU%s with %d MSTIs",
681                         (bpdu->flags & (1 << offsetTc)) ? ", tcFlag" : "",
682                         prt->rcvdBpduNumOfMstis
683                        );
684             break;
685         default:
686             goto bpdu_validation_failed;
687     }
688 
689     if((protoSTP == bpdu->protocolVersion) && (bpduTypeTCN == bpdu->bpduType))
690     {
691         ++(prt->num_rx_tcn);
692     }
693     else
694     {
695         if(bpdu->flags & (1 << offsetTc))
696             ++(prt->num_rx_tcn);
697     }
698 
699     assign(prt->rcvdBpduData, *bpdu);
700     prt->rcvdBpdu = true;
701 
702     /* Reset bridge assurance on receipt of valid BPDU */
703     if(prt->BaInconsistent)
704     {
705         prt->BaInconsistent = false;
706         INFO_PRTNAME(br, prt, "Clear Bridge assurance inconsistency");
707     }
708     updtbrAssuRcvdInfoWhile(prt);
709 
710     br_state_machines_run(br);
711 }
712 
713 /* 12.8.1.1 Read CIST Bridge Protocol Parameters */
714 void MSTP_IN_get_cist_bridge_status(bridge_t *br, CIST_BridgeStatus *status)
715 {
716     tree_t *cist = GET_CIST_TREE(br);
717     assign(status->bridge_id, cist->BridgeIdentifier);
718     assign(status->time_since_topology_change,
719            cist->time_since_topology_change);
720     assign(status->topology_change_count, cist->topology_change_count);
721     status->topology_change = cist->topology_change;
722     strncpy(status->topology_change_port, cist->topology_change_port,
723             IFNAMSIZ);
724     strncpy(status->last_topology_change_port, cist->last_topology_change_port,
725             IFNAMSIZ);
726     assign(status->designated_root, cist->rootPriority.RootID);
727     assign(status->root_path_cost,
728            __be32_to_cpu(cist->rootPriority.ExtRootPathCost));
729     assign(status->regional_root, cist->rootPriority.RRootID);
730     assign(status->internal_path_cost,
731            __be32_to_cpu(cist->rootPriority.IntRootPathCost));
732     assign(status->root_port_id, cist->rootPortId);
733     assign(status->root_max_age, cist->rootTimes.Max_Age);
734     assign(status->root_forward_delay, cist->rootTimes.Forward_Delay);
735     assign(status->bridge_max_age, br->Max_Age);
736     assign(status->bridge_forward_delay, br->Forward_Delay);
737     assign(status->max_hops, br->MaxHops);
738     assign(status->tx_hold_count, br->Transmit_Hold_Count);
739     status->protocol_version = br->ForceProtocolVersion;
740     status->enabled = br->bridgeEnabled;
741     assign(status->bridge_hello_time, br->Hello_Time);
742     assign(status->Ageing_Time, br->Ageing_Time);
743 }
744 
745 /* 12.8.1.2 Read MSTI Bridge Protocol Parameters */
746 void MSTP_IN_get_msti_bridge_status(tree_t *tree, MSTI_BridgeStatus *status)
747 {
748     assign(status->bridge_id, tree->BridgeIdentifier);
749     assign(status->time_since_topology_change,
750            tree->time_since_topology_change);
751     assign(status->topology_change_count, tree->topology_change_count);
752     status->topology_change = tree->topology_change;
753     strncpy(status->topology_change_port, tree->topology_change_port,
754             IFNAMSIZ);
755     strncpy(status->last_topology_change_port, tree->last_topology_change_port,
756             IFNAMSIZ);
757     assign(status->regional_root, tree->rootPriority.RRootID);
758     assign(status->internal_path_cost,
759            __be32_to_cpu(tree->rootPriority.IntRootPathCost));
760     assign(status->root_port_id, tree->rootPortId);
761 }
762 
763 /* 12.8.1.3 Set CIST Bridge Protocol Parameters */
764 int MSTP_IN_set_cist_bridge_config(bridge_t *br, CIST_BridgeConfig *cfg)
765 {
766     bool changed, changedBridgeTimes, init;
767     int r = 0;
768     __u8 new_forward_delay, new_max_age;
769     tree_t *tree;
770     port_t *prt;
771     per_tree_port_t *ptp;
772 
773     /* Firstly, validation */
774     if(cfg->set_bridge_max_age)
775     {
776         new_max_age = cfg->bridge_max_age;
777         if((6 > new_max_age) || (40 < new_max_age))
778         {
779             ERROR_BRNAME(br,
780                 "Bridge Max Age must be between 6 and 40 seconds");
781             r = -1;
782         }
783     }
784     else
785         new_max_age = br->Max_Age;
786 
787     if(cfg->set_bridge_forward_delay)
788     {
789         new_forward_delay = cfg->bridge_forward_delay;
790         if((4 > new_forward_delay) || (30 < new_forward_delay))
791         {
792             ERROR_BRNAME(br,
793                 "Bridge Forward Delay must be between 4 and 30 seconds");
794             r = -1;
795         }
796     }
797     else
798         new_forward_delay = br->Forward_Delay;
799 
800     if(cfg->set_bridge_max_age || cfg->set_bridge_forward_delay)
801     {
802         if((2 * (new_forward_delay - 1)) < new_max_age)
803         {
804             ERROR_BRNAME(br, "Configured Bridge Times don't meet "
805                 "2 * (Bridge Foward Delay - 1 second) >= Bridge Max Age");
806             r = -1;
807         }
808     }
809 
810     if(cfg->set_protocol_version)
811     {
812         switch(cfg->protocol_version)
813         {
814             case protoSTP:
815             case protoRSTP:
816             case protoMSTP:
817                 break;
818             default:
819                 ERROR_BRNAME(br, "Bad protocol version (%d)",
820                              cfg->protocol_version);
821                 r = -1;
822         }
823     }
824 
825     if(cfg->set_tx_hold_count)
826     {
827         if((1 > cfg->tx_hold_count) || (10 < cfg->tx_hold_count))
828         {
829             ERROR_BRNAME(br,
830                 "Transmit Hold Count must be between 1 and 10 seconds");
831             r = -1;
832         }
833     }
834 
835     if(cfg->set_max_hops)
836     {
837         if((6 > cfg->max_hops) || (40 < cfg->max_hops))
838         {
839             ERROR_BRNAME(br, "Bridge Max Hops must be between 6 and 40");
840             r = -1;
841         }
842     }
843 
844     if(cfg->set_bridge_hello_time)
845     {
846         if((1 > cfg->bridge_hello_time) || (10 < cfg->bridge_hello_time))
847         {
848             ERROR_BRNAME(br, "Bridge Hello Time must be between 1 and 10");
849             r = -1;
850         }
851     }
852 
853     if(cfg->set_bridge_ageing_time)
854     {
855         if((10 > cfg->bridge_ageing_time)||(1000000 < cfg->bridge_ageing_time))
856         {
857             ERROR_BRNAME(br,
858                 "Bridge Ageing Time must be between 10 and 1000000 seconds");
859             r = -1;
860         }
861     }
862 
863     if(r)
864         return r;
865 
866     /* Secondly, do set */
867     changed = changedBridgeTimes = init = false;
868 
869     if(cfg->set_bridge_max_age || cfg->set_bridge_forward_delay)
870     {
871         if(cmp(new_max_age, !=, br->Max_Age)
872            || cmp(new_forward_delay, !=, br->Forward_Delay)
873           )
874         {
875             assign(br->Max_Age, new_max_age);
876             assign(br->Forward_Delay, new_forward_delay);
877             changed = changedBridgeTimes = true;
878         }
879     }
880 
881     if((cfg->set_protocol_version)
882        && (cfg->protocol_version != br->ForceProtocolVersion)
883       )
884     {
885         br->ForceProtocolVersion = cfg->protocol_version;
886         changed = init = true;
887     }
888 
889     if(cfg->set_tx_hold_count)
890     {
891         if(cfg->tx_hold_count != br->Transmit_Hold_Count)
892         {
893             assign(br->Transmit_Hold_Count, cfg->tx_hold_count);
894             FOREACH_PORT_IN_BRIDGE(prt, br)
895                 assign(prt->txCount, 0u);
896             changed = true;
897         }
898     }
899 
900     if(cfg->set_max_hops)
901     {
902         if(cfg->max_hops != br->MaxHops)
903         {
904             assign(br->MaxHops, cfg->max_hops);
905             changed = changedBridgeTimes = true;
906         }
907     }
908 
909     if(cfg->set_bridge_hello_time)
910     {
911         if(cfg->bridge_hello_time != br->Hello_Time)
912         {
913             INFO_BRNAME(br, "bridge hello_time new=%hhu, old=%hhu",
914                         cfg->bridge_hello_time, br->Hello_Time);
915             assign(br->Hello_Time, cfg->bridge_hello_time);
916             changed = changedBridgeTimes = true;
917         }
918     }
919 
920     if(cfg->set_bridge_ageing_time)
921     {
922         if(cfg->bridge_ageing_time != br->Ageing_Time)
923         {
924             INFO_BRNAME(br, "bridge ageing_time new=%u, old=%u",
925                         cfg->bridge_ageing_time, br->Ageing_Time);
926             assign(br->Ageing_Time, cfg->bridge_ageing_time);
927         }
928     }
929 
930     /* Thirdly, finalize changes */
931     if(changedBridgeTimes)
932     {
933         FOREACH_TREE_IN_BRIDGE(tree, br)
934         {
935             assign(tree->BridgeTimes.remainingHops, br->MaxHops);
936             assign(tree->BridgeTimes.Forward_Delay, br->Forward_Delay);
937             assign(tree->BridgeTimes.Max_Age, br->Max_Age);
938             assign(tree->BridgeTimes.Hello_Time, br->Hello_Time);
939         /* Comment found in rstpd by Srinivas Aji:
940          * Do this for any change in BridgeTimes.
941          * Otherwise we fail UNH rstp.op_D test 3.2 since when administratively
942          * setting BridgeForwardDelay, etc, the values don't propagate from
943          * rootTimes to designatedTimes immediately without this change.
944          */
945             FOREACH_PTP_IN_TREE(ptp, tree)
946             {
947                 ptp->selected = false;
948                 ptp->reselect = true;
949                 /* TODO: change this when Hello_Time will be configurable
950                  *   per-port. For now, copy Bridge's Hello_Time
951                  *   to the port's Hello_Time.
952                  */
953                 assign(ptp->portTimes.Hello_Time, br->Hello_Time);
954             }
955         }
956     }
957 
958     if(changed && br->bridgeEnabled)
959     {
960         if(init)
961             br_state_machines_begin(br);
962         else
963             br_state_machines_run(br);
964     }
965 
966     return 0;
967 }
968 
969 /* 12.8.1.4 Set MSTI Bridge Protocol Parameters */
970 int MSTP_IN_set_msti_bridge_config(tree_t *tree, __u8 bridge_priority)
971 {
972     per_tree_port_t *ptp;
973     __u8 valuePri;
974 
975     if(15 < bridge_priority)
976     {
977         ERROR_BRNAME(tree->bridge,
978                      "MSTI %hu: Bridge Priority must be between 0 and 15",
979                      __be16_to_cpu(tree->MSTID));
980         return -1;
981     }
982 
983     valuePri = bridge_priority << 4;
984     if(GET_PRIORITY_FROM_IDENTIFIER(tree->BridgeIdentifier) == valuePri)
985         return 0;
986     SET_PRIORITY_IN_IDENTIFIER(valuePri, tree->BridgeIdentifier);
987     tree->BridgePriority.RootID = tree->BridgePriority.RRootID =
988         tree->BridgePriority.DesignatedBridgeID = tree->BridgeIdentifier;
989     /* 12.8.1.4.4 do not require reselect, but I think it is needed,
990      *  because 12.8.1.3.4.c) requires it */
991     FOREACH_PTP_IN_TREE(ptp, tree)
992     {
993         ptp->selected = false;
994         ptp->reselect = true;
995     }
996     return 0;
997 }
998 
999 /* 12.8.2.1 Read CIST Port Parameters */
1000 void MSTP_IN_get_cist_port_status(port_t *prt, CIST_PortStatus *status)
1001 {
1002     per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
1003     /* 12.8.2.2.3 b) */
1004     status->uptime = (signed int)((prt->bridge)->uptime)
1005                      - (signed int)(cist->start_time);
1006     status->state = cist->state;
1007     assign(status->port_id, cist->portId);
1008     assign(status->admin_external_port_path_cost,
1009            prt->AdminExternalPortPathCost);
1010     assign(status->external_port_path_cost, prt->ExternalPortPathCost);
1011     assign(status->designated_root, cist->portPriority.RootID);
1012     assign(status->designated_external_cost,
1013            __be32_to_cpu(cist->portPriority.ExtRootPathCost));
1014     assign(status->designated_bridge, cist->portPriority.DesignatedBridgeID);
1015     assign(status->designated_port, cist->portPriority.DesignatedPortID);
1016     assign(status->designated_regional_root, cist->portPriority.RRootID);
1017     assign(status->designated_internal_cost,
1018            __be32_to_cpu(cist->portPriority.IntRootPathCost));
1019     status->tc_ack = prt->tcAck;
1020     assign(status->port_hello_time, cist->portTimes.Hello_Time);
1021     status->admin_edge_port = prt->AdminEdgePort;
1022     status->auto_edge_port = prt->AutoEdge;
1023     status->oper_edge_port = prt->operEdge;
1024     status->enabled = prt->portEnabled;
1025     status->admin_p2p = prt->AdminP2P;
1026     status->oper_p2p = prt->operPointToPointMAC;
1027     status->restricted_role = prt->restrictedRole;
1028     status->restricted_tcn = prt->restrictedTcn;
1029     status->role = cist->role;
1030     status->disputed = cist->disputed;
1031     assign(status->admin_internal_port_path_cost,
1032            cist->AdminInternalPortPathCost);
1033     assign(status->internal_port_path_cost, cist->InternalPortPathCost);
1034     status->bpdu_guard_port = prt->BpduGuardPort;
1035     status->bpdu_guard_error = prt->BpduGuardError;
1036     status->network_port = prt->NetworkPort;
1037     status->ba_inconsistent = prt->BaInconsistent;
1038     status->bpdu_filter_port = prt->bpduFilterPort;
1039     status->num_rx_bpdu_filtered = prt->num_rx_bpdu_filtered;
1040     status->num_rx_bpdu = prt->num_rx_bpdu;
1041     status->num_rx_tcn = prt->num_rx_tcn;
1042     status->num_tx_bpdu = prt->num_tx_bpdu;
1043     status->num_tx_tcn = prt->num_tx_tcn;
1044     status->num_trans_fwd = prt->num_trans_fwd;
1045     status->num_trans_blk = prt->num_trans_blk;
1046     status->rcvdBpdu = prt->rcvdBpdu;
1047     status->rcvdRSTP = prt->rcvdRSTP;
1048     status->rcvdSTP = prt->rcvdSTP;
1049     status->rcvdTcAck = prt->rcvdTcAck;
1050     status->rcvdTcn = prt->rcvdTcn;
1051     status->sendRSTP = prt->sendRSTP;
1052 }
1053 
1054 /* 12.8.2.2 Read MSTI Port Parameters */
1055 void MSTP_IN_get_msti_port_status(per_tree_port_t *ptp,
1056                                   MSTI_PortStatus *status)
1057 {
1058     status->uptime = (signed int)((ptp->port->bridge)->uptime)
1059                      - (signed int)(ptp->start_time);
1060     status->state = ptp->state;
1061     assign(status->port_id, ptp->portId);
1062     assign(status->admin_internal_port_path_cost,
1063            ptp->AdminInternalPortPathCost);
1064     assign(status->internal_port_path_cost, ptp->InternalPortPathCost);
1065     assign(status->designated_regional_root, ptp->portPriority.RRootID);
1066     assign(status->designated_internal_cost,
1067            __be32_to_cpu(ptp->portPriority.IntRootPathCost));
1068     assign(status->designated_bridge, ptp->portPriority.DesignatedBridgeID);
1069     assign(status->designated_port, ptp->portPriority.DesignatedPortID);
1070     status->role = ptp->role;
1071     status->disputed = ptp->disputed;
1072 }
1073 
1074 /* 12.8.2.3 Set CIST port parameters */
1075 int MSTP_IN_set_cist_port_config(port_t *prt, CIST_PortConfig *cfg)
1076 {
1077     bool changed;
1078     __u32 new_ExternalPathCost;
1079     bool new_p2p;
1080     per_tree_port_t *cist;
1081     bridge_t *br = prt->bridge;
1082 
1083     /* Firstly, validation */
1084     if(cfg->set_admin_p2p)
1085     {
1086         switch(cfg->admin_p2p)
1087         {
1088             case p2pAuto:
1089             case p2pForceTrue:
1090             case p2pForceFalse:
1091                 break;
1092             default:
1093                 cfg->admin_p2p = p2pAuto;
1094         }
1095     }
1096 
1097     /* Secondly, do set */
1098     changed = false;
1099 
1100     if(cfg->set_admin_external_port_path_cost)
1101     {
1102         prt->AdminExternalPortPathCost = cfg->admin_external_port_path_cost;
1103         new_ExternalPathCost = (0 == prt->AdminExternalPortPathCost) ?
1104                                  compute_pcost(GET_PORT_SPEED(prt))
1105                                : prt->AdminExternalPortPathCost;
1106         if(prt->ExternalPortPathCost != new_ExternalPathCost)
1107         {
1108             assign(prt->ExternalPortPathCost, new_ExternalPathCost);
1109             changed = true;
1110             /* 12.8.2.3.4 */
1111             cist = GET_CIST_PTP_FROM_PORT(prt);
1112             cist->selected = false;
1113             cist->reselect = true;
1114         }
1115     }
1116 
1117     if(cfg->set_admin_p2p)
1118     {
1119         prt->AdminP2P = cfg->admin_p2p;
1120         switch(prt->AdminP2P)
1121         {
1122             case p2pForceTrue:
1123                 new_p2p = true;
1124                 break;
1125             case p2pForceFalse:
1126                 new_p2p = false;
1127                 break;
1128             case p2pAuto:
1129             default:
1130                 new_p2p = !!GET_PORT_DUPLEX(prt);
1131                 break;
1132         }
1133         if(prt->operPointToPointMAC != new_p2p)
1134         {
1135             prt->operPointToPointMAC = new_p2p;
1136             changed = true;
1137         }
1138     }
1139 
1140     if(cfg->set_admin_edge_port)
1141     {
1142         if(prt->AdminEdgePort != cfg->admin_edge_port)
1143         {
1144             prt->AdminEdgePort = cfg->admin_edge_port;
1145             BDSM_begin(prt);
1146             changed = true;
1147         }
1148     }
1149 
1150     if(cfg->set_auto_edge_port)
1151     {
1152         if(prt->AutoEdge != cfg->auto_edge_port)
1153         {
1154             prt->AutoEdge = cfg->auto_edge_port;
1155             changed = true;
1156         }
1157     }
1158 
1159     if(cfg->set_restricted_role)
1160     {
1161         if(prt->restrictedRole != cfg->restricted_role)
1162         {
1163             prt->restrictedRole = cfg->restricted_role;
1164             changed = true;
1165         }
1166     }
1167 
1168     if(cfg->set_restricted_tcn)
1169     {
1170         if(prt->restrictedTcn != cfg->restricted_tcn)
1171         {
1172             prt->restrictedTcn = cfg->restricted_tcn;
1173             changed = true;
1174         }
1175     }
1176 
1177     if(cfg->set_bpdu_guard_port)
1178     {
1179         if(prt->BpduGuardPort != cfg->bpdu_guard_port)
1180         {
1181             prt->BpduGuardPort = cfg->bpdu_guard_port;
1182             INFO_PRTNAME(br, prt,"BpduGuardPort new=%d", prt->BpduGuardPort);
1183         }
1184     }
1185 
1186     if(cfg->set_network_port)
1187     {
1188         if(prt->NetworkPort != cfg->network_port)
1189         {
1190             prt->NetworkPort = cfg->network_port;
1191             INFO_PRTNAME(br, prt, "NetworkPort new=%d", prt->NetworkPort);
1192             /* When Network port config is removed and bridge assurance
1193              * inconsistency is set, clear the inconsistency.
1194              */
1195             if(!prt->NetworkPort && prt->BaInconsistent)
1196             {
1197                 prt->BaInconsistent = false;
1198                 INFO_PRTNAME(br, prt, "Clear Bridge assurance inconsistency");
1199             }
1200             changed = true;
1201         }
1202     }
1203 
1204     if(cfg->set_dont_txmt)
1205     {
1206         if(prt->dontTxmtBpdu != cfg->dont_txmt)
1207         {
1208             prt->dontTxmtBpdu = cfg->dont_txmt;
1209             INFO_PRTNAME(br, prt, "donttxmt new=%d", prt->dontTxmtBpdu);
1210         }
1211     }
1212 
1213     if(cfg->set_bpdu_filter_port)
1214     {
1215         if (prt->bpduFilterPort != cfg->bpdu_filter_port)
1216         {
1217             prt->bpduFilterPort = cfg->bpdu_filter_port;
1218             prt->num_rx_bpdu_filtered = 0;
1219             INFO_PRTNAME(br, prt,"bpduFilterPort new=%d", prt->bpduFilterPort);
1220         }
1221     }
1222 
1223     if(changed && prt->portEnabled)
1224         br_state_machines_run(prt->bridge);
1225 
1226     return 0;
1227 }
1228 
1229 /* 12.8.2.4 Set MSTI port parameters */
1230 int MSTP_IN_set_msti_port_config(per_tree_port_t *ptp, MSTI_PortConfig *cfg)
1231 {
1232     __u8 valuePri;
1233     __u32 new_InternalPathCost;
1234     bool changed = false;
1235     port_t *prt = ptp->port;
1236     bridge_t *br = prt->bridge;
1237 
1238     if(cfg->set_port_priority)
1239     {
1240         if(15 < cfg->port_priority)
1241         {
1242             ERROR_MSTINAME(br, prt, ptp,
1243                            "Port Priority must be between 0 and 15");
1244             return -1;
1245         }
1246         valuePri = cfg->port_priority << 4;
1247         if(GET_PRIORITY_FROM_IDENTIFIER(ptp->portId) != valuePri)
1248         {
1249             SET_PRIORITY_IN_IDENTIFIER(valuePri, ptp->portId);
1250             changed = true;
1251         }
1252     }
1253 
1254     if(cfg->set_admin_internal_port_path_cost)
1255     {
1256         ptp->AdminInternalPortPathCost = cfg->admin_internal_port_path_cost;
1257         new_InternalPathCost = (0 == ptp->AdminInternalPortPathCost) ?
1258                                  compute_pcost(GET_PORT_SPEED(prt))
1259                                : ptp->AdminInternalPortPathCost;
1260         if(ptp->InternalPortPathCost != new_InternalPathCost)
1261         {
1262             assign(ptp->InternalPortPathCost, new_InternalPathCost);
1263             changed = true;
1264         }
1265     }
1266 
1267     if(changed && prt->portEnabled)
1268     {
1269         /* 12.8.2.4.4 */
1270         ptp->selected = false;
1271         ptp->reselect = true;
1272 
1273         br_state_machines_run(br);
1274     }
1275 
1276     return 0;
1277 }
1278 
1279 /* 12.8.2.5 Force BPDU Migration Check */
1280 int MSTP_IN_port_mcheck(port_t *prt)
1281 {
1282     bridge_t *br = prt->bridge;
1283     per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
1284 
1285     if(rstpVersion(br) && prt->portEnabled && br->bridgeEnabled)
1286     {
1287         prt->mcheck = true;
1288         cist->proposing = true;
1289         br_state_machines_run(br);
1290     }
1291 
1292     return 0;
1293 }
1294 
1295 /* 12.10.3.8 Set VID to FID allocation */
1296 bool MSTP_IN_set_vid2fid(bridge_t *br, __u16 vid, __u16 fid)
1297 {
1298     bool vid2mstid_changed;
1299 
1300     if((vid < 1) || (vid > MAX_VID) || (fid > MAX_FID))
1301     {
1302         ERROR_BRNAME(br, "Error allocating VID(%hu) to FID(%hu)", vid, fid);
1303         return false;
1304     }
1305 
1306     vid2mstid_changed =
1307         (br->fid2mstid[fid] != br->fid2mstid[br->vid2fid[vid]]);
1308     br->vid2fid[vid] = fid;
1309     if(vid2mstid_changed)
1310     {
1311         RecalcConfigDigest(br);
1312         br_state_machines_begin(br);
1313     }
1314 
1315     return true;
1316 }
1317 
1318 /* Set all VID-to-FID mappings at once */
1319 bool MSTP_IN_set_all_vids2fids(bridge_t *br, __u16 *vids2fids)
1320 {
1321     bool vid2mstid_changed;
1322     int vid;
1323 
1324     vid2mstid_changed = false;
1325     for(vid = 1; vid <= MAX_VID; ++vid)
1326     {
1327         if(vids2fids[vid] > MAX_FID)
1328         { /* Incorrect value == keep prev value */
1329             vids2fids[vid] = br->vid2fid[vid];
1330             continue;
1331         }
1332         if(br->fid2mstid[vids2fids[vid]] != br->fid2mstid[br->vid2fid[vid]])
1333             vid2mstid_changed = true;
1334     }
1335     memcpy(br->vid2fid, vids2fids, sizeof(br->vid2fid));
1336     if(vid2mstid_changed)
1337     {
1338         RecalcConfigDigest(br);
1339         br_state_machines_begin(br);
1340     }
1341 
1342     return true;
1343 }
1344 
1345 /* 12.12.2.2 Set FID to MSTID allocation */
1346 bool MSTP_IN_set_fid2mstid(bridge_t *br, __u16 fid, __u16 mstid)
1347 {
1348     tree_t *tree;
1349     __be16 MSTID;
1350     bool found;
1351     int vid;
1352 
1353     if(fid > MAX_FID)
1354     {
1355         ERROR_BRNAME(br, "Bad FID(%hu)", fid);
1356         return false;
1357     }
1358 
1359     MSTID = __cpu_to_be16(mstid);
1360     found = false;
1361     FOREACH_TREE_IN_BRIDGE(tree, br)
1362     {
1363         if(tree->MSTID == MSTID)
1364         {
1365             found = true;
1366             break;
1367         }
1368     }
1369     if(!found)
1370     {
1371         ERROR_BRNAME(br, "MSTID(%hu) not found", mstid);
1372         return false;
1373     }
1374 
1375     if(br->fid2mstid[fid] != MSTID)
1376     {
1377         br->fid2mstid[fid] = MSTID;
1378         /* check if there are VLANs using this FID */
1379         for(vid = 1; vid <= MAX_VID; ++vid)
1380         {
1381             if(br->vid2fid[vid] == fid)
1382             {
1383                 RecalcConfigDigest(br);
1384                 br_state_machines_begin(br);
1385                 break;
1386             }
1387         }
1388     }
1389 
1390     return true;
1391 }
1392 
1393 /* Set all FID-to-MSTID mappings at once */
1394 bool MSTP_IN_set_all_fids2mstids(bridge_t *br, __u16 *fids2mstids)
1395 {
1396     tree_t *tree;
1397     __be16 MSTID[MAX_FID + 1];
1398     bool found, vid2mstid_changed;
1399     int fid, vid;
1400     __be16 prev_vid2mstid[MAX_VID + 2];
1401 
1402     for(fid = 0; fid <= MAX_FID; ++fid)
1403     {
1404         if(fids2mstids[fid] > MAX_MSTID)
1405         { /* Incorrect value == keep prev value */
1406             fids2mstids[fid] = __be16_to_cpu(MSTID[fid] = br->fid2mstid[fid]);
1407         }
1408         else
1409             MSTID[fid] = __cpu_to_be16(fids2mstids[fid]);
1410         found = false;
1411         FOREACH_TREE_IN_BRIDGE(tree, br)
1412         {
1413             if(tree->MSTID == MSTID[fid])
1414             {
1415                 found = true;
1416                 break;
1417             }
1418         }
1419         if(!found)
1420         {
1421             ERROR_BRNAME(br,
1422                 "Error allocating FID(%hu) to MSTID(%hu): MSTID not found",
1423                 fid, fids2mstids[fid]);
1424             return false;
1425         }
1426     }
1427 
1428     for(vid = 1; vid <= MAX_VID; ++vid)
1429         prev_vid2mstid[vid] = br->fid2mstid[br->vid2fid[vid]];
1430     memcpy(br->fid2mstid, MSTID, sizeof(br->fid2mstid));
1431     vid2mstid_changed = false;
1432     for(vid = 1; vid <= MAX_VID; ++vid)
1433     {
1434         if(prev_vid2mstid[vid] != br->fid2mstid[br->vid2fid[vid]])
1435         {
1436             vid2mstid_changed = true;
1437             break;
1438         }
1439     }
1440     if(vid2mstid_changed)
1441     {
1442         RecalcConfigDigest(br);
1443         br_state_machines_begin(br);
1444     }
1445 
1446     return true;
1447 }
1448 
1449 /* 12.12.1.1 Read MSTI List */
1450 bool MSTP_IN_get_mstilist(bridge_t *br, int *num_mstis, __u16 *mstids)
1451 {
1452     tree_t *tree;
1453 
1454     *num_mstis = 0;
1455     FOREACH_TREE_IN_BRIDGE(tree, br)
1456     {
1457         mstids[*num_mstis] = __be16_to_cpu(tree->MSTID);
1458         /* Check for "<", not for "<=", as num_mstis include CIST */
1459         if(MAX_IMPLEMENTATION_MSTIS < ++(*num_mstis))
1460             break;
1461     }
1462 
1463     return true;
1464 }
1465 
1466 /* 12.12.1.2 Create MSTI */
1467 bool MSTP_IN_create_msti(bridge_t *br, __u16 mstid)
1468 {
1469     tree_t *tree, *tree_after, *new_tree;
1470     per_tree_port_t *ptp, *nxt, *ptp_after, *new_ptp;
1471     int num_of_mstis;
1472     __be16 MSTID;
1473 
1474     if((mstid < 1) || (mstid > MAX_MSTID))
1475     {
1476         ERROR_BRNAME(br, "Bad MSTID(%hu)", mstid);
1477         return false;
1478     }
1479 
1480     MSTID = __cpu_to_be16(mstid);
1481     /* Find place where to insert new MSTID.
1482      * Also check if such MSTID is already in the list.
1483      * Also count existing mstis.
1484      */
1485     tree_after = NULL;
1486     num_of_mstis = 0;
1487     FOREACH_TREE_IN_BRIDGE(tree, br)
1488     {
1489         if(tree->MSTID == MSTID)
1490         {
1491             INFO_BRNAME(br, "MSTID(%hu) is already in the list", mstid);
1492             return true; /* yes, it is success */
1493         }
1494         if(cmp(tree->MSTID, <, MSTID))
1495             tree_after = tree;
1496         ++num_of_mstis;
1497     }
1498     /* Sanity check */
1499     if(NULL == tree_after)
1500     {
1501         ERROR_BRNAME(br, "Can't add MSTID(%hu): no CIST in the list", mstid);
1502         return false;
1503     }
1504     /* End of Sanity check */
1505 
1506     /* Check for "<", not for "<=", as num_of_mstis include CIST */
1507     if(MAX_IMPLEMENTATION_MSTIS < num_of_mstis)
1508     {
1509         ERROR_BRNAME(br, "Can't add MSTID(%hu): maximum count(%u) reached",
1510                      mstid, MAX_IMPLEMENTATION_MSTIS);
1511         return false;
1512     }
1513 
1514     /* Create new tree and its list of PerTreePort structures */
1515     tree = GET_CIST_TREE(br);
1516     if(!(new_tree=create_tree(br,tree->BridgeIdentifier.s.mac_address,MSTID)))
1517         return false;
1518 
1519     FOREACH_PTP_IN_TREE(ptp_after, tree_after)
1520     {
1521         if(!(new_ptp = create_ptp(new_tree, ptp_after->port)))
1522         {
1523             /* Remove and free all previously created entries in tree's list */
1524             list_for_each_entry_safe(ptp, nxt, &new_tree->ports, tree_list)
1525             {
1526                 list_del(&ptp->port_list);
1527                 list_del(&ptp->tree_list);
1528                 free(ptp);
1529             }
1530             return false;
1531         }
1532         list_add(&new_ptp->port_list, &ptp_after->port_list);
1533         list_add_tail(&new_ptp->tree_list, &new_tree->ports);
1534     }
1535 
1536     list_add(&new_tree->bridge_list, &tree_after->bridge_list);
1537     /* There are no FIDs allocated to this MSTID, so VID-to-MSTID mapping
1538      *  did not change. So, no need in RecalcConfigDigest.
1539      * Just initialize state machines for this tree.
1540      */
1541     tree_state_machines_begin(new_tree);
1542     return true;
1543 }
1544 
1545 /* 12.12.1.3 Delete MSTI */
1546 bool MSTP_IN_delete_msti(bridge_t *br, __u16 mstid)
1547 {
1548     tree_t *tree;
1549     per_tree_port_t *ptp, *nxt;
1550     int fid;
1551     bool found;
1552     __be16 MSTID = __cpu_to_be16(mstid);
1553 
1554     if((mstid < 1) || (mstid > MAX_MSTID))
1555     {
1556         ERROR_BRNAME(br, "Bad MSTID(%hu)", mstid);
1557         return false;
1558     }
1559 
1560     /* Check if there are FIDs associated with this MSTID */
1561     for(fid = 0; fid <= MAX_FID; ++fid)
1562     {
1563         if(br->fid2mstid[fid] == MSTID)
1564         {
1565             ERROR_BRNAME(br,
1566                 "Can't delete MSTID(%hu): there are FIDs allocated to it",
1567                 mstid);
1568             return false;
1569         }
1570     }
1571 
1572     found = false;
1573     FOREACH_TREE_IN_BRIDGE(tree, br)
1574     {
1575         if(tree->MSTID == MSTID)
1576         {
1577             found = true;
1578             break;
1579         }
1580     }
1581     if(!found)
1582     {
1583         INFO_BRNAME(br, "MSTID(%hu) is not in the list", mstid);
1584         return true; /* yes, it is success */
1585     }
1586 
1587     list_del(&tree->bridge_list);
1588     list_for_each_entry_safe(ptp, nxt, &tree->ports, tree_list)
1589     {
1590         list_del(&ptp->port_list);
1591         list_del(&ptp->tree_list);
1592         free(ptp);
1593     }
1594     free(tree);
1595 
1596     /* There are no FIDs allocated to this MSTID, so VID-to-MSTID mapping
1597      *  did not change. So, no need in RecalcConfigDigest.
1598      * Give state machine a spare run, just for the case...
1599      */
1600     br_state_machines_run(br);
1601     return true;
1602 }
1603 
1604 /* 12.12.3.4 Set MST Configuration Identifier Elements */
1605 void MSTP_IN_set_mst_config_id(bridge_t *br, __u16 revision, __u8 *name)
1606 {
1607     __be16 valueRevision = __cpu_to_be16(revision);
1608     bool changed = (0 != strncmp((char *)name, (char *)br->MstConfigId.s.configuration_name,
1609                                  sizeof(br->MstConfigId.s.configuration_name))
1610                    )
1611                    || (valueRevision != br->MstConfigId.s.revision_level);
1612 
1613     if(changed)
1614     {
1615         assign(br->MstConfigId.s.revision_level, valueRevision);
1616         memset(br->MstConfigId.s.configuration_name, 0,
1617                sizeof(br->MstConfigId.s.configuration_name));
1618         strncpy((char *)br->MstConfigId.s.configuration_name, (char *)name,
1619                 sizeof(br->MstConfigId.s.configuration_name) - 1);
1620         br_state_machines_begin(br);
1621     }
1622 }
1623 
1624 /*
1625  * If hint_SetToYes == true, some tcWhile in this tree has non-zero value.
1626  * If hint_SetToYes == false, some tcWhile in this tree has just became zero,
1627  *  so we should check all other tcWhile's in this tree.
1628  */
1629 static void set_TopologyChange(tree_t *tree, bool hint_SetToYes, port_t *port)
1630 {
1631     per_tree_port_t *ptp;
1632     bool prev_tc_not_set = !tree->topology_change;
1633 
1634     if(hint_SetToYes)
1635     {
1636         tree->topology_change = true;
1637         tree->time_since_topology_change = 0;
1638         if(prev_tc_not_set)
1639             ++(tree->topology_change_count);
1640         strncpy(tree->topology_change_port, tree->last_topology_change_port,
1641                 IFNAMSIZ);
1642         strncpy(tree->last_topology_change_port, port->sysdeps.name, IFNAMSIZ);
1643         return;
1644     }
1645 
1646     /* Some tcWhile has just became zero. Check if we need reset
1647      * topology_change flag */
1648     if(prev_tc_not_set)
1649         return;
1650 
1651     tree->topology_change = false;
1652     FOREACH_PTP_IN_TREE(ptp, tree)
1653     {
1654         if(0 != ptp->tcWhile)
1655         {
1656             tree->topology_change = true;
1657             tree->time_since_topology_change = 0;
1658             return;
1659         }
1660     }
1661 }
1662 
1663 /* Helper functions, compare two priority vectors */
1664 static bool samePriorityAndTimers(port_priority_vector_t *vec1,
1665                                   port_priority_vector_t *vec2,
1666                                   times_t *time1,
1667                                   times_t *time2,
1668                                   bool cist)
1669 {
1670     if(cist)
1671     {
1672         if(cmp(time1->Forward_Delay, !=, time2->Forward_Delay))
1673             return false;
1674         if(cmp(time1->Max_Age, !=, time2->Max_Age))
1675             return false;
1676         if(cmp(time1->Message_Age, !=, time2->Message_Age))
1677             return false;
1678         if(cmp(time1->Hello_Time, !=, time2->Hello_Time))
1679             return false;
1680 
1681         if(cmp(vec1->RootID, !=, vec2->RootID))
1682             return false;
1683         if(cmp(vec1->ExtRootPathCost, !=, vec2->ExtRootPathCost))
1684             return false;
1685     }
1686 
1687     if(cmp(time1->remainingHops, !=, time2->remainingHops))
1688         return false;
1689 
1690     if(cmp(vec1->RRootID, !=, vec2->RRootID))
1691         return false;
1692     if(cmp(vec1->IntRootPathCost, !=, vec2->IntRootPathCost))
1693         return false;
1694     if(cmp(vec1->DesignatedBridgeID, !=, vec2->DesignatedBridgeID))
1695         return false;
1696     if(cmp(vec1->DesignatedPortID, !=, vec2->DesignatedPortID))
1697         return false;
1698 
1699     return true;
1700 }
1701 
1702 static bool betterorsamePriority(port_priority_vector_t *vec1,
1703                                  port_priority_vector_t *vec2,
1704                                  port_identifier_t pId1,
1705                                  port_identifier_t pId2,
1706                                  bool cist)
1707 {
1708     int result;
1709 
1710     if(cist)
1711     {
1712         if(0 < (result = _ncmp(vec1->RootID, vec2->RootID)))
1713             return false; /* worse */
1714         else if(0 > result)
1715             return true; /* better */
1716         /* The same. Check further. */
1717         if(0 < (result = _ncmp(vec1->ExtRootPathCost, vec2->ExtRootPathCost)))
1718             return false; /* worse */
1719         else if(0 > result)
1720             return true; /* better */
1721         /* The same. Check further. */
1722     }
1723 
1724     if(0 < (result = _ncmp(vec1->RRootID, vec2->RRootID)))
1725         return false; /* worse */
1726     else if(0 > result)
1727         return true; /* better */
1728     /* The same. Check further. */
1729 
1730     if(0 < (result = _ncmp(vec1->IntRootPathCost, vec2->IntRootPathCost)))
1731         return false; /* worse */
1732     else if(0 > result)
1733         return true; /* better */
1734     /* The same. Check further. */
1735 
1736     if(0 < (result = _ncmp(vec1->DesignatedBridgeID, vec2->DesignatedBridgeID)))
1737         return false; /* worse */
1738     else if(0 > result)
1739         return true; /* better */
1740     /* The same. Check further. */
1741 
1742     if(0 < (result = _ncmp(vec1->DesignatedPortID, vec2->DesignatedPortID)))
1743         return false; /* worse */
1744     else if(0 > result)
1745         return true; /* better */
1746     /* The same. Check further. */
1747 
1748     /* Port ID is a tie-breaker */
1749     return cmp(pId1, <=, pId2);
1750 }
1751 
1752 /* 13.26.1 betterorsameInfo */
1753 static bool betterorsameInfo(per_tree_port_t *ptp, port_info_origin_t newInfoIs)
1754 {
1755     if((ioReceived == newInfoIs) && (ioReceived == ptp->infoIs))
1756         return betterorsamePriority(&ptp->msgPriority,
1757                                     &ptp->portPriority,
1758                                     0, 0, (0 == ptp->MSTID));
1759     else if((ioMine == newInfoIs) && (ioMine == ptp->infoIs))
1760         return betterorsamePriority(&ptp->designatedPriority,
1761                                     &ptp->portPriority,
1762                                     0, 0, (0 == ptp->MSTID));
1763     return false;
1764 }
1765 
1766 /* 13.26.2 clearAllRcvdMsgs */
1767 static bool clearAllRcvdMsgs(port_t *prt, bool dry_run)
1768 {
1769     per_tree_port_t *ptp;
1770 
1771     if(dry_run)
1772     {
1773         FOREACH_PTP_IN_PORT(ptp, prt)
1774             if(ptp->rcvdMsg)
1775                 return true;
1776         return false;
1777     }
1778 
1779     FOREACH_PTP_IN_PORT(ptp, prt)
1780         ptp->rcvdMsg = false;
1781 
1782     return false;
1783 }
1784 
1785 /* 13.26.3 clearReselectTree */
1786 static void clearReselectTree(tree_t *tree)
1787 {
1788     per_tree_port_t *ptp;
1789 
1790     FOREACH_PTP_IN_TREE(ptp, tree)
1791         ptp->reselect = false;
1792 }
1793 
1794 /* 13.26.4 fromSameRegion */
1795 static bool fromSameRegion(port_t *prt)
1796 {
1797     /* Check for rcvdRSTP is superfluous here */
1798     if((protoMSTP > prt->rcvdBpduData.protocolVersion)/* || (!prt->rcvdRSTP)*/)
1799         return false;
1800     return cmp(prt->bridge->MstConfigId,
1801                ==, prt->rcvdBpduData.mstConfigurationIdentifier);
1802 }
1803 
1804 /* 13.26.5 newTcWhile */
1805 static void newTcWhile(per_tree_port_t *ptp)
1806 {
1807     if(0 != ptp->tcWhile)
1808         return;
1809 
1810     tree_t *tree = ptp->tree;
1811     port_t *prt = ptp->port;
1812 
1813     if(prt->sendRSTP)
1814     {
1815         per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
1816 
1817         ptp->tcWhile = cist->portTimes.Hello_Time + 1;
1818         set_TopologyChange(tree, true, prt);
1819 
1820         if(0 == ptp->MSTID)
1821             prt->newInfo = true;
1822         else
1823             prt->newInfoMsti = true;
1824         return;
1825     }
1826 
1827     times_t *times = &tree->rootTimes;
1828 
1829     ptp->tcWhile = times->Max_Age + times->Forward_Delay;
1830     set_TopologyChange(tree, true, prt);
1831 }
1832 
1833 /* 13.26.6 rcvInfo */
1834 static port_info_t rcvInfo(per_tree_port_t *ptp)
1835 {
1836     msti_configuration_message_t *msti_msg;
1837     per_tree_port_t *ptp_1;
1838     bool roleIsDesignated, cist;
1839     bool msg_Better_port, msg_SamePriorityAndTimers_port;
1840     port_priority_vector_t *mPri = &(ptp->msgPriority);
1841     times_t *mTimes = &(ptp->msgTimes);
1842     port_t *prt = ptp->port;
1843     bpdu_t *b = &(prt->rcvdBpduData);
1844 
1845     if(bpduTypeTCN == b->bpduType)
1846     {
1847         prt->rcvdTcn = true;
1848         FOREACH_PTP_IN_PORT(ptp_1, prt)
1849             ptp_1->rcvdTc = true;
1850         return OtherInfo;
1851     }
1852 
1853     if(0 == ptp->MSTID)
1854     { /* CIST */
1855         if(protoSTP != b->protocolVersion)
1856         {
1857             switch(BPDU_FLAGS_ROLE_GET(b->flags))
1858             {
1859                 case encodedRoleAlternateBackup:
1860                 case encodedRoleRoot:
1861                     roleIsDesignated = false;
1862                     break;
1863                 case encodedRoleDesignated:
1864                     roleIsDesignated = true;
1865                     break;
1866                 case encodedRoleMaster:
1867                     /* 802.1D-2004 S9.2.9 P61. The Unknown value of Port Role
1868                      * cannot be generated by a valid implementation; however,
1869                      * this value is accepted on receipt. roleMaster in MSTP is
1870                      * roleUnknown in RSTP.
1871                      * NOTE.If the Unknown value of the Port Role parameter is
1872                      * received, the state machines will effectively treat the RST
1873                      * BPDU as if it were a Configuration BPDU
1874                      */
1875                     if(protoRSTP == b->protocolVersion)
1876                     {
1877                         roleIsDesignated = true;
1878                         break;
1879                     }
1880                     else
1881                     {
1882                         return OtherInfo;
1883                     }
1884                     break;
1885                 default:
1886                     return OtherInfo;
1887             }
1888         }
1889         else
1890         { /* 13.26.6.NOTE: A Configuration BPDU implicitly conveys a
1891            *   Designated Port Role */
1892             roleIsDesignated = true;
1893         }
1894         cist = true;
1895 
1896         assign(mPri->RRootID, b->cistRRootID);
1897         assign(mPri->DesignatedPortID, b->cistPortID);
1898         assign(mPri->RootID, b->cistRootID);
1899         assign(mPri->ExtRootPathCost, b->cistExtRootPathCost);
1900         /* messageTimes */
1901 #define NEAREST_WHOLE_SECOND(msgTime)  \
1902     ((128 > msgTime[1]) ? msgTime[0] : msgTime[0] + 1)
1903         mTimes->Forward_Delay = NEAREST_WHOLE_SECOND(b->ForwardDelay);
1904         mTimes->Max_Age = NEAREST_WHOLE_SECOND(b->MaxAge);
1905         mTimes->Message_Age = NEAREST_WHOLE_SECOND(b->MessageAge);
1906         mTimes->Hello_Time = NEAREST_WHOLE_SECOND(b->HelloTime);
1907         if(protoMSTP > b->protocolVersion)
1908         { /* STP Configuration BPDU or RST BPDU */
1909             assign(mPri->IntRootPathCost, __constant_cpu_to_be32(0));
1910             assign(mPri->DesignatedBridgeID, b->cistRRootID);
1911             /* messageTimes.remainingHops */
1912             assign(mTimes->remainingHops, prt->bridge->MaxHops);
1913         }
1914         else
1915         { /* MST BPDU */
1916             assign(mPri->IntRootPathCost, b->cistIntRootPathCost);
1917             assign(mPri->DesignatedBridgeID, b->cistBridgeID);
1918             /* messageTimes.remainingHops */
1919             assign(mTimes->remainingHops, b->cistRemainingHops);
1920         }
1921     }
1922     else
1923     { /* MSTI */
1924         if(protoMSTP > b->protocolVersion)
1925             return OtherInfo;
1926         msti_msg = ptp->rcvdMstiConfig;
1927         switch(BPDU_FLAGS_ROLE_GET(msti_msg->flags))
1928         {
1929             case encodedRoleAlternateBackup:
1930             case encodedRoleRoot:
1931                 roleIsDesignated = false;
1932                 break;
1933             case encodedRoleDesignated:
1934                 roleIsDesignated = true;
1935                 break;
1936             default:
1937                 return OtherInfo;
1938         }
1939         cist = false;
1940 
1941         assign(mPri->RRootID, msti_msg->mstiRRootID);
1942         assign(mPri->IntRootPathCost, msti_msg->mstiIntRootPathCost);
1943         /* Build MSTI DesignatedBridgeID */
1944         assign(mPri->DesignatedBridgeID, b->cistBridgeID);
1945         assign(mPri->DesignatedBridgeID.s.priority, ptp->MSTID);
1946         SET_PRIORITY_IN_IDENTIFIER(msti_msg->bridgeIdentifierPriority,
1947                                    mPri->DesignatedBridgeID);
1948         /* Build MSTI DesignatedPortID */
1949         assign(mPri->DesignatedPortID, b->cistPortID);
1950         SET_PRIORITY_IN_IDENTIFIER(msti_msg->portIdentifierPriority,
1951                                    mPri->DesignatedPortID);
1952         /* messageTimes */
1953         assign(mTimes->remainingHops, msti_msg->remainingHops);
1954     }
1955 
1956     msg_Better_port = !betterorsamePriority(&(ptp->portPriority), mPri,
1957                                             0, 0, cist);
1958     if(roleIsDesignated)
1959     {
1960         /* a).1) */
1961         if(msg_Better_port
1962            || ((0 == memcmp(mPri->DesignatedBridgeID.s.mac_address,
1963                             ptp->portPriority.DesignatedBridgeID.s.mac_address,
1964                             ETH_ALEN)
1965                )
1966                && (0 == ((mPri->DesignatedPortID
1967                           ^ ptp->portPriority.DesignatedPortID
1968                          ) & __constant_cpu_to_be16(0x0FFF)
1969                         )
1970                   )
1971               )
1972           )
1973             return SuperiorDesignatedInfo;
1974 
1975         /* a).2) */
1976         /* We already know that msgPriority _IS_NOT_BETTER_than portPriority.
1977          * So, if msgPriority _IS_SAME_OR_BETTER_than portPriority then
1978          *   msgPriority _IS_SAME_as portPriority.
1979         */
1980         msg_SamePriorityAndTimers_port =
1981             samePriorityAndTimers(mPri, &(ptp->portPriority),
1982                                   mTimes, &(ptp->portTimes),
1983                                   cist);
1984         if((!msg_SamePriorityAndTimers_port)
1985            && betterorsamePriority(mPri, &(ptp->portPriority), 0, 0, cist)
1986           )
1987             return SuperiorDesignatedInfo;
1988 
1989         /* b) */
1990         if(msg_SamePriorityAndTimers_port && (ioReceived == ptp->infoIs))
1991             return RepeatedDesignatedInfo;
1992 
1993         /* c) */
1994         return InferiorDesignatedInfo;
1995     }
1996 
1997     /* d) */
1998     if(!msg_Better_port)
1999         return InferiorRootAlternateInfo;
2000 
2001     return OtherInfo;
2002 }
2003 
2004 /* 13.26.7 recordAgreement */
2005 static void recordAgreement(per_tree_port_t *ptp)
2006 {
2007     bool cist_agreed, cist_proposing;
2008     per_tree_port_t *cist;
2009     port_t *prt = ptp->port;
2010     bpdu_t *b = &(prt->rcvdBpduData);
2011 
2012     if(0 == ptp->MSTID)
2013     { /* CIST */
2014         if(rstpVersion(prt->bridge) && prt->operPointToPointMAC
2015            && (b->flags & (1 << offsetAgreement))
2016           )
2017         {
2018             ptp->agreed = true;
2019             ptp->proposing = false;
2020         }
2021         else
2022             ptp->agreed = false;
2023         cist_agreed = ptp->agreed;
2024         cist_proposing = ptp->proposing;
2025         if(!prt->rcvdInternal)
2026             list_for_each_entry_continue(ptp, &prt->trees, port_list)
2027             {
2028                 ptp->agreed = cist_agreed;
2029                 ptp->proposing = cist_proposing;
2030             }
2031         return;
2032     }
2033     /* MSTI */
2034     cist = GET_CIST_PTP_FROM_PORT(prt);
2035     if(prt->operPointToPointMAC 
2036        && cmp(b->cistRootID, ==, cist->portPriority.RootID)
2037        && cmp(b->cistExtRootPathCost, ==, cist->portPriority.ExtRootPathCost)
2038        && cmp(b->cistRRootID, ==, cist->portPriority.RRootID)
2039        && (ptp->rcvdMstiConfig->flags & (1 << offsetAgreement))
2040       )
2041     {
2042         ptp->agreed = true;
2043         ptp->proposing = false;
2044     }
2045     else
2046         ptp->agreed = false;
2047 }
2048 
2049 /* 13.26.8 recordDispute */
2050 static void recordDispute(per_tree_port_t *ptp)
2051 {
2052     port_t *prt;
2053 
2054     if(0 == ptp->MSTID)
2055     { /* CIST */
2056         prt = ptp->port;
2057         /* 802.1Q-2005(-2011) is somewhat unclear for the case
2058          *  (!prt->rcvdInternal): if we should record dispute for all MSTIs
2059          *  unconditionally or only when CIST Learning flag is set in BPDU.
2060          * I guess that in this case MSTIs should be in sync with CIST
2061          * so record dispute for the MSTIs only when the same is done for CIST.
2062          * Additional supporting argument to this guess is that in
2063          *  setTcFlags() we do the same.
2064          * But that is only a guess and I could be wrong here ;)
2065          */
2066         if(prt->rcvdBpduData.flags & (1 << offsetLearnig))
2067         {
2068             ptp->disputed = true;
2069             ptp->agreed = false;
2070             if(!prt->rcvdInternal)
2071                 list_for_each_entry_continue(ptp, &prt->trees, port_list)
2072                 {
2073                     ptp->disputed = true;
2074                     ptp->agreed = false;
2075                 }
2076         }
2077         return;
2078     }
2079     /* MSTI */
2080     if(ptp->rcvdMstiConfig->flags & (1 << offsetLearnig))
2081     {
2082         ptp->disputed = true;
2083         ptp->agreed = false;
2084     }
2085 }
2086 
2087 /* 13.26.9 recordMastered */
2088 static void recordMastered(per_tree_port_t *ptp)
2089 {
2090     port_t *prt = ptp->port;
2091 
2092     if(0 == ptp->MSTID)
2093     { /* CIST */
2094         if(!prt->rcvdInternal)
2095             list_for_each_entry_continue(ptp, &prt->trees, port_list)
2096                 ptp->mastered = false;
2097         return;
2098     }
2099     /* MSTI */
2100     ptp->mastered = prt->operPointToPointMAC
2101                     && (ptp->rcvdMstiConfig->flags & (1 << offsetMaster));
2102 }
2103 
2104 /* 13.26.f) recordPriority */
2105 static void recordPriority(per_tree_port_t *ptp)
2106 {
2107     assign(ptp->portPriority, ptp->msgPriority);
2108 }
2109 
2110 /* 13.26.10 recordProposal */
2111 static void recordProposal(per_tree_port_t *ptp)
2112 {
2113     bool cist_proposed;
2114     port_t *prt;
2115 
2116     /* 802.1Q-2005 says to check if received message conveys
2117      *  a Designated Port Role. But there is no need in this check,
2118      *  as it is always true. This function is called only in two states:
2119      *  PISM_SUPERIOR_DESIGNATED and PISM_REPEATED_DESIGNATED, which
2120      *  can be entered only if rcvInfo returns
2121      *  SuperiorDesignatedInfo or RepeatedDesignatedInfo.
2122      *  Which in turn can only happen if message conveys designated role
2123      *   (see rcvInfo).
2124      */
2125     if(0 == ptp->MSTID)
2126     { /* CIST */
2127         prt = ptp->port;
2128         if(prt->rcvdBpduData.flags & (1 << offsetProposal))
2129             ptp->proposed = true;
2130         cist_proposed = ptp->proposed;
2131         if(!prt->rcvdInternal)
2132             list_for_each_entry_continue(ptp, &prt->trees, port_list)
2133                 ptp->proposed = cist_proposed;
2134         return;
2135     }
2136     /* MSTI */
2137     if(ptp->rcvdMstiConfig->flags & (1 << offsetProposal))
2138         ptp->proposed = true;
2139 }
2140 
2141 /* 13.26.11 recordTimes */
2142 static void recordTimes(per_tree_port_t *ptp)
2143 {
2144     /* 802.1Q-2005 and 802.1D-2004 both say that we have to copy
2145      *   Hello_Time from msgTimes to portTimes.
2146      * 802.1Q-2011, on the other hand, says that Hello_Time should be set
2147      *   to the default here.
2148      * As we have configurable Hello_Time, I choose the third option:
2149      *   preserve the configured Hello_Time, It is in accordance with the
2150      *   spirit of 802.1Q-2011, if we allow Hello_Time to be configurable.
2151      */
2152     __u8 prev_Hello_Time = 0;
2153     assign(prev_Hello_Time, ptp->portTimes.Hello_Time);
2154     assign(ptp->portTimes, ptp->msgTimes);
2155     assign(ptp->portTimes.Hello_Time, prev_Hello_Time);
2156 }
2157 
2158 /* 13.24.s) + 17.19.7 of 802.1D : fdbFlush */
2159 static void set_fdbFlush(per_tree_port_t *ptp)
2160 {
2161     port_t *prt = ptp->port;
2162 
2163     if(prt->operEdge || prt->deleted)
2164     {
2165         ptp->fdbFlush = false;
2166         return;
2167     }
2168 
2169     bridge_t *br = prt->bridge;
2170 
2171     if(rstpVersion(br))
2172     {
2173         ptp->fdbFlush = true;
2174         ptp->calledFromFlushRoutine = true;
2175         MSTP_OUT_flush_all_fids(ptp);
2176         ptp->calledFromFlushRoutine = false;
2177     }
2178     else
2179     {
2180         per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
2181         unsigned int FwdDelay = cist->designatedTimes.Forward_Delay;
2182         /* Initiate rapid ageing */
2183         MSTP_OUT_set_ageing_time(prt, FwdDelay);
2184         assign(prt->rapidAgeingWhile, FwdDelay);
2185         ptp->fdbFlush = false;
2186     }
2187 }
2188 
2189 /* 13.26.12 setRcvdMsgs */
2190 static void setRcvdMsgs(port_t *prt)
2191 {
2192     msti_configuration_message_t *msti_msg;
2193     int i;
2194     __be16 msg_MSTID;
2195     bool found;
2196     per_tree_port_t *ptp = GET_CIST_PTP_FROM_PORT(prt);
2197     ptp->rcvdMsg = true;
2198 
2199     /* 802.1Q-2005 says:
2200      *   "Make the received CST or CIST message available to the CIST Port
2201      *    Information state machines"
2202      * No need to do something special here, we already have rcvdBpduData.
2203      */
2204 
2205     if(prt->rcvdInternal)
2206     {
2207         list_for_each_entry_continue(ptp, &prt->trees, port_list)
2208         {
2209             found = false;
2210             /* Find if message for this MSTI is conveyed in the BPDU */
2211             for(i = 0, msti_msg = prt->rcvdBpduData.mstConfiguration;
2212                 i < prt->rcvdBpduNumOfMstis;
2213                 ++i, ++msti_msg)
2214             {
2215                 msg_MSTID = msti_msg->mstiRRootID.s.priority
2216                             & __constant_cpu_to_be16(0x0FFF);
2217                 if(msg_MSTID == ptp->MSTID)
2218                 {
2219                     found = true;
2220                     break;
2221                 }
2222             }
2223             if(found)
2224             {
2225                 ptp->rcvdMsg = true;
2226                 /* 802.1Q-2005 says:
2227                  *   "Make available each MSTI message and the common parts of
2228                  *    the CIST message priority (the CIST Root Identifier,
2229                  *    External Root Path Cost and Regional Root Identifier)
2230                  *    to the Port Information state machine for that MSTI"
2231                  * We set pointer to the MSTI configuration message for
2232                  * fast access, while do not anything special for common
2233                  * parts of the message, as the whole message is available
2234                  * in rcvdBpduData.
2235                  */
2236                 ptp->rcvdMstiConfig = msti_msg;
2237             }
2238         }
2239     }
2240 }
2241 
2242 /* 13.26.13 setReRootTree */
2243 static void setReRootTree(tree_t *tree)
2244 {
2245     per_tree_port_t *ptp;
2246 
2247     FOREACH_PTP_IN_TREE(ptp, tree)
2248         ptp->reRoot = true;
2249 }
2250 
2251 /* 13.26.14 setSelectedTree */
2252 static void setSelectedTree(tree_t *tree)
2253 {
2254     per_tree_port_t *ptp;
2255 
2256     /*
2257      * 802.1Q-2005 says that I should check "reselect" var
2258      * and take no action if it is "true" for any of the ports.
2259      * But there is no need in this check as setSelectedTree is called
2260      * only from PRSSM_to_ROLE_SELECTION, which is atomic, and it is called
2261      * in this sequence (13.33):
2262      *   clearReselectTree(tree);
2263      *   updtRolesTree(tree);
2264      *   setSelectedTree(tree);
2265      * And we know that clearReselectTree resets "reselect" for all ports
2266      * and updtRolesTree() does not change value of "reselect".
2267      */
2268     FOREACH_PTP_IN_TREE(ptp, tree)
2269         ptp->selected = true;
2270 }
2271 
2272 /* 13.26.15 setSyncTree */
2273 static void setSyncTree(tree_t *tree)
2274 {
2275     per_tree_port_t *ptp;
2276 
2277     FOREACH_PTP_IN_TREE(ptp, tree)
2278         ptp->sync = true;
2279 }
2280 
2281 /* 13.26.16 setTcFlags */
2282 static void setTcFlags(per_tree_port_t *ptp)
2283 {
2284     __u8 cistFlags;
2285     port_t *prt;
2286 
2287     if(0 == ptp->MSTID)
2288     { /* CIST */
2289         prt = ptp->port;
2290         cistFlags = prt->rcvdBpduData.flags;
2291         if(cistFlags & (1 << offsetTcAck))
2292             prt->rcvdTcAck = true;
2293         if(cistFlags & (1 << offsetTc))
2294         {
2295             ptp->rcvdTc = true;
2296             if(!prt->rcvdInternal)
2297                 list_for_each_entry_continue(ptp, &prt->trees, port_list)
2298                     ptp->proposed = true;
2299         }
2300         return;
2301     }
2302     /* MSTI */
2303     if(ptp->rcvdMstiConfig->flags & (1 << offsetTc))
2304         ptp->rcvdTc = true;
2305 }
2306 
2307 /* 13.26.17 setTcPropTree */
2308 static void setTcPropTree(per_tree_port_t *ptp)
2309 {
2310     per_tree_port_t *ptp_1;
2311 
2312     if(ptp->port->restrictedTcn)
2313         return;
2314 
2315     FOREACH_PTP_IN_TREE(ptp_1, ptp->tree)
2316     {
2317         if(ptp != ptp_1)
2318             ptp_1->tcProp = true;
2319     }
2320 }
2321 
2322 /* 13.26.18 syncMaster */
2323 static void syncMaster(bridge_t *br)
2324 {
2325     per_tree_port_t *ptp;
2326     tree_t *tree = GET_CIST_TREE(br);
2327 
2328     /* For each MSTI */
2329     list_for_each_entry_continue(tree, &br->trees, bridge_list)
2330     {
2331         FOREACH_PTP_IN_TREE(ptp, tree)
2332         {
2333             /* for each Port that has infoInternal set */
2334             if(ptp->port->infoInternal)
2335             {
2336                 ptp->agree = false;
2337                 ptp->agreed = false;
2338                 ptp->synced = false;
2339                 ptp->sync = true;
2340             }
2341         }
2342     }
2343 }
2344 
2345 /* 13.26.19 txConfig */
2346 static void txConfig(port_t *prt)
2347 {
2348     bpdu_t b;
2349     per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
2350 
2351     if(prt->deleted || (roleDisabled == cist->role) || prt->dontTxmtBpdu)
2352         return;
2353 
2354     b.protocolIdentifier = 0;
2355     b.protocolVersion = protoSTP;
2356     b.bpduType = bpduTypeConfig;
2357     /* Standard says "tcWhile ... for the Port". Which one tcWhile?
2358      * I guess that this means tcWhile for the CIST.
2359      * But that is only a guess and I could be wrong here ;)
2360      */
2361     b.flags = (0 != cist->tcWhile) ? (1 << offsetTc) : 0;
2362     if(prt->tcAck)
2363         b.flags |= (1 << offsetTcAck);
2364     assign(b.cistRootID, cist->designatedPriority.RootID);
2365     assign(b.cistExtRootPathCost, cist->designatedPriority.ExtRootPathCost);
2366     assign(b.cistRRootID, cist->designatedPriority.DesignatedBridgeID);
2367     assign(b.cistPortID, cist->designatedPriority.DesignatedPortID);
2368     b.MessageAge[0] = cist->designatedTimes.Message_Age;
2369     b.MessageAge[1] = 0;
2370     b.MaxAge[0] = cist->designatedTimes.Max_Age;
2371     b.MaxAge[1] = 0;
2372     b.HelloTime[0] = cist->portTimes.Hello_Time; /* ! use portTimes ! */
2373     b.HelloTime[1] = 0;
2374     b.ForwardDelay[0] = cist->designatedTimes.Forward_Delay;
2375     b.ForwardDelay[1] = 0;
2376 
2377     MSTP_OUT_tx_bpdu(prt, &b, CONFIG_BPDU_SIZE);
2378 }
2379 
2380 static inline __u8 message_role_from_port_role(per_tree_port_t *ptp)
2381 {
2382     switch(ptp->role)
2383     {
2384         case roleRoot:
2385             return encodedRoleRoot;
2386         case roleDesignated:
2387             return encodedRoleDesignated;
2388         case roleAlternate:
2389         case roleBackup:
2390             return encodedRoleAlternateBackup;
2391         case roleMaster:
2392             return encodedRoleMaster;
2393         default:
2394             ERROR_PRTNAME(ptp->port->bridge, ptp->port,
2395                           "Attempt to send from port with Disabled role");
2396             return encodedRoleAlternateBackup;
2397     }
2398 }
2399 
2400 /* 802.1Q-2005: 13.26.20 txMstp
2401  * 802.1Q-2011: 13.27.27 txRstp
2402  */
2403 static void txMstp(port_t *prt)
2404 {
2405     bpdu_t b;
2406     bridge_t *br = prt->bridge;
2407     per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
2408     int msti_msgs_total_size;
2409     per_tree_port_t *ptp;
2410     msti_configuration_message_t *msti_msg;
2411 
2412     if(prt->deleted || (roleDisabled == cist->role) || prt->dontTxmtBpdu)
2413         return;
2414 
2415     b.protocolIdentifier = 0;
2416     b.bpduType = bpduTypeRST;
2417     /* Standard says "{tcWhile, agree, proposing} ... for the Port".
2418      * Which one {tcWhile, agree, proposing}?
2419      * I guess that this means {tcWhile, agree, proposing} for the CIST.
2420      * But that is only a guess and I could be wrong here ;)
2421      */
2422     b.flags = BPDU_FLAGS_ROLE_SET(message_role_from_port_role(cist));
2423     if(0 != cist->tcWhile)
2424         b.flags |= (1 << offsetTc);
2425     if(cist->proposing)
2426         b.flags |= (1 << offsetProposal);
2427     if(cist->learning)
2428         b.flags |= (1 << offsetLearnig);
2429     if(cist->forwarding)
2430         b.flags |= (1 << offsetForwarding);
2431     if(cist->agree)
2432         b.flags |= (1 << offsetAgreement);
2433     assign(b.cistRootID, cist->designatedPriority.RootID);
2434     assign(b.cistExtRootPathCost, cist->designatedPriority.ExtRootPathCost);
2435     assign(b.cistRRootID, cist->designatedPriority.RRootID);
2436     assign(b.cistPortID, cist->designatedPriority.DesignatedPortID);
2437     b.MessageAge[0] = cist->designatedTimes.Message_Age;
2438     b.MessageAge[1] = 0;
2439     b.MaxAge[0] = cist->designatedTimes.Max_Age;
2440     b.MaxAge[1] = 0;
2441     b.HelloTime[0] = cist->portTimes.Hello_Time; /* ! use portTimes ! */
2442     b.HelloTime[1] = 0;
2443     b.ForwardDelay[0] = cist->designatedTimes.Forward_Delay;
2444     b.ForwardDelay[1] = 0;
2445 
2446     b.version1_len = 0;
2447 
2448     if(br->ForceProtocolVersion < protoMSTP)
2449     {
2450         b.protocolVersion = protoRSTP;
2451         MSTP_OUT_tx_bpdu(prt, &b, RST_BPDU_SIZE);
2452         return;
2453     }
2454 
2455     b.protocolVersion = protoMSTP;
2456 
2457     /* MST specific fields */
2458     assign(b.mstConfigurationIdentifier, br->MstConfigId);
2459     assign(b.cistIntRootPathCost, cist->designatedPriority.IntRootPathCost);
2460     assign(b.cistBridgeID, cist->designatedPriority.DesignatedBridgeID);
2461     assign(b.cistRemainingHops, cist->designatedTimes.remainingHops);
2462 
2463     msti_msgs_total_size = 0;
2464     ptp = cist;
2465     msti_msg = b.mstConfiguration;
2466     /* 13.26.20.f) requires that msti configs should be inserted in
2467      * MSTID order. This is met by inserting trees in port's list of trees
2468      * in sorted (by MSTID) order (see MSTP_IN_create_msti) */
2469     list_for_each_entry_continue(ptp, &prt->trees, port_list)
2470     {
2471         msti_msg->flags =
2472             BPDU_FLAGS_ROLE_SET(message_role_from_port_role(ptp));
2473         if(0 != ptp->tcWhile)
2474             msti_msg->flags |= (1 << offsetTc);
2475         if(ptp->proposing)
2476             msti_msg->flags |= (1 << offsetProposal);
2477         if(ptp->learning)
2478             msti_msg->flags |= (1 << offsetLearnig);
2479         if(ptp->forwarding)
2480             msti_msg->flags |= (1 << offsetForwarding);
2481         if(ptp->agree)
2482             msti_msg->flags |= (1 << offsetAgreement);
2483         if(ptp->master)
2484             msti_msg->flags |= (1 << offsetMaster);
2485         assign(msti_msg->mstiRRootID, ptp->designatedPriority.RRootID);
2486         assign(msti_msg->mstiIntRootPathCost,
2487                ptp->designatedPriority.IntRootPathCost);
2488         msti_msg->bridgeIdentifierPriority =
2489             GET_PRIORITY_FROM_IDENTIFIER(ptp->designatedPriority.DesignatedBridgeID);
2490         msti_msg->portIdentifierPriority =
2491             GET_PRIORITY_FROM_IDENTIFIER(ptp->designatedPriority.DesignatedPortID);
2492         assign(msti_msg->remainingHops, ptp->designatedTimes.remainingHops);
2493 
2494         msti_msgs_total_size += sizeof(msti_configuration_message_t);
2495         ++msti_msg;
2496     }
2497 
2498     assign(b.version3_len, __cpu_to_be16(MST_BPDU_VER3LEN_WO_MSTI_MSGS
2499                                          + msti_msgs_total_size));
2500     MSTP_OUT_tx_bpdu(prt, &b, MST_BPDU_SIZE_WO_MSTI_MSGS
2501                               + msti_msgs_total_size);
2502 }
2503 
2504 /* 13.26.a) txTcn */
2505 static void txTcn(port_t *prt)
2506 {
2507     bpdu_t b;
2508     per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
2509 
2510     if(prt->deleted || (roleDisabled == cist->role) || prt->dontTxmtBpdu)
2511         return;
2512 
2513     b.protocolIdentifier = 0;
2514     b.protocolVersion = protoSTP;
2515     b.bpduType = bpduTypeTCN;
2516 
2517     MSTP_OUT_tx_bpdu(prt, &b, TCN_BPDU_SIZE);
2518 }
2519 
2520 /* 13.26.21 updtBPDUVersion */
2521 static void updtBPDUVersion(port_t *prt)
2522 {
2523     if(protoRSTP <= prt->rcvdBpduData.protocolVersion)
2524         prt->rcvdRSTP = true;
2525     else
2526         prt->rcvdSTP = true;
2527 }
2528 
2529 /* 13.26.22 updtRcvdInfoWhile */
2530 static void updtRcvdInfoWhile(per_tree_port_t *ptp)
2531 {
2532     port_t *prt = ptp->port;
2533     per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
2534     unsigned int Message_Age = cist->portTimes.Message_Age;
2535     unsigned int Max_Age = cist->portTimes.Max_Age;
2536     unsigned int Hello_Time = cist->portTimes.Hello_Time;
2537 
2538     /* NOTE: 802.1Q-2005(-2011) says that we should use
2539      *  "remainingHops ... from the CIST's portTimes parameter"
2540      *  As for me this is clear oversight in the standard,
2541      *  the remainingHops should be taken form the port's own portTimes,
2542      *  not from CIST's. After all, if we don't use port's own
2543      *  remainingHops here, they aren't used anywhere at all.
2544      *  Besides, there is a scenario which breaks if we use CIST's
2545      *  remainingHops here:
2546      *   1) Connect two switches (SW1,SW2) with two ports, thus forming a loop
2547      *   2) Configure them to be in the same region, with two trees:
2548      *      0 (CIST) and 1.
2549      *   3) at SW1# mstpctl settreeprio br0 1 4
2550      *      SW1 becomes regional root in tree 1
2551      *   4) at SW2# mstpctl settreeprio br0 1 14
2552      *   5) at SW1# mstpctl settreeprio br0 1 9
2553      *
2554      *  And now we have the classic "count-to-infinity" problem when the old
2555      *  info ("Regional Root is SW1 with priority 4") circulates in the loop,
2556      *  because it is better than current info ("Regional Root is SW1 with
2557      *  priority 9"). The only way to get rid of that old info is
2558      *  to age it out by the means of remainingHops counter.
2559      *  In this situation we certainly must use counter from tree 1,
2560      *  not CIST's.
2561      */
2562     if((!prt->rcvdInternal && ((Message_Age + 1) <= Max_Age))
2563        || (prt->rcvdInternal && (ptp->portTimes.remainingHops > 1))
2564       )
2565         ptp->rcvdInfoWhile = 3 * Hello_Time;
2566     else
2567         ptp->rcvdInfoWhile = 0;
2568 }
2569 
2570 static void updtbrAssuRcvdInfoWhile(port_t *prt)
2571 {
2572     per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
2573 
2574     prt->brAssuRcvdInfoWhile = 3 * cist->portTimes.Hello_Time;
2575 }
2576 
2577 /* 13.26.24 updtRolesDisabledTree */
2578 static void updtRolesDisabledTree(tree_t *tree)
2579 {
2580     per_tree_port_t *ptp;
2581 
2582     FOREACH_PTP_IN_TREE(ptp, tree)
2583         ptp->selectedRole = roleDisabled;
2584 }
2585 
2586 /* Aux function, not in standard.
2587  * Sets reselect for all MSTIs in the case CIST state for the port changes
2588  */
2589 static void reselectMSTIs(port_t *prt)
2590 {
2591     per_tree_port_t *ptp = GET_CIST_PTP_FROM_PORT(prt);
2592 
2593     /* For each non-CIST ptp */
2594     list_for_each_entry_continue(ptp, &prt->trees, port_list)
2595         ptp->reselect = true;
2596 }
2597 
2598 /* 13.26.23 updtRolesTree */
2599 static void updtRolesTree(tree_t *tree)
2600 {
2601     per_tree_port_t *ptp, *root_ptp = NULL;
2602     port_priority_vector_t root_path_priority;
2603     bridge_identifier_t prevRRootID = tree->rootPriority.RRootID;
2604     __be32 prevExtRootPathCost = tree->rootPriority.ExtRootPathCost;
2605     bool cist = (0 == tree->MSTID);
2606 
2607     /* a), b) Select new root priority vector = {rootPriority, rootPortId} */
2608       /* Initial value = bridge priority vector = {BridgePriority, 0} */
2609     assign(tree->rootPriority, tree->BridgePriority);
2610     assign(tree->rootPortId, __constant_cpu_to_be16(0));
2611       /* Now check root path priority vectors of all ports in tree and see if
2612        * there is a better vector */
2613     FOREACH_PTP_IN_TREE(ptp, tree)
2614     {
2615         port_t *prt = ptp->port;
2616         /* 802.1Q says to calculate root priority vector only if port
2617          * is not Disabled, but check (infoIs == ioReceived) covers
2618          * the case (infoIs != ioDisabled).
2619          */
2620         if((ioReceived == ptp->infoIs) && !prt->restrictedRole
2621            && cmp(ptp->portPriority.DesignatedBridgeID, !=,
2622                   tree->BridgeIdentifier)
2623           )
2624         {
2625             root_path_priority = ptp->portPriority;
2626             if(prt->rcvdInternal)
2627             {
2628                 assign(root_path_priority.IntRootPathCost,
2629                        __cpu_to_be32(__be32_to_cpu(root_path_priority.IntRootPathCost)
2630                                      + ptp->InternalPortPathCost)
2631                       );
2632             }
2633             else if(cist) /* Yes, this check might be superfluous,
2634                            * but I want to be on the safe side */
2635             {
2636                 assign(root_path_priority.ExtRootPathCost,
2637                        __cpu_to_be32(__be32_to_cpu(root_path_priority.ExtRootPathCost)
2638                                      + prt->ExternalPortPathCost)
2639                       );
2640                 assign(root_path_priority.RRootID, tree->BridgeIdentifier);
2641                 assign(root_path_priority.IntRootPathCost,
2642                        __constant_cpu_to_be32(0));
2643             }
2644             if(betterorsamePriority(&root_path_priority, &tree->rootPriority,
2645                                     ptp->portId, tree->rootPortId, cist))
2646             {
2647                 assign(tree->rootPriority, root_path_priority);
2648                 assign(tree->rootPortId, ptp->portId);
2649                 root_ptp = ptp;
2650             }
2651         }
2652     }
2653 
2654     /* 802.1q-2005 says, that at some point we need compare portTimes with
2655      * "... one for the Root Port ...". Bad IEEE! Why not mention explicit
2656      * var names??? (see 13.26.23.g) for instance)
2657      * These comparisons happen three times, twice in clause g)
2658      *   and once in clause i). Look for samePriorityAndTimers() calls.
2659      * So, now I should guess what will work for the "times for the Root Port".
2660      * Thanks to Rajani's experiments I know for sure that I should use
2661      *  designatedTimes here. Thank you, Rajani!
2662      * NOTE: Both Alex Rozin (author of rstplib) and Srinivas Aji (author
2663      *   of rstpd) also compare portTimes with designatedTimes.
2664      */
2665 
2666     /* c) Set new rootTimes */
2667     if(root_ptp)
2668     {
2669         assign(tree->rootTimes, root_ptp->portTimes);
2670         port_t *prt = root_ptp->port;
2671         if(prt->rcvdInternal)
2672         {
2673             if(tree->rootTimes.remainingHops)
2674                 --(tree->rootTimes.remainingHops);
2675         }
2676         else
2677             ++(tree->rootTimes.Message_Age);
2678     }
2679     else
2680     {
2681         assign(tree->rootTimes, tree->BridgeTimes);
2682     }
2683 
2684     FOREACH_PTP_IN_TREE(ptp, tree)
2685     {
2686         port_t *prt = ptp->port;
2687 
2688         /* d) Set new designatedPriority */
2689         assign(ptp->designatedPriority, tree->rootPriority);
2690         assign(ptp->designatedPriority.DesignatedBridgeID,
2691                tree->BridgeIdentifier);
2692         assign(ptp->designatedPriority.DesignatedPortID, ptp->portId);
2693         /* I am not sure which condition to check here, as 802.1Q-2005 says:
2694          * "... If {Port} is attached to a LAN that has one or more STP Bridges
2695          *  attached (as determined by the Port Protocol Migration state
2696          * machine) ..." -- why not to mention explicit var name? Bad IEEE.
2697          * But I guess that sendSTP (i.e. !sendRSTP) var will do ;)
2698          */
2699         if(cist && !prt->sendRSTP)
2700             assign(ptp->designatedPriority.RRootID, tree->BridgeIdentifier);
2701 
2702         /* e) Set new designatedTimes */
2703         assign(ptp->designatedTimes, tree->rootTimes);
2704         /* Keep the configured Hello_Time for the port.
2705          * NOTE: this is in accordance with the spirit of 802.1D-2004.
2706          *    Also, this does not contradict 802.1Q-2005(-2011), as in these
2707          *    standards both designatedTimes and rootTimes structures
2708          *    don't have Hello_Time member.
2709          */
2710         assign(ptp->designatedTimes.Hello_Time, ptp->portTimes.Hello_Time);
2711     }
2712 
2713     /* syncMaster */
2714     if(cist && cmp(tree->rootPriority.RRootID, !=, prevRRootID)
2715        && ((0 != tree->rootPriority.ExtRootPathCost)
2716            || (0 != prevExtRootPathCost)
2717           )
2718       )
2719         syncMaster(tree->bridge);
2720 
2721     FOREACH_PTP_IN_TREE(ptp, tree)
2722     {
2723         port_t *prt = ptp->port;
2724         per_tree_port_t *cist_tree = GET_CIST_PTP_FROM_PORT(prt);
2725 
2726         /* f) Set Disabled role */
2727         if(ioDisabled == ptp->infoIs)
2728         {
2729             ptp->selectedRole = roleDisabled;
2730             continue;
2731         }
2732 
2733         if(!cist && (ioReceived == cist_tree->infoIs) && !prt->infoInternal)
2734         {
2735             /* g) Set role for the boundary port in MSTI */
2736             if(roleRoot == cist_tree->selectedRole)
2737             {
2738                 ptp->selectedRole = roleMaster;
2739                 if(!samePriorityAndTimers(&ptp->portPriority,
2740                                           &ptp->designatedPriority,
2741                                           &ptp->portTimes,
2742                                           &ptp->designatedTimes,
2743                                           /*cist*/ false))
2744                     ptp->updtInfo = true;
2745                 continue;
2746             }
2747             /* Bad IEEE again! It says in 13.26.23 g) 2) that
2748              * MSTI state should follow CIST state only for the case of
2749              * Alternate port. This is obviously wrong!
2750              * In the descriptive clause 13.13 f) it says:
2751              *  "At a Boundary Port frames allocated to the CIST and
2752              *   all MSTIs are forwarded or not forwarded alike.
2753              *   This is because Port Role assignments are such that
2754              *   if the CIST Port Role is Root Port, the MSTI Port Role
2755              *   will be Master Port, and if the CIST Port Role is
2756              *   Designated Port, Alternate Port, Backup Port,
2757              *   or Disabled Port, each MSTI’s Port Role will be the same."
2758              * So, ignore wrong 13.26.23 g) 2) and do as stated in 13.13 f) !
2759              */
2760             /* if(roleAlternate == cist_tree->selectedRole) */
2761             {
2762                 ptp->selectedRole = cist_tree->selectedRole;
2763                 if(!samePriorityAndTimers(&ptp->portPriority,
2764                                           &ptp->designatedPriority,
2765                                           &ptp->portTimes,
2766                                           &ptp->designatedTimes,
2767                                           /*cist*/ false))
2768                     ptp->updtInfo = true;
2769                 continue;
2770             }
2771         }
2772         else
2773      /* if(cist || (ioReceived != cist_tree->infoIs) || prt->infoInternal) */
2774         {
2775             /* h) Set role for the aged info */
2776             if(ioAged == ptp->infoIs)
2777             {
2778                 ptp->selectedRole = roleDesignated;
2779                 ptp->updtInfo = true;
2780                 continue;
2781             }
2782             /* i) Set role for the mine info */
2783             if(ioMine == ptp->infoIs)
2784             {
2785                 ptp->selectedRole = roleDesignated;
2786                 if(!samePriorityAndTimers(&ptp->portPriority,
2787                                           &ptp->designatedPriority,
2788                                           &ptp->portTimes,
2789                                           &ptp->designatedTimes,
2790                                           cist))
2791                     ptp->updtInfo = true;
2792                 continue;
2793             }
2794             if(ioReceived == ptp->infoIs)
2795             {
2796                 /* j) Set Root role */
2797                 if(root_ptp == ptp)
2798                 {
2799                     ptp->selectedRole = roleRoot;
2800                     ptp->updtInfo = false;
2801                 }
2802                 else
2803                 {
2804                     if(betterorsamePriority(&ptp->portPriority,
2805                                              &ptp->designatedPriority,
2806                                              0, 0, cist))
2807                     {
2808                         if(cmp(ptp->portPriority.DesignatedBridgeID, !=,
2809                                tree->BridgeIdentifier))
2810                         {
2811                             /* k) Set Alternate role */
2812                             ptp->selectedRole = roleAlternate;
2813                         }
2814                         else
2815                         {
2816                             /* l) Set Backup role */
2817                             ptp->selectedRole = roleBackup;
2818                         }
2819                         /* reset updtInfo for both k) and l) */
2820                         ptp->updtInfo = false;
2821                     }
2822                     else /* designatedPriority is better than portPriority */
2823                     {
2824                         /* m) Set Designated role */
2825                         ptp->selectedRole = roleDesignated;
2826                         ptp->updtInfo = true;
2827                     }
2828                 }
2829                 /* This is not in standard. But we really should set here
2830                  * reselect for all MSTIs so that updtRolesTree is called
2831                  * for each MSTI and due to above clause g) MSTI role is
2832                  * changed to Master or reflects CIST port role.
2833                  * Because in 802.1Q-2005 this will not happen when BPDU arrives
2834                  * at boundary port - the rcvdMsg is not set for the MSTIs and
2835                  * updtRolesTree is not called.
2836                  * Bad IEEE !!!
2837                  */
2838                 if(cist && (ptp->selectedRole != ptp->role))
2839                     reselectMSTIs(prt);
2840                 continue;
2841             }
2842         }
2843     }
2844 }
2845 
2846 /* 13.27  The Port Timers state machine */
2847 
2848 static void PTSM_tick(port_t *prt)
2849 {
2850     per_tree_port_t *ptp;
2851 
2852     if(prt->helloWhen)
2853         --(prt->helloWhen);
2854     if(prt->mdelayWhile)
2855         --(prt->mdelayWhile);
2856     if(prt->edgeDelayWhile)
2857         --(prt->edgeDelayWhile);
2858     if(prt->txCount)
2859         --(prt->txCount);
2860     if(prt->brAssuRcvdInfoWhile)
2861         --(prt->brAssuRcvdInfoWhile);
2862 
2863     FOREACH_PTP_IN_PORT(ptp, prt)
2864     {
2865         if(ptp->fdWhile)
2866             --(ptp->fdWhile);
2867         if(ptp->rrWhile)
2868             --(ptp->rrWhile);
2869         if(ptp->rbWhile)
2870             --(ptp->rbWhile);
2871         if(ptp->tcWhile)
2872         {
2873             if(0 == --(ptp->tcWhile))
2874                 set_TopologyChange(ptp->tree, false, prt);
2875         }
2876         if(ptp->rcvdInfoWhile)
2877             --(ptp->rcvdInfoWhile);
2878     }
2879 }
2880 
2881 /* 13.28  Port Receive state machine */
2882 #define PRSM_begin(prt) PRSM_to_DISCARD((prt), false)
2883 static bool PRSM_to_DISCARD(port_t *prt, bool dry_run)
2884 {
2885     if(dry_run)
2886     {
2887         return (prt->PRSM_state != PRSM_DISCARD)
2888                || prt->rcvdBpdu || prt->rcvdRSTP || prt->rcvdSTP
2889                || (prt->edgeDelayWhile != prt->bridge->Migrate_Time)
2890                || clearAllRcvdMsgs(prt, dry_run);
2891     }
2892 
2893     prt->PRSM_state = PRSM_DISCARD;
2894 
2895     prt->rcvdBpdu = false;
2896     prt->rcvdRSTP = false;
2897     prt->rcvdSTP = false;
2898     clearAllRcvdMsgs(prt, false /* actual run */);
2899     assign(prt->edgeDelayWhile, prt->bridge->Migrate_Time);
2900 
2901     /* No need to run, no one condition will be met
2902      * if(!begin)
2903      *     PRSM_run(prt, false); */
2904     return false;
2905 }
2906 
2907 static void PRSM_to_RECEIVE(port_t *prt)
2908 {
2909     prt->PRSM_state = PRSM_RECEIVE;
2910 
2911     updtBPDUVersion(prt);
2912     prt->rcvdInternal = fromSameRegion(prt);
2913     setRcvdMsgs(prt);
2914     prt->operEdge = false;
2915     prt->rcvdBpdu = false;
2916     assign(prt->edgeDelayWhile, prt->bridge->Migrate_Time);
2917 
2918     /* No need to run, no one condition will be met
2919       PRSM_run(prt, false); */
2920 }
2921 
2922 static bool PRSM_run(port_t *prt, bool dry_run)
2923 {
2924     per_tree_port_t *ptp;
2925     bool rcvdAnyMsg;
2926 
2927     if((prt->rcvdBpdu || (prt->edgeDelayWhile != prt->bridge->Migrate_Time))
2928        && !prt->portEnabled)
2929     {
2930         return PRSM_to_DISCARD(prt, dry_run);
2931     }
2932 
2933     switch(prt->PRSM_state)
2934     {
2935         case PRSM_DISCARD:
2936             if(prt->rcvdBpdu && prt->portEnabled)
2937             {
2938                 if(dry_run) /* state change */
2939                     return true;
2940                 PRSM_to_RECEIVE(prt);
2941             }
2942             return false;
2943         case PRSM_RECEIVE:
2944             rcvdAnyMsg = false;
2945             FOREACH_PTP_IN_PORT(ptp, prt)
2946             {
2947                 if(ptp->rcvdMsg)
2948                 {
2949                     rcvdAnyMsg = true;
2950                     break;
2951                 }
2952             }
2953             if(prt->rcvdBpdu && prt->portEnabled && !rcvdAnyMsg)
2954             {
2955                 if(dry_run) /* at least rcvdBpdu will change */
2956                     return true;
2957                 PRSM_to_RECEIVE(prt);
2958             }
2959         default:
2960             return false;
2961     }
2962 }
2963 
2964 /* 13.29  Port Protocol Migration state machine */
2965 
2966 static bool PPMSM_run(port_t *prt, bool dry_run);
2967 #define PPMSM_begin(prt) PPMSM_to_CHECKING_RSTP(prt)
2968 
2969 static void PPMSM_to_CHECKING_RSTP(port_t *prt/*, bool begin*/)
2970 {
2971     prt->PPMSM_state = PPMSM_CHECKING_RSTP;
2972 
2973     bridge_t *br = prt->bridge;
2974     prt->mcheck = false;
2975     prt->sendRSTP = rstpVersion(br);
2976     assign(prt->mdelayWhile, br->Migrate_Time);
2977 
2978     /* No need to run, no one condition will be met
2979      * if(!begin)
2980      *     PPMSM_run(prt, false); */
2981 }
2982 
2983 static void PPMSM_to_SELECTING_STP(port_t *prt)
2984 {
2985     prt->PPMSM_state = PPMSM_SELECTING_STP;
2986 
2987     prt->sendRSTP = false;
2988     assign(prt->mdelayWhile, prt->bridge->Migrate_Time);
2989 
2990     PPMSM_run(prt, false /* actual run */);
2991 }
2992 
2993 static void PPMSM_to_SENSING(port_t *prt)
2994 {
2995     prt->PPMSM_state = PPMSM_SENSING;
2996 
2997     prt->rcvdRSTP = false;
2998     prt->rcvdSTP = false;
2999 
3000     PPMSM_run(prt, false /* actual run */);
3001 }
3002 
3003 static bool PPMSM_run(port_t *prt, bool dry_run)
3004 {
3005     bridge_t *br = prt->bridge;
3006 
3007     switch(prt->PPMSM_state)
3008     {
3009         case PPMSM_CHECKING_RSTP:
3010             if((prt->mdelayWhile != br->Migrate_Time)
3011                && !prt->portEnabled)
3012             {
3013                 if(dry_run) /* at least mdelayWhile will change */
3014                     return true;
3015                 PPMSM_to_CHECKING_RSTP(prt);
3016                 return false;
3017             }
3018             if(0 == prt->mdelayWhile)
3019             {
3020                 if(dry_run) /* state change */
3021                     return true;
3022                 PPMSM_to_SENSING(prt);
3023             }
3024             return false;
3025         case PPMSM_SELECTING_STP:
3026             if(0 == prt->mdelayWhile || !prt->portEnabled || prt->mcheck)
3027             {
3028                 if(dry_run) /* state change */
3029                     return true;
3030                 PPMSM_to_SENSING(prt);
3031             }
3032             return false;
3033         case PPMSM_SENSING:
3034             if(!prt->portEnabled || prt->mcheck
3035                || (rstpVersion(br) && !prt->sendRSTP && prt->rcvdRSTP))
3036             {
3037                 if(dry_run) /* state change */
3038                     return true;
3039                 PPMSM_to_CHECKING_RSTP(prt);
3040                 return false;
3041             }
3042             if(prt->sendRSTP && prt->rcvdSTP)
3043             {
3044                 if(dry_run) /* state change */
3045                     return true;
3046                 PPMSM_to_SELECTING_STP(prt);
3047             }
3048             return false;
3049     }
3050 
3051     return false;
3052 }
3053 
3054 /* 13.30  Bridge Detection state machine */
3055 static void BDSM_to_EDGE(port_t *prt/*, bool begin*/)
3056 {
3057     prt->BDSM_state = BDSM_EDGE;
3058 
3059     prt->operEdge = true;
3060 
3061     /* No need to run, no one condition will be met
3062      * if(!begin)
3063      *     BDSM_run(prt, false); */
3064 }
3065 
3066 static void BDSM_to_NOT_EDGE(port_t *prt/*, bool begin*/)
3067 {
3068     prt->BDSM_state = BDSM_NOT_EDGE;
3069 
3070     prt->operEdge = false;
3071 
3072     /* No need to run, no one condition will be met
3073      * if(!begin)
3074      *     BDSM_run(prt, false); */
3075 }
3076 
3077 static void BDSM_begin(port_t *prt/*, bool begin*/)
3078 {
3079     if(prt->AdminEdgePort)
3080         BDSM_to_EDGE(prt/*, begin*/);
3081     else
3082         BDSM_to_NOT_EDGE(prt/*, begin*/);
3083 }
3084 
3085 static bool BDSM_run(port_t *prt, bool dry_run)
3086 {
3087     per_tree_port_t *cist;
3088 
3089     switch(prt->BDSM_state)
3090     {
3091         case BDSM_EDGE:
3092             if(((!prt->portEnabled || !prt->AutoEdge) && !prt->AdminEdgePort)
3093                || !prt->operEdge
3094               )
3095             {
3096                 if(dry_run) /* state change */
3097                     return true;
3098                 BDSM_to_NOT_EDGE(prt);
3099             }
3100             return false;
3101         case BDSM_NOT_EDGE:
3102              cist = GET_CIST_PTP_FROM_PORT(prt);
3103             /* NOTE: 802.1Q-2005(-2011) is not clear, which of the per-tree
3104              *  "proposing" flags to use here, or one should combine
3105              *  them all for all trees?
3106              * So, I decide that it will be the "proposing" flag
3107              *  from CIST tree - it seems like a good bet.
3108              */
3109             if((!prt->portEnabled && prt->AdminEdgePort)
3110                || ((0 == prt->edgeDelayWhile) && prt->AutoEdge && prt->sendRSTP
3111                    && cist->proposing)
3112               )
3113             {
3114                 if(dry_run) /* state change */
3115                     return true;
3116                 BDSM_to_EDGE(prt);
3117             }
3118         default:
3119             return false;
3120     }
3121 }
3122 
3123 /* 13.31  Port Transmit state machine */
3124 
3125 static bool PTSM_run(port_t *prt, bool dry_run);
3126 #define PTSM_begin(prt) PTSM_to_TRANSMIT_INIT((prt), true, false)
3127 
3128 static bool PTSM_to_TRANSMIT_INIT(port_t *prt, bool begin, bool dry_run)
3129 {
3130     if(dry_run)
3131     {
3132         return (prt->PTSM_state != PTSM_TRANSMIT_INIT)
3133                || (!prt->newInfo) || (!prt->newInfoMsti)
3134                || (0 != prt->txCount);
3135     }
3136 
3137     prt->PTSM_state = PTSM_TRANSMIT_INIT;
3138 
3139     prt->newInfo = true;
3140     prt->newInfoMsti = true;
3141     assign(prt->txCount, 0u);
3142 
3143     if(!begin && prt->portEnabled) /* prevent infinite loop */
3144         PTSM_run(prt, false /* actual run */);
3145     return false;
3146 }
3147 
3148 static void PTSM_to_TRANSMIT_CONFIG(port_t *prt)
3149 {
3150     prt->PTSM_state = PTSM_TRANSMIT_CONFIG;
3151 
3152     prt->newInfo = false;
3153     txConfig(prt);
3154     ++(prt->txCount);
3155     prt->tcAck = false;
3156 
3157     PTSM_run(prt, false /* actual run */);
3158 }
3159 
3160 static void PTSM_to_TRANSMIT_TCN(port_t *prt)
3161 {
3162     prt->PTSM_state = PTSM_TRANSMIT_TCN;
3163 
3164     prt->newInfo = false;
3165     txTcn(prt);
3166     ++(prt->txCount);
3167 
3168     PTSM_run(prt, false /* actual run */);
3169 }
3170 
3171 static void PTSM_to_TRANSMIT_RSTP(port_t *prt)
3172 {
3173     prt->PTSM_state = PTSM_TRANSMIT_RSTP;
3174 
3175     prt->newInfo = false;
3176     prt->newInfoMsti = false;
3177     txMstp(prt);
3178     ++(prt->txCount);
3179     prt->tcAck = false;
3180 
3181     PTSM_run(prt, false /* actual run */);
3182 }
3183 
3184 static void PTSM_to_TRANSMIT_PERIODIC(port_t *prt)
3185 {
3186     prt->PTSM_state = PTSM_TRANSMIT_PERIODIC;
3187 
3188     per_tree_port_t *ptp = GET_CIST_PTP_FROM_PORT(prt);
3189     bool cistDesignatedOrTCpropagatingRootPort =
3190         (roleDesignated == ptp->role)
3191         || ((roleRoot == ptp->role) && (0 != ptp->tcWhile));
3192     bool mstiDesignatedOrTCpropagatingRootPort;
3193 
3194     mstiDesignatedOrTCpropagatingRootPort = false;
3195     list_for_each_entry_continue(ptp, &prt->trees, port_list)
3196     {
3197         if((roleDesignated == ptp->role)
3198            || ((roleRoot == ptp->role) && (0 != ptp->tcWhile))
3199           )
3200         {
3201             mstiDesignatedOrTCpropagatingRootPort = true;
3202             break;
3203         }
3204     }
3205 
3206     prt->newInfo = prt->newInfo || cistDesignatedOrTCpropagatingRootPort;
3207     prt->newInfoMsti = prt->newInfoMsti
3208                        || mstiDesignatedOrTCpropagatingRootPort;
3209 
3210     PTSM_run(prt, false /* actual run */);
3211 }
3212 
3213 static void PTSM_to_IDLE(port_t *prt)
3214 {
3215     prt->PTSM_state = PTSM_IDLE;
3216 
3217     per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
3218     prt->helloWhen = cist->portTimes.Hello_Time;
3219 
3220     PTSM_run(prt, false /* actual run */);
3221 }
3222 
3223 static bool PTSM_run(port_t *prt, bool dry_run)
3224 {
3225    /* bool allTransmitReady; */
3226     per_tree_port_t *ptp;
3227     port_role_t cistRole;
3228     bool mstiMasterPort;
3229 
3230     if(!prt->portEnabled)
3231     {
3232         return PTSM_to_TRANSMIT_INIT(prt, false, dry_run);
3233     }
3234 
3235     switch(prt->PTSM_state)
3236     {
3237         case PTSM_TRANSMIT_INIT:
3238            /* return; */
3239         case PTSM_TRANSMIT_CONFIG:
3240            /* return; */
3241         case PTSM_TRANSMIT_TCN:
3242            /* return; */
3243         case PTSM_TRANSMIT_RSTP:
3244            /* return; */
3245         case PTSM_TRANSMIT_PERIODIC:
3246             if(dry_run) /* state change */
3247                 return true;
3248             PTSM_to_IDLE(prt); /* UnConditional Transition */
3249             return false;
3250         case PTSM_IDLE:
3251             /* allTransmitReady = true; */
3252             ptp = GET_CIST_PTP_FROM_PORT(prt);
3253             if(!ptp->selected || ptp->updtInfo)
3254             {
3255                 /* allTransmitReady = false; */
3256                 return false;
3257             }
3258             cistRole = ptp->role;
3259             mstiMasterPort = false;
3260             list_for_each_entry_continue(ptp, &prt->trees, port_list)
3261             {
3262                 if(!ptp->selected || ptp->updtInfo)
3263                 {
3264                     /* allTransmitReady = false; */
3265                     return false;
3266                 }
3267                 if(roleMaster == ptp->role)
3268                     mstiMasterPort = true;
3269             }
3270             if(0 == prt->helloWhen)
3271             {
3272                 if(dry_run) /* state change */
3273                     return true;
3274                 PTSM_to_TRANSMIT_PERIODIC(prt);
3275                 return false;
3276             }
3277             if(!(prt->txCount < prt->bridge->Transmit_Hold_Count))
3278                 return false;
3279 
3280             if(prt->bpduFilterPort)
3281                 return false;
3282 
3283             if(prt->sendRSTP)
3284             { /* implement MSTP */
3285                 if(prt->newInfo || (prt->newInfoMsti && !mstiMasterPort)
3286                    || assurancePort(prt)
3287                   )
3288                 {
3289                     if(dry_run) /* state change */
3290                         return true;
3291                     PTSM_to_TRANSMIT_RSTP(prt);
3292                     return false;
3293                 }
3294             }
3295             else
3296             { /* fallback to STP */
3297                 if(prt->newInfo && (roleDesignated == cistRole))
3298                 {
3299                     if(dry_run) /* state change */
3300                         return true;
3301                     PTSM_to_TRANSMIT_CONFIG(prt);
3302                     return false;
3303                 }
3304                 if(prt->newInfo && (roleRoot == cistRole))
3305                 {
3306                     if(dry_run) /* state change */
3307                         return true;
3308                     PTSM_to_TRANSMIT_TCN(prt);
3309                     return false;
3310                 }
3311             }
3312             return false;
3313     }
3314 
3315     return false;
3316 }
3317 
3318 /* 13.32  Port Information state machine */
3319 
3320 #ifdef PISM_ENABLE_LOG
3321 #define PISM_LOG(_fmt, _args...) SMLOG_MSTINAME(ptp, _fmt, ##_args)
3322 #else
3323 #define PISM_LOG(_fmt, _args...) {}
3324 #endif /* PISM_ENABLE_LOG */
3325 
3326 static bool PISM_run(per_tree_port_t *ptp, bool dry_run);
3327 #define PISM_begin(ptp) PISM_to_DISABLED((ptp), true)
3328 
3329 static void PISM_to_DISABLED(per_tree_port_t *ptp, bool begin)
3330 {
3331     PISM_LOG("");
3332     ptp->PISM_state = PISM_DISABLED;
3333 
3334     ptp->rcvdMsg = false;
3335     ptp->proposing = false;
3336     ptp->proposed = false;
3337     ptp->agree = false;
3338     ptp->agreed = false;
3339     assign(ptp->rcvdInfoWhile, 0u);
3340     ptp->infoIs = ioDisabled;
3341     ptp->reselect = true;
3342     ptp->selected = false;
3343 
3344     if(!begin)
3345         PISM_run(ptp, false /* actual run */);
3346 }
3347 
3348 static void PISM_to_AGED(per_tree_port_t *ptp)
3349 {
3350     PISM_LOG("");
3351     ptp->PISM_state = PISM_AGED;
3352 
3353     ptp->infoIs = ioAged;
3354     ptp->reselect = true;
3355     ptp->selected = false;
3356 
3357     PISM_run(ptp, false /* actual run */);
3358 }
3359 
3360 static void PISM_to_UPDATE(per_tree_port_t *ptp)
3361 {
3362     PISM_LOG("");
3363     ptp->PISM_state = PISM_UPDATE;
3364 
3365     ptp->proposing = false;
3366     ptp->proposed = false;
3367     ptp->agreed = ptp->agreed && betterorsameInfo(ptp, ioMine);
3368     ptp->synced = ptp->synced && ptp->agreed;
3369     assign(ptp->portPriority, ptp->designatedPriority);
3370     assign(ptp->portTimes, ptp->designatedTimes);
3371     ptp->updtInfo = false;
3372     ptp->infoIs = ioMine;
3373     /* newInfoXst = TRUE; */
3374     port_t *prt = ptp->port;
3375     if(0 == ptp->MSTID)
3376         prt->newInfo = true;
3377     else
3378         prt->newInfoMsti = true;
3379 
3380     PISM_run(ptp, false /* actual run */);
3381 }
3382 
3383 static void PISM_to_SUPERIOR_DESIGNATED(per_tree_port_t *ptp)
3384 {
3385     PISM_LOG("");
3386     ptp->PISM_state = PISM_SUPERIOR_DESIGNATED;
3387 
3388     port_t *prt = ptp->port;
3389 
3390     prt->infoInternal = prt->rcvdInternal;
3391     ptp->agreed = false;
3392     ptp->proposing = false;
3393     recordProposal(ptp);
3394     setTcFlags(ptp);
3395     ptp->agree = ptp->agree && betterorsameInfo(ptp, ioReceived);
3396     recordAgreement(ptp);
3397     ptp->synced = ptp->synced && ptp->agreed;
3398     recordPriority(ptp);
3399     recordTimes(ptp);
3400     updtRcvdInfoWhile(ptp);
3401     ptp->infoIs = ioReceived;
3402     ptp->reselect = true;
3403     ptp->selected = false;
3404     ptp->rcvdMsg = false;
3405 
3406     PISM_run(ptp, false /* actual run */);
3407 }
3408 
3409 static void PISM_to_REPEATED_DESIGNATED(per_tree_port_t *ptp)
3410 {
3411     PISM_LOG("");
3412     ptp->PISM_state = PISM_REPEATED_DESIGNATED;
3413 
3414     port_t *prt = ptp->port;
3415 
3416     prt->infoInternal = prt->rcvdInternal;
3417     recordProposal(ptp);
3418     setTcFlags(ptp);
3419     recordAgreement(ptp);
3420     updtRcvdInfoWhile(ptp);
3421     ptp->rcvdMsg = false;
3422 
3423     PISM_run(ptp, false /* actual run */);
3424 }
3425 
3426 static void PISM_to_INFERIOR_DESIGNATED(per_tree_port_t *ptp)
3427 {
3428     PISM_LOG("");
3429     ptp->PISM_state = PISM_INFERIOR_DESIGNATED;
3430 
3431     recordDispute(ptp);
3432     ptp->rcvdMsg = false;
3433 
3434     PISM_run(ptp, false /* actual run */);
3435 }
3436 
3437 static void PISM_to_NOT_DESIGNATED(per_tree_port_t *ptp)
3438 {
3439     PISM_LOG("");
3440     ptp->PISM_state = PISM_NOT_DESIGNATED;
3441 
3442     recordAgreement(ptp);
3443     setTcFlags(ptp);
3444     ptp->rcvdMsg = false;
3445 
3446     PISM_run(ptp, false /* actual run */);
3447 }
3448 
3449 static void PISM_to_OTHER(per_tree_port_t *ptp)
3450 {
3451     PISM_LOG("");
3452     ptp->PISM_state = PISM_OTHER;
3453 
3454     ptp->rcvdMsg = false;
3455 
3456     PISM_run(ptp, false /* actual run */);
3457 }
3458 
3459 static void PISM_to_CURRENT(per_tree_port_t *ptp)
3460 {
3461     PISM_LOG("");
3462     ptp->PISM_state = PISM_CURRENT;
3463 
3464     PISM_run(ptp, false /* actual run */);
3465 }
3466 
3467 static void PISM_to_RECEIVE(per_tree_port_t *ptp)
3468 {
3469     PISM_LOG("");
3470     ptp->PISM_state = PISM_RECEIVE;
3471 
3472     ptp->rcvdInfo = rcvInfo(ptp);
3473     recordMastered(ptp);
3474 
3475     PISM_run(ptp, false /* actual run */);
3476 }
3477 
3478 static bool PISM_run(per_tree_port_t *ptp, bool dry_run)
3479 {
3480     bool rcvdXstMsg, updtXstInfo;
3481     port_t *prt = ptp->port;
3482 
3483     if((!prt->portEnabled) && (ioDisabled != ptp->infoIs))
3484     {
3485         if(dry_run) /* at least infoIs will change */
3486             return true;
3487         PISM_to_DISABLED(ptp, false);
3488         return false;
3489     }
3490 
3491     switch(ptp->PISM_state)
3492     {
3493         case PISM_DISABLED:
3494             if(prt->portEnabled)
3495             {
3496                 if(dry_run) /* state change */
3497                     return true;
3498                 PISM_to_AGED(ptp);
3499                 return false;
3500             }
3501             if(ptp->rcvdMsg)
3502             {
3503                 if(dry_run) /* at least rcvdMsg will change */
3504                     return true;
3505                 PISM_to_DISABLED(ptp, false);
3506             }
3507             return false;
3508         case PISM_AGED:
3509             if(ptp->selected && ptp->updtInfo)
3510             {
3511                 if(dry_run) /* state change */
3512                     return true;
3513                 PISM_to_UPDATE(ptp);
3514             }
3515             return false;
3516         case PISM_UPDATE:
3517             /* return; */
3518         case PISM_SUPERIOR_DESIGNATED:
3519             /* return; */
3520         case PISM_REPEATED_DESIGNATED:
3521             /* return; */
3522         case PISM_INFERIOR_DESIGNATED:
3523             /* return; */
3524         case PISM_NOT_DESIGNATED:
3525             /* return; */
3526         case PISM_OTHER:
3527             if(dry_run) /* state change */
3528                 return true;
3529             PISM_to_CURRENT(ptp);
3530             return false;
3531         case PISM_CURRENT:
3532             /*
3533              * Although 802.1Q-2005 does not define rcvdXstMsg and updtXstInfo
3534              *  from 802.1s we can conclude that they are:
3535              *  - rcvdXstMsg = rcvdCistMsg, if tree is CIST
3536              *                 rcvdMstiMsg, if tree is MSTI.
3537              *  - updtXstInfo = updtCistInfo, if tree is CIST
3538              *                  updtMstiInfo, if tree is MSTI.
3539              */
3540             if(0 == ptp->MSTID)
3541             { /* CIST */
3542                 rcvdXstMsg = ptp->rcvdMsg; /* 13.25.12 */
3543                 updtXstInfo = ptp->updtInfo; /* 13.25.16 */
3544             }
3545             else
3546             { /* MSTI */
3547                 per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
3548                 rcvdXstMsg = !cist->rcvdMsg && ptp->rcvdMsg; /* 13.25.13 */
3549                 updtXstInfo = ptp->updtInfo || cist->updtInfo; /* 13.25.17 */
3550             }
3551             if(rcvdXstMsg && !updtXstInfo)
3552             {
3553                 if(dry_run) /* state change */
3554                     return true;
3555                 PISM_to_RECEIVE(ptp);
3556                 return false;
3557             }
3558             if((ioReceived == ptp->infoIs) && (0 == ptp->rcvdInfoWhile)
3559                && !ptp->updtInfo && !rcvdXstMsg)
3560             {
3561                 if(dry_run) /* state change */
3562                     return true;
3563                 PISM_to_AGED(ptp);
3564                 return false;
3565             }
3566             if(ptp->selected && ptp->updtInfo)
3567             {
3568                 if(dry_run) /* state change */
3569                     return true;
3570                 PISM_to_UPDATE(ptp);
3571             }
3572             return false;
3573         case PISM_RECEIVE:
3574             switch(ptp->rcvdInfo)
3575             {
3576                 case SuperiorDesignatedInfo:
3577                     if(dry_run) /* state change */
3578                         return true;
3579                     PISM_to_SUPERIOR_DESIGNATED(ptp);
3580                     return false;
3581                 case RepeatedDesignatedInfo:
3582                     if(dry_run) /* state change */
3583                         return true;
3584                     PISM_to_REPEATED_DESIGNATED(ptp);
3585                     return false;
3586                 case InferiorDesignatedInfo:
3587                     if(dry_run) /* state change */
3588                         return true;
3589                     PISM_to_INFERIOR_DESIGNATED(ptp);
3590                     return false;
3591                 case InferiorRootAlternateInfo:
3592                     if(dry_run) /* state change */
3593                         return true;
3594                     PISM_to_NOT_DESIGNATED(ptp);
3595                     return false;
3596                 case OtherInfo:
3597                     if(dry_run) /* state change */
3598                         return true;
3599                     PISM_to_OTHER(ptp);
3600                     return false;
3601             }
3602             return false;
3603     }
3604 
3605     return false;
3606 }
3607 
3608 /* 13.33  Port Role Selection state machine */
3609 
3610 static bool PRSSM_run(tree_t *tree, bool dry_run);
3611 #define PRSSM_begin(tree) PRSSM_to_INIT_TREE(tree)
3612 
3613 static void PRSSM_to_INIT_TREE(tree_t *tree/*, bool begin*/)
3614 {
3615     tree->PRSSM_state = PRSSM_INIT_TREE;
3616 
3617     updtRolesDisabledTree(tree);
3618 
3619     /* No need to check, as we assume begin = true here
3620      * because transition to this state can be initiated only by BEGIN var.
3621      * In other words, this function is called via xxx_begin macro only.
3622      * if(!begin)
3623      *     PRSSM_run(prt, false); */
3624 }
3625 
3626 static void PRSSM_to_ROLE_SELECTION(tree_t *tree)
3627 {
3628     tree->PRSSM_state = PRSSM_ROLE_SELECTION;
3629 
3630     clearReselectTree(tree);
3631     updtRolesTree(tree);
3632     setSelectedTree(tree);
3633 
3634     /* No need to run, no one condition will be met
3635       PRSSM_run(tree, false); */
3636 }
3637 
3638 static bool PRSSM_run(tree_t *tree, bool dry_run)
3639 {
3640     per_tree_port_t *ptp;
3641 
3642     switch(tree->PRSSM_state)
3643     {
3644         case PRSSM_INIT_TREE:
3645             if(dry_run) /* state change */
3646                 return true;
3647             PRSSM_to_ROLE_SELECTION(tree);
3648             return false;
3649         case PRSSM_ROLE_SELECTION:
3650             FOREACH_PTP_IN_TREE(ptp, tree)
3651                 if(ptp->reselect)
3652                 {
3653                     if(dry_run) /* at least reselect will change */
3654                         return true;
3655                     PRSSM_to_ROLE_SELECTION(tree);
3656                     return false;
3657                 }
3658             return false;
3659     }
3660 
3661     return false;
3662 }
3663 
3664 /* 13.34  Port Role Transitions state machine */
3665 
3666 #ifdef PRTSM_ENABLE_LOG
3667 #define PRTSM_LOG(_fmt, _args...) SMLOG_MSTINAME(ptp, _fmt, ##_args)
3668 #else
3669 #define PRTSM_LOG(_fmt, _args...) {}
3670 #endif /* PRTSM_ENABLE_LOG */
3671 
3672 static bool PRTSM_runr(per_tree_port_t *ptp, bool recursive_call, bool dry_run);
3673 #define PRTSM_run(ptp, dry_run) PRTSM_runr((ptp), false, (dry_run))
3674 #define PRTSM_begin(ptp) PRTSM_to_INIT_PORT(ptp)
3675 
3676  /* Disabled Port role transitions */
3677 
3678 static void PRTSM_to_INIT_PORT(per_tree_port_t *ptp/*, bool begin*/)
3679 {
3680     PRTSM_LOG("");
3681     ptp->PRTSM_state = PRTSM_INIT_PORT;
3682 
3683     unsigned int MaxAge, FwdDelay;
3684     per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(ptp->port);
3685 
3686     ptp->role = roleDisabled;
3687     ptp->learn = false;
3688     ptp->forward = false;
3689     ptp->synced = false;
3690     ptp->sync = true;
3691     ptp->reRoot = true;
3692     /* 13.25.6 */
3693     FwdDelay = cist->designatedTimes.Forward_Delay;
3694     assign(ptp->rrWhile, FwdDelay);
3695     /* 13.25.8 */
3696     MaxAge = cist->designatedTimes.Max_Age;
3697     assign(ptp->fdWhile, MaxAge);
3698     assign(ptp->rbWhile, 0u);
3699 
3700     /* No need to check, as we assume begin = true here
3701      * because transition to this state can be initiated only by BEGIN var.
3702      * In other words, this function is called via xxx_begin macro only.
3703      * if(!begin)
3704      *     PRTSM_runr(ptp, false, false); */
3705 }
3706 
3707 static void PRTSM_to_DISABLE_PORT(per_tree_port_t *ptp)
3708 {
3709     PRTSM_LOG("");
3710     ptp->PRTSM_state = PRTSM_DISABLE_PORT;
3711 
3712     /* Although 802.1Q-2005 says here to do role = selectedRole
3713      * I have difficulties with it in the next scenario:
3714      *  1) port was designated (role == selectedRole == roleDesignated)
3715      *  2) some config event occurs, e.g. MAC address changes and
3716      *     br_state_machines_begin is called
3717      *  3) role == selectedRole == roleDisabled, PRTSM_state = PRTSM_INIT_PORT
3718      * Because port was not actually down, on the next run
3719      *  Port Role Selection state machine sets selectedRole = roleDesignated
3720      *  and updtInfo = true:
3721      *  4) we have unconditional transition to DISABLE_PORT, and because
3722      *     updtInfo = true we can not follow transition to DESIGNATED_PORT
3723      *  5) if we follow standard, role = selectedRole = roleDesignated and
3724      *     on the next run we have transition to the DISABLED_PORT
3725      * And there we stuck. role == selectedRole, so we can not transit to
3726      *  DESIGNATED_PORT (it requires role != selectedRole ).
3727      *
3728      * Solution: do not follow the standard, and do role = roleDisabled
3729      *  instead of role = selectedRole.
3730      */
3731     ptp->role = roleDisabled;
3732     ptp->learn = false;
3733     ptp->forward = false;
3734 
3735     PRTSM_runr(ptp, true, false /* actual run */);
3736 }
3737 
3738 static void PRTSM_to_DISABLED_PORT(per_tree_port_t *ptp, unsigned int MaxAge)
3739 {
3740     PRTSM_LOG("");
3741     ptp->PRTSM_state = PRTSM_DISABLED_PORT;
3742 
3743     assign(ptp->fdWhile, MaxAge);
3744     ptp->synced = true;
3745     assign(ptp->rrWhile, 0u);
3746     ptp->sync = false;
3747     ptp->reRoot = false;
3748 
3749     PRTSM_runr(ptp, true, false /* actual run */);
3750 }
3751 
3752  /* MasterPort role transitions */
3753 
3754 static void PRTSM_to_MASTER_PROPOSED(per_tree_port_t *ptp)
3755 {
3756     PRTSM_LOG("");
3757     ptp->PRTSM_state = PRTSM_MASTER_PROPOSED;
3758 
3759     setSyncTree(ptp->tree);
3760     ptp->proposed = false;
3761 
3762     PRTSM_runr(ptp, true, false /* actual run */);
3763 }
3764 
3765 static void PRTSM_to_MASTER_AGREED(per_tree_port_t *ptp)
3766 {
3767     PRTSM_LOG("");
3768     ptp->PRTSM_state = PRTSM_MASTER_AGREED;
3769 
3770     ptp->proposed = false;
3771     ptp->sync = false;
3772     ptp->agree = true;
3773 
3774     PRTSM_runr(ptp, true, false /* actual run */);
3775 }
3776 
3777 static void PRTSM_to_MASTER_SYNCED(per_tree_port_t *ptp)
3778 {
3779     PRTSM_LOG("");
3780     ptp->PRTSM_state = PRTSM_MASTER_SYNCED;
3781 
3782     assign(ptp->rrWhile, 0u);
3783     ptp->synced = true;
3784     ptp->sync = false;
3785 
3786     PRTSM_runr(ptp, true, false /* actual run */);
3787 }
3788 
3789 static void PRTSM_to_MASTER_RETIRED(per_tree_port_t *ptp)
3790 {
3791     PRTSM_LOG("");
3792     ptp->PRTSM_state = PRTSM_MASTER_RETIRED;
3793 
3794     ptp->reRoot = false;
3795 
3796     PRTSM_runr(ptp, true, false /* actual run */);
3797 }
3798 
3799 static void PRTSM_to_MASTER_FORWARD(per_tree_port_t *ptp)
3800 {
3801     PRTSM_LOG("");
3802     ptp->PRTSM_state = PRTSM_MASTER_FORWARD;
3803 
3804     ptp->forward = true;
3805     assign(ptp->fdWhile, 0u);
3806     ptp->agreed = ptp->port->sendRSTP;
3807 
3808     PRTSM_runr(ptp, true, false /* actual run */);
3809 }
3810 
3811 static void PRTSM_to_MASTER_LEARN(per_tree_port_t *ptp, unsigned int forwardDelay)
3812 {
3813     PRTSM_LOG("");
3814     ptp->PRTSM_state = PRTSM_MASTER_LEARN;
3815 
3816     ptp->learn = true;
3817     assign(ptp->fdWhile, forwardDelay);
3818 
3819     PRTSM_runr(ptp, true, false /* actual run */);
3820 }
3821 
3822 static void PRTSM_to_MASTER_DISCARD(per_tree_port_t *ptp, unsigned int forwardDelay)
3823 {
3824     PRTSM_LOG("");
3825     ptp->PRTSM_state = PRTSM_MASTER_DISCARD;
3826 
3827     ptp->learn = false;
3828     ptp->forward = false;
3829     ptp->disputed = false;
3830     assign(ptp->fdWhile, forwardDelay);
3831 
3832     PRTSM_runr(ptp, true, false /* actual run */);
3833 }
3834 
3835 static void PRTSM_to_MASTER_PORT(per_tree_port_t *ptp)
3836 {
3837     PRTSM_LOG("");
3838     ptp->PRTSM_state = PRTSM_MASTER_PORT;
3839 
3840     ptp->role = roleMaster;
3841 
3842     PRTSM_runr(ptp, true, false /* actual run */);
3843 }
3844 
3845  /* RootPort role transitions */
3846 
3847 static void PRTSM_to_ROOT_PROPOSED(per_tree_port_t *ptp)
3848 {
3849     PRTSM_LOG("");
3850     ptp->PRTSM_state = PRTSM_ROOT_PROPOSED;
3851 
3852     setSyncTree(ptp->tree);
3853     ptp->proposed = false;
3854 
3855     PRTSM_runr(ptp, true, false /* actual run */);
3856 }
3857 
3858 static void PRTSM_to_ROOT_AGREED(per_tree_port_t *ptp)
3859 {
3860     PRTSM_LOG("");
3861     ptp->PRTSM_state = PRTSM_ROOT_AGREED;
3862 
3863     ptp->proposed = false;
3864     ptp->sync = false;
3865     ptp->agree = true;
3866     /* newInfoXst = TRUE; */
3867     port_t *prt = ptp->port;
3868     if(0 == ptp->MSTID)
3869         prt->newInfo = true;
3870     else
3871         prt->newInfoMsti = true;
3872 
3873     PRTSM_runr(ptp, true, false /* actual run */);
3874 }
3875 
3876 static void PRTSM_to_ROOT_SYNCED(per_tree_port_t *ptp)
3877 {
3878     PRTSM_LOG("");
3879     ptp->PRTSM_state = PRTSM_ROOT_SYNCED;
3880 
3881     ptp->synced = true;
3882     ptp->sync = false;
3883 
3884     PRTSM_runr(ptp, true, false /* actual run */);
3885 }
3886 
3887 static void PRTSM_to_REROOT(per_tree_port_t *ptp)
3888 {
3889     PRTSM_LOG("");
3890     ptp->PRTSM_state = PRTSM_REROOT;
3891 
3892     setReRootTree(ptp->tree);
3893 
3894     PRTSM_runr(ptp, true, false /* actual run */);
3895 }
3896 
3897 static void PRTSM_to_ROOT_FORWARD(per_tree_port_t *ptp)
3898 {
3899     PRTSM_LOG("");
3900     ptp->PRTSM_state = PRTSM_ROOT_FORWARD;
3901 
3902     assign(ptp->fdWhile, 0u);
3903     ptp->forward = true;
3904 
3905     PRTSM_runr(ptp, true, false /* actual run */);
3906 }
3907 
3908 static void PRTSM_to_ROOT_LEARN(per_tree_port_t *ptp, unsigned int forwardDelay)
3909 {
3910     PRTSM_LOG("");
3911     ptp->PRTSM_state = PRTSM_ROOT_LEARN;
3912 
3913     assign(ptp->fdWhile, forwardDelay);
3914     ptp->learn = true;
3915 
3916     PRTSM_runr(ptp, true, false /* actual run */);
3917 }
3918 
3919 static void PRTSM_to_REROOTED(per_tree_port_t *ptp)
3920 {
3921     PRTSM_LOG("");
3922     ptp->PRTSM_state = PRTSM_REROOTED;
3923 
3924     ptp->reRoot = false;
3925 
3926     PRTSM_runr(ptp, true, false /* actual run */);
3927 }
3928 
3929 static void PRTSM_to_ROOT_PORT(per_tree_port_t *ptp, unsigned int FwdDelay)
3930 {
3931     PRTSM_LOG("");
3932     ptp->PRTSM_state = PRTSM_ROOT_PORT;
3933 
3934     ptp->role = roleRoot;
3935     assign(ptp->rrWhile, FwdDelay);
3936 
3937     PRTSM_runr(ptp, true, false /* actual run */);
3938 }
3939 
3940  /* DesignatedPort role transitions */
3941 
3942 static void PRTSM_to_DESIGNATED_PROPOSE(per_tree_port_t *ptp)
3943 {
3944     PRTSM_LOG("");
3945     ptp->PRTSM_state = PRTSM_DESIGNATED_PROPOSE;
3946 
3947     port_t *prt = ptp->port;
3948 
3949     ptp->proposing = true;
3950     /* newInfoXst = TRUE; */
3951     if(0 == ptp->MSTID)
3952     { /* CIST */
3953         /* 13.25.8. This tree is CIST. */
3954         unsigned int MaxAge = ptp->designatedTimes.Max_Age;
3955         /* 13.25.c) -> 17.20.4 of 802.1D : EdgeDelay */
3956         unsigned int EdgeDelay = prt->operPointToPointMAC ?
3957                                    prt->bridge->Migrate_Time
3958                                  : MaxAge;
3959         assign(prt->edgeDelayWhile, EdgeDelay);
3960         prt->newInfo = true;
3961     }
3962     else
3963         prt->newInfoMsti = true;
3964 
3965     PRTSM_runr(ptp, true, false /* actual run */);
3966 }
3967 
3968 static void PRTSM_to_DESIGNATED_AGREED(per_tree_port_t *ptp)
3969 {
3970     PRTSM_LOG("");
3971     ptp->PRTSM_state = PRTSM_DESIGNATED_AGREED;
3972 
3973     ptp->proposed = false;
3974     ptp->sync = false;
3975     ptp->agree = true;
3976     /* newInfoXst = TRUE; */
3977     port_t *prt = ptp->port;
3978     if(0 == ptp->MSTID)
3979         prt->newInfo = true;
3980     else
3981         prt->newInfoMsti = true;
3982 
3983     PRTSM_runr(ptp, true, false /* actual run */);
3984 }
3985 
3986 static void PRTSM_to_DESIGNATED_SYNCED(per_tree_port_t *ptp)
3987 {
3988     PRTSM_LOG("");
3989     ptp->PRTSM_state = PRTSM_DESIGNATED_SYNCED;
3990 
3991     assign(ptp->rrWhile, 0u);
3992     ptp->synced = true;
3993     ptp->sync = false;
3994 
3995     PRTSM_runr(ptp, true, false /* actual run */);
3996 }
3997 
3998 static void PRTSM_to_DESIGNATED_RETIRED(per_tree_port_t *ptp)
3999 {
4000     PRTSM_LOG("");
4001     ptp->PRTSM_state = PRTSM_DESIGNATED_RETIRED;
4002 
4003     ptp->reRoot = false;
4004 
4005     PRTSM_runr(ptp, true, false /* actual run */);
4006 }
4007 
4008 static void PRTSM_to_DESIGNATED_FORWARD(per_tree_port_t *ptp)
4009 {
4010     PRTSM_LOG("");
4011     ptp->PRTSM_state = PRTSM_DESIGNATED_FORWARD;
4012 
4013     ptp->forward = true;
4014     assign(ptp->fdWhile, 0u);
4015     ptp->agreed = ptp->port->sendRSTP;
4016 
4017     PRTSM_runr(ptp, true, false /* actual run */);
4018 }
4019 
4020 static void PRTSM_to_DESIGNATED_LEARN(per_tree_port_t *ptp, unsigned int forwardDelay)
4021 {
4022     PRTSM_LOG("");
4023     ptp->PRTSM_state = PRTSM_DESIGNATED_LEARN;
4024 
4025     ptp->learn = true;
4026     assign(ptp->fdWhile, forwardDelay);
4027 
4028     PRTSM_runr(ptp, true, false /* actual run */);
4029 }
4030 
4031 static void PRTSM_to_DESIGNATED_DISCARD(per_tree_port_t *ptp, unsigned int forwardDelay)
4032 {
4033     PRTSM_LOG("");
4034     ptp->PRTSM_state = PRTSM_DESIGNATED_DISCARD;
4035 
4036     ptp->learn = false;
4037     ptp->forward = false;
4038     ptp->disputed = false;
4039     assign(ptp->fdWhile, forwardDelay);
4040 
4041     PRTSM_runr(ptp, true, false /* actual run */);
4042 }
4043 
4044 static void PRTSM_to_DESIGNATED_PORT(per_tree_port_t *ptp)
4045 {
4046     PRTSM_LOG("");
4047     ptp->PRTSM_state = PRTSM_DESIGNATED_PORT;
4048 
4049     ptp->role = roleDesignated;
4050 
4051     PRTSM_runr(ptp, true, false /* actual run */);
4052 }
4053 
4054  /* AlternatePort and BackupPort role transitions */
4055 
4056 static void PRTSM_to_BLOCK_PORT(per_tree_port_t *ptp)
4057 {
4058     PRTSM_LOG("");
4059     ptp->PRTSM_state = PRTSM_BLOCK_PORT;
4060 
4061     ptp->role = ptp->selectedRole;
4062     ptp->learn = false;
4063     ptp->forward = false;
4064 
4065     PRTSM_runr(ptp, true, false /* actual run */);
4066 }
4067 
4068 static void PRTSM_to_BACKUP_PORT(per_tree_port_t *ptp, unsigned int HelloTime)
4069 {
4070     PRTSM_LOG("");
4071     ptp->PRTSM_state = PRTSM_BACKUP_PORT;
4072 
4073     assign(ptp->rbWhile, 2 * HelloTime);
4074 
4075     PRTSM_runr(ptp, true, false /* actual run */);
4076 }
4077 
4078 static void PRTSM_to_ALTERNATE_PROPOSED(per_tree_port_t *ptp)
4079 {
4080     PRTSM_LOG("");
4081     ptp->PRTSM_state = PRTSM_ALTERNATE_PROPOSED;
4082 
4083     setSyncTree(ptp->tree);
4084     ptp->proposed = false;
4085 
4086     PRTSM_runr(ptp, true, false /* actual run */);
4087 }
4088 
4089 static void PRTSM_to_ALTERNATE_AGREED(per_tree_port_t *ptp)
4090 {
4091     PRTSM_LOG("");
4092     ptp->PRTSM_state = PRTSM_ALTERNATE_AGREED;
4093 
4094     ptp->proposed = false;
4095     ptp->agree = true;
4096     /* newInfoXst = TRUE; */
4097     port_t *prt = ptp->port;
4098     if(0 == ptp->MSTID)
4099         prt->newInfo = true;
4100     else
4101         prt->newInfoMsti = true;
4102 
4103     PRTSM_runr(ptp, true, false /* actual run */);
4104 }
4105 
4106 static void PRTSM_to_ALTERNATE_PORT(per_tree_port_t *ptp, unsigned int forwardDelay)
4107 {
4108     PRTSM_LOG("");
4109     ptp->PRTSM_state = PRTSM_ALTERNATE_PORT;
4110 
4111     assign(ptp->fdWhile, forwardDelay);
4112     ptp->synced = true;
4113     assign(ptp->rrWhile, 0u);
4114     ptp->sync = false;
4115     ptp->reRoot = false;
4116 
4117     PRTSM_runr(ptp, true, false /* actual run */);
4118 }
4119 
4120 static bool PRTSM_runr(per_tree_port_t *ptp, bool recursive_call, bool dry_run)
4121 {
4122     /* Following vars do not need recalculating on recursive calls */
4123     static unsigned int MaxAge, FwdDelay, forwardDelay, HelloTime;
4124     static port_t *prt;
4125     static tree_t *tree;
4126     static per_tree_port_t *cist;
4127     /* Following vars are recalculated on each state transition */
4128     bool allSynced, reRooted;
4129     /* Following vars are auxiliary and don't depend on recursive_call */
4130     per_tree_port_t *ptp_1;
4131 
4132     if(!recursive_call)
4133     { /* calculate these intermediate vars only first time in chain of
4134        * recursive calls */
4135         prt = ptp->port;
4136         tree = ptp->tree;
4137 
4138         cist = GET_CIST_PTP_FROM_PORT(prt);
4139 
4140         /* 13.25.6 */
4141         FwdDelay = cist->designatedTimes.Forward_Delay;
4142 
4143         /* 13.25.7 */
4144         HelloTime = cist->portTimes.Hello_Time;
4145 
4146         /* 13.25.d) -> 17.20.5 of 802.1D */
4147         forwardDelay = prt->sendRSTP ? HelloTime : FwdDelay;
4148 
4149         /* 13.25.8 */
4150         MaxAge = cist->designatedTimes.Max_Age;
4151     }
4152 
4153     PRTSM_LOG("role = %d, selectedRole = %d, selected = %d, updtInfo = %d",
4154               ptp->role, ptp->selectedRole, ptp->selected, ptp->updtInfo);
4155     if((ptp->role != ptp->selectedRole) && ptp->selected && !ptp->updtInfo)
4156     {
4157         switch(ptp->selectedRole)
4158         {
4159             case roleDisabled:
4160                 if(dry_run) /* at least role will change */
4161                     return true;
4162                 PRTSM_to_DISABLE_PORT(ptp);
4163                 return false;
4164             case roleMaster:
4165                 if(dry_run) /* at least role will change */
4166                     return true;
4167                 PRTSM_to_MASTER_PORT(ptp);
4168                 return false;
4169             case roleRoot:
4170                 if(dry_run) /* at least role will change */
4171                     return true;
4172                 PRTSM_to_ROOT_PORT(ptp, FwdDelay);
4173                 return false;
4174             case roleDesignated:
4175                 if(dry_run) /* at least role will change */
4176                     return true;
4177                 PRTSM_to_DESIGNATED_PORT(ptp);
4178                 return false;
4179             case roleAlternate:
4180             case roleBackup:
4181                 if(dry_run) /* at least role will change */
4182                     return true;
4183                 PRTSM_to_BLOCK_PORT(ptp);
4184                 return false;
4185         }
4186     }
4187 
4188     /* 13.25.1 */
4189     allSynced = true;
4190     FOREACH_PTP_IN_TREE(ptp_1, tree)
4191     {
4192         /* a) */
4193         if(!ptp_1->selected
4194            || (ptp_1->role != ptp_1->selectedRole)
4195            || ptp_1->updtInfo
4196           )
4197         {
4198             allSynced = false;
4199             break;
4200         }
4201 
4202         /* b) */
4203         switch(ptp->role)
4204         {
4205             case roleRoot:
4206             case roleAlternate:
4207                 if((roleRoot != ptp_1->role) && !ptp_1->synced)
4208                     allSynced = false;
4209                 break;
4210             case roleDesignated:
4211             case roleMaster:
4212                 if((ptp != ptp_1) && !ptp_1->synced)
4213                     allSynced = false;
4214                 break;
4215             default:
4216                 allSynced = false;
4217         }
4218         if(!allSynced)
4219             break;
4220     }
4221 
4222     switch(ptp->PRTSM_state)
4223     {
4224      /* Disabled Port role transitions */
4225         case PRTSM_INIT_PORT:
4226             if(dry_run) /* state change */
4227                 return true;
4228             PRTSM_to_DISABLE_PORT(ptp);
4229             return false;
4230         case PRTSM_DISABLE_PORT:
4231             if(ptp->selected && !ptp->updtInfo
4232                && !ptp->learning && !ptp->forwarding
4233               )
4234             {
4235                 if(dry_run) /* state change */
4236                     return true;
4237                 PRTSM_to_DISABLED_PORT(ptp, MaxAge);
4238             }
4239             return false;
4240         case PRTSM_DISABLED_PORT:
4241             if(ptp->selected && !ptp->updtInfo
4242                && (ptp->sync || ptp->reRoot || !ptp->synced
4243                    || (ptp->fdWhile != MaxAge))
4244               )
4245             {
4246                 if(dry_run) /* one of (sync,reRoot,synced,fdWhile) will change */
4247                     return true;
4248                 PRTSM_to_DISABLED_PORT(ptp, MaxAge);
4249             }
4250             return false;
4251      /* MasterPort role transitions */
4252         case PRTSM_MASTER_PROPOSED:
4253             /* return; */
4254         case PRTSM_MASTER_AGREED:
4255             /* return; */
4256         case PRTSM_MASTER_SYNCED:
4257             /* return; */
4258         case PRTSM_MASTER_RETIRED:
4259             /* return; */
4260         case PRTSM_MASTER_FORWARD:
4261             /* return; */
4262         case PRTSM_MASTER_LEARN:
4263             /* return; */
4264         case PRTSM_MASTER_DISCARD:
4265             if(dry_run) /* state change */
4266                 return true;
4267             PRTSM_to_MASTER_PORT(ptp);
4268             return false;
4269         case PRTSM_MASTER_PORT:
4270             if(!(ptp->selected && !ptp->updtInfo))
4271                 return false;
4272             if(ptp->reRoot && (0 == ptp->rrWhile))
4273             {
4274                 if(dry_run) /* state change */
4275                     return true;
4276                 PRTSM_to_MASTER_RETIRED(ptp);
4277                 return false;
4278             }
4279             if((!ptp->learning && !ptp->forwarding && !ptp->synced)
4280                || (ptp->agreed && !ptp->synced)
4281                || (prt->operEdge && !ptp->synced)
4282                || (ptp->sync && ptp->synced)
4283               )
4284             {
4285                 if(dry_run) /* state change */
4286                     return true;
4287                 PRTSM_to_MASTER_SYNCED(ptp);
4288                 return false;
4289             }
4290             if((allSynced && !ptp->agree)
4291                || (ptp->proposed && ptp->agree)
4292               )
4293             {
4294                 if(dry_run) /* state change */
4295                     return true;
4296                 PRTSM_to_MASTER_AGREED(ptp);
4297                 return false;
4298             }
4299             if(ptp->proposed && !ptp->agree)
4300             {
4301                 if(dry_run) /* state change */
4302                     return true;
4303                 PRTSM_to_MASTER_PROPOSED(ptp);
4304                 return false;
4305             }
4306             if(((0 == ptp->fdWhile) || allSynced)
4307                && ptp->learn && !ptp->forward
4308               )
4309             {
4310                 if(dry_run) /* state change */
4311                     return true;
4312                 PRTSM_to_MASTER_FORWARD(ptp);
4313                 return false;
4314             }
4315             if(((0 == ptp->fdWhile) || allSynced)
4316                && !ptp->learn
4317               )
4318             {
4319                 if(dry_run) /* state change */
4320                     return true;
4321                 PRTSM_to_MASTER_LEARN(ptp, forwardDelay);
4322                 return false;
4323             }
4324             if(((ptp->sync && !ptp->synced)
4325                 || (ptp->reRoot && (0 != ptp->rrWhile))
4326                 || ptp->disputed
4327                )
4328                && !prt->operEdge && (ptp->learn || ptp->forward)
4329               )
4330             {
4331                 if(dry_run) /* state change */
4332                     return true;
4333                 PRTSM_to_MASTER_DISCARD(ptp, forwardDelay);
4334                 return false;
4335             }
4336             return false;
4337      /* RootPort role transitions */
4338         case PRTSM_ROOT_PROPOSED:
4339             /* return; */
4340         case PRTSM_ROOT_AGREED:
4341             /* return; */
4342         case PRTSM_ROOT_SYNCED:
4343             /* return; */
4344         case PRTSM_REROOT:
4345             /* return; */
4346         case PRTSM_ROOT_FORWARD:
4347             /* return; */
4348         case PRTSM_ROOT_LEARN:
4349             /* return; */
4350         case PRTSM_REROOTED:
4351             if(dry_run) /* state change */
4352                 return true;
4353             PRTSM_to_ROOT_PORT(ptp, FwdDelay);
4354             return false;
4355         case PRTSM_ROOT_PORT:
4356             if(!(ptp->selected && !ptp->updtInfo))
4357                 return false;
4358             if(!ptp->forward && !ptp->reRoot)
4359             {
4360                 if(dry_run) /* state change */
4361                     return true;
4362                 PRTSM_to_REROOT(ptp);
4363                 return false;
4364             }
4365             if((ptp->agreed && !ptp->synced) || (ptp->sync && ptp->synced))
4366             {
4367                 if(dry_run) /* state change */
4368                     return true;
4369                 PRTSM_to_ROOT_SYNCED(ptp);
4370                 return false;
4371             }
4372             if((allSynced && !ptp->agree) || (ptp->proposed && ptp->agree))
4373             {
4374                 if(dry_run) /* state change */
4375                     return true;
4376                 PRTSM_to_ROOT_AGREED(ptp);
4377                 return false;
4378             }
4379             if(ptp->proposed && !ptp->agree)
4380             {
4381                 if(dry_run) /* state change */
4382                     return true;
4383                 PRTSM_to_ROOT_PROPOSED(ptp);
4384                 return false;
4385             }
4386             /* 17.20.10 of 802.1D : reRooted */
4387             reRooted = true;
4388             FOREACH_PTP_IN_TREE(ptp_1, tree)
4389             {
4390                 if((ptp != ptp_1) && (0 != ptp_1->rrWhile))
4391                 {
4392                     reRooted = false;
4393                     break;
4394                 }
4395             }
4396             if((0 == ptp->fdWhile)
4397                || (reRooted && (0 == ptp->rbWhile) && rstpVersion(prt->bridge))
4398               )
4399             {
4400                 if(!ptp->learn)
4401                 {
4402                     if(dry_run) /* state change */
4403                         return true;
4404                     PRTSM_to_ROOT_LEARN(ptp, forwardDelay);
4405                     return false;
4406                 }
4407                 else if(!ptp->forward)
4408                 {
4409                     if(dry_run) /* state change */
4410                         return true;
4411                     PRTSM_to_ROOT_FORWARD(ptp);
4412                     return false;
4413                 }
4414             }
4415             if(ptp->reRoot && ptp->forward)
4416             {
4417                 if(dry_run) /* state change */
4418                     return true;
4419                 PRTSM_to_REROOTED(ptp);
4420                 return false;
4421             }
4422             if(ptp->rrWhile != FwdDelay)
4423             {
4424                 if(dry_run) /* state change */
4425                     return true;
4426                 PRTSM_to_ROOT_PORT(ptp, FwdDelay);
4427                 return false;
4428             }
4429             return false;
4430      /* DesignatedPort role transitions */
4431         case PRTSM_DESIGNATED_PROPOSE:
4432             /* return; */
4433         case PRTSM_DESIGNATED_AGREED:
4434             /* return; */
4435         case PRTSM_DESIGNATED_SYNCED:
4436             /* return; */
4437         case PRTSM_DESIGNATED_RETIRED:
4438             /* return; */
4439         case PRTSM_DESIGNATED_FORWARD:
4440             /* return; */
4441         case PRTSM_DESIGNATED_LEARN:
4442             /* return; */
4443         case PRTSM_DESIGNATED_DISCARD:
4444             if(dry_run) /* state change */
4445                 return true;
4446             PRTSM_to_DESIGNATED_PORT(ptp);
4447             return false;
4448         case PRTSM_DESIGNATED_PORT:
4449             if(!(ptp->selected && !ptp->updtInfo))
4450                 return false;
4451             if(ptp->reRoot && (0 == ptp->rrWhile))
4452             {
4453                 if(dry_run) /* state change */
4454                     return true;
4455                 PRTSM_to_DESIGNATED_RETIRED(ptp);
4456                 return false;
4457             }
4458             if((!ptp->learning && !ptp->forwarding && !ptp->synced)
4459                || (ptp->agreed && !ptp->synced)
4460                || (prt->operEdge && !ptp->synced)
4461                || (ptp->sync && ptp->synced)
4462               )
4463             {
4464                 if(dry_run) /* state change */
4465                     return true;
4466                 PRTSM_to_DESIGNATED_SYNCED(ptp);
4467                 return false;
4468             }
4469             if(allSynced && (ptp->proposed || !ptp->agree))
4470             {
4471                 if(dry_run) /* state change */
4472                     return true;
4473                 PRTSM_to_DESIGNATED_AGREED(ptp);
4474                 return false;
4475             }
4476             if(!ptp->forward && !ptp->agreed && !ptp->proposing
4477                && !prt->operEdge)
4478             {
4479                 if(dry_run) /* state change */
4480                     return true;
4481                 PRTSM_to_DESIGNATED_PROPOSE(ptp);
4482                 return false;
4483             }
4484             /* Dont transition to learn/forward when BA inconsistent */
4485             if(((0 == ptp->fdWhile) || ptp->agreed || prt->operEdge)
4486                && ((0 == ptp->rrWhile) || !ptp->reRoot) && !ptp->sync
4487                && !ptp->port->BaInconsistent
4488               )
4489             {
4490                 if(!ptp->learn)
4491                 {
4492                     if(dry_run) /* state change */
4493                         return true;
4494                     PRTSM_to_DESIGNATED_LEARN(ptp, forwardDelay);
4495                     return false;
4496                 }
4497                 else if(!ptp->forward)
4498                 {
4499                     if(dry_run) /* state change */
4500                         return true;
4501                     PRTSM_to_DESIGNATED_FORWARD(ptp);
4502                     return false;
4503                 }
4504             }
4505             /* Transition to discarding when BA inconsistent */
4506             if(((ptp->sync && !ptp->synced)
4507                 || (ptp->reRoot && (0 != ptp->rrWhile))
4508                 || ptp->disputed
4509                 || ptp->port->BaInconsistent
4510                )
4511                && !prt->operEdge && (ptp->learn || ptp->forward)
4512               )
4513             {
4514                 if(dry_run) /* state change */
4515                     return true;
4516                 PRTSM_to_DESIGNATED_DISCARD(ptp, forwardDelay);
4517                 return false;
4518             }
4519             return false;
4520      /* AlternatePort and BackupPort role transitions */
4521         case PRTSM_BLOCK_PORT:
4522             if(ptp->selected && !ptp->updtInfo
4523                && !ptp->learning && !ptp->forwarding
4524               )
4525             {
4526                 if(dry_run) /* state change */
4527                     return true;
4528                 PRTSM_to_ALTERNATE_PORT(ptp, forwardDelay);
4529             }
4530             return false;
4531         case PRTSM_BACKUP_PORT:
4532             /* return; */
4533         case PRTSM_ALTERNATE_PROPOSED:
4534             /* return; */
4535         case PRTSM_ALTERNATE_AGREED:
4536             if(dry_run) /* state change */
4537                 return true;
4538             PRTSM_to_ALTERNATE_PORT(ptp, forwardDelay);
4539             return false;
4540         case PRTSM_ALTERNATE_PORT:
4541             if(!(ptp->selected && !ptp->updtInfo))
4542                 return false;
4543             if((allSynced && !ptp->agree) || (ptp->proposed && ptp->agree))
4544             {
4545                 if(dry_run) /* state change */
4546                     return true;
4547                 PRTSM_to_ALTERNATE_AGREED(ptp);
4548                 return false;
4549             }
4550             if(ptp->proposed && !ptp->agree)
4551             {
4552                 if(dry_run) /* state change */
4553                     return true;
4554                 PRTSM_to_ALTERNATE_PROPOSED(ptp);
4555                 return false;
4556             }
4557             if((ptp->rbWhile != 2 * HelloTime) && (roleBackup == ptp->role))
4558             {
4559                 if(dry_run) /* state change */
4560                     return true;
4561                 PRTSM_to_BACKUP_PORT(ptp, HelloTime);
4562                 return false;
4563             }
4564             if((ptp->fdWhile != forwardDelay) || ptp->sync || ptp->reRoot
4565                || !ptp->synced)
4566             {
4567                 if(dry_run) /* state change */
4568                     return true;
4569                 PRTSM_to_ALTERNATE_PORT(ptp, forwardDelay);
4570                 return false;
4571             }
4572             return false;
4573     }
4574 
4575     return false;
4576 }
4577 
4578 /* 13.35  Port State Transition state machine */
4579 
4580 static bool PSTSM_run(per_tree_port_t *ptp, bool dry_run);
4581 #define PSTSM_begin(ptp) PSTSM_to_DISCARDING((ptp), true)
4582 
4583 static void PSTSM_to_DISCARDING(per_tree_port_t *ptp, bool begin)
4584 {
4585     ptp->PSTSM_state = PSTSM_DISCARDING;
4586 
4587     /* This effectively sets BLOCKING state:
4588     disableLearning();
4589     disableForwarding();
4590     */
4591     if(BR_STATE_BLOCKING != ptp->state)
4592     {
4593         if(!ptp->port->deleted)
4594             MSTP_OUT_set_state(ptp, BR_STATE_BLOCKING);
4595     }
4596     ptp->learning = false;
4597     ptp->forwarding = false;
4598 
4599     if(!begin)
4600         PSTSM_run(ptp, false /* actual run */);
4601 }
4602 
4603 static void PSTSM_to_LEARNING(per_tree_port_t *ptp)
4604 {
4605     ptp->PSTSM_state = PSTSM_LEARNING;
4606 
4607     /* enableLearning(); */
4608     if(BR_STATE_LEARNING != ptp->state)
4609     {
4610         if(!ptp->port->deleted)
4611             MSTP_OUT_set_state(ptp, BR_STATE_LEARNING);
4612     }
4613     ptp->learning = true;
4614 
4615     PSTSM_run(ptp, false /* actual run */);
4616 }
4617 
4618 static void PSTSM_to_FORWARDING(per_tree_port_t *ptp)
4619 {
4620     ptp->PSTSM_state = PSTSM_FORWARDING;
4621 
4622     /* enableForwarding(); */
4623     if(BR_STATE_FORWARDING != ptp->state)
4624     {
4625         if(!ptp->port->deleted)
4626             MSTP_OUT_set_state(ptp, BR_STATE_FORWARDING);
4627     }
4628     ptp->forwarding = true;
4629 
4630     /* No need to run, no one condition will be met
4631       PSTSM_run(ptp, false); */
4632 }
4633 
4634 static bool PSTSM_run(per_tree_port_t *ptp, bool dry_run)
4635 {
4636     switch(ptp->PSTSM_state)
4637     {
4638         case PSTSM_DISCARDING:
4639             if(ptp->learn)
4640             {
4641                 if(dry_run) /* state change */
4642                     return true;
4643                 PSTSM_to_LEARNING(ptp);
4644             }
4645             return false;
4646         case PSTSM_LEARNING:
4647             if(!ptp->learn)
4648             {
4649                 if(dry_run) /* state change */
4650                     return true;
4651                 PSTSM_to_DISCARDING(ptp, false);
4652             }
4653             else if(ptp->forward)
4654             {
4655                 if(dry_run) /* state change */
4656                     return true;
4657                 PSTSM_to_FORWARDING(ptp);
4658             }
4659             return false;
4660         case PSTSM_FORWARDING:
4661             if(!ptp->forward)
4662             {
4663                 if(dry_run) /* state change */
4664                     return true;
4665                 PSTSM_to_DISCARDING(ptp, false);
4666             }
4667             return false;
4668     }
4669 
4670     return false;
4671 }
4672 
4673 /* 13.36  Topology Change state machine */
4674 
4675 #define TCSM_begin(ptp) TCSM_to_INACTIVE((ptp), true)
4676 
4677 static void TCSM_to_INACTIVE(per_tree_port_t *ptp, bool begin)
4678 {
4679     ptp->TCSM_state = TCSM_INACTIVE;
4680 
4681     set_fdbFlush(ptp);
4682     assign(ptp->tcWhile, 0u);
4683     set_TopologyChange(ptp->tree, false, ptp->port);
4684     if(0 == ptp->MSTID) /* CIST */
4685         ptp->port->tcAck = false;
4686 
4687     if(!begin)
4688         TCSM_run(ptp, false /* actual run */);
4689 }
4690 
4691 static bool TCSM_to_LEARNING(per_tree_port_t *ptp, bool dry_run)
4692 {
4693     if(dry_run)
4694     {
4695         if((ptp->TCSM_state != TCSM_LEARNING) || ptp->rcvdTc || ptp->tcProp)
4696             return true;
4697         if(0 == ptp->MSTID) /* CIST */
4698         {
4699             port_t *prt = ptp->port;
4700             if(prt->rcvdTcn || prt->rcvdTcAck)
4701                 return true;
4702         }
4703         return false;
4704     }
4705 
4706     ptp->TCSM_state = TCSM_LEARNING;
4707 
4708     if(0 == ptp->MSTID) /* CIST */
4709     {
4710         port_t *prt = ptp->port;
4711         prt->rcvdTcn = false;
4712         prt->rcvdTcAck = false;
4713     }
4714     ptp->rcvdTc = false;
4715     ptp->tcProp = false;
4716 
4717     TCSM_run(ptp, false /* actual run */);
4718     return false;
4719 }
4720 
4721 static void TCSM_to_DETECTED(per_tree_port_t *ptp)
4722 {
4723     ptp->TCSM_state = TCSM_DETECTED;
4724 
4725     newTcWhile(ptp);
4726     setTcPropTree(ptp);
4727     /* newInfoXst = TRUE; */
4728     port_t *prt = ptp->port;
4729     if(0 == ptp->MSTID)
4730         prt->newInfo = true;
4731     else
4732         prt->newInfoMsti = true;
4733 
4734     TCSM_run(ptp, false /* actual run */);
4735 }
4736 
4737 static void TCSM_to_NOTIFIED_TCN(per_tree_port_t *ptp)
4738 {
4739     ptp->TCSM_state = TCSM_NOTIFIED_TCN;
4740 
4741     newTcWhile(ptp);
4742 
4743     TCSM_run(ptp, false /* actual run */);
4744 }
4745 
4746 static void TCSM_to_NOTIFIED_TC(per_tree_port_t *ptp)
4747 {
4748     ptp->TCSM_state = TCSM_NOTIFIED_TC;
4749 
4750     ptp->rcvdTc = false;
4751     if(0 == ptp->MSTID) /* CIST */
4752     {
4753         port_t *prt = ptp->port;
4754         prt->rcvdTcn = false;
4755         if(roleDesignated == ptp->role)
4756             prt->tcAck = true;
4757     }
4758     setTcPropTree(ptp);
4759 
4760     TCSM_run(ptp, false /* actual run */);
4761 }
4762 
4763 static void TCSM_to_PROPAGATING(per_tree_port_t *ptp)
4764 {
4765     ptp->TCSM_state = TCSM_PROPAGATING;
4766 
4767     newTcWhile(ptp);
4768     set_fdbFlush(ptp);
4769     ptp->tcProp = false;
4770 
4771     TCSM_run(ptp, false /* actual run */);
4772 }
4773 
4774 static void TCSM_to_ACKNOWLEDGED(per_tree_port_t *ptp)
4775 {
4776     ptp->TCSM_state = TCSM_ACKNOWLEDGED;
4777 
4778     assign(ptp->tcWhile, 0u);
4779     set_TopologyChange(ptp->tree, false, ptp->port);
4780     ptp->port->rcvdTcAck = false;
4781 
4782     TCSM_run(ptp, false /* actual run */);
4783 }
4784 
4785 static void TCSM_to_ACTIVE(per_tree_port_t *ptp)
4786 {
4787     ptp->TCSM_state = TCSM_ACTIVE;
4788 
4789     TCSM_run(ptp, false /* actual run */);
4790 }
4791 
4792 static bool TCSM_run(per_tree_port_t *ptp, bool dry_run)
4793 {
4794     bool active_port;
4795     port_t *prt = ptp->port;
4796 
4797     switch(ptp->TCSM_state)
4798     {
4799         case TCSM_INACTIVE:
4800             if(ptp->learn && !ptp->fdbFlush)
4801             {
4802                 if(dry_run) /* state change */
4803                     return true;
4804                 TCSM_to_LEARNING(ptp, false /* actual run */);
4805             }
4806             return false;
4807         case TCSM_LEARNING:
4808             active_port = (roleRoot == ptp->role)
4809                           || (roleDesignated == ptp->role)
4810                           || (roleMaster == ptp->role);
4811             if(active_port && ptp->forward && !prt->operEdge)
4812             {
4813                 if(dry_run) /* state change */
4814                     return true;
4815                 TCSM_to_DETECTED(ptp);
4816                 return false;
4817             }
4818             if(ptp->rcvdTc || prt->rcvdTcn || prt->rcvdTcAck || ptp->tcProp)
4819             {
4820                 return TCSM_to_LEARNING(ptp, dry_run);
4821             }
4822             else if(!active_port && !(ptp->learn || ptp->learning))
4823             {
4824                 if(dry_run) /* state change */
4825                     return true;
4826                 TCSM_to_INACTIVE(ptp, false);
4827             }
4828             return false;
4829         case TCSM_NOTIFIED_TCN:
4830             if(dry_run) /* state change */
4831                 return true;
4832             TCSM_to_NOTIFIED_TC(ptp);
4833             return false;
4834         case TCSM_DETECTED:
4835             /* return; */
4836         case TCSM_NOTIFIED_TC:
4837             /* return; */
4838         case TCSM_PROPAGATING:
4839             /* return; */
4840         case TCSM_ACKNOWLEDGED:
4841             if(dry_run) /* state change */
4842                 return true;
4843             TCSM_to_ACTIVE(ptp);
4844             return false;
4845         case TCSM_ACTIVE:
4846             active_port = (roleRoot == ptp->role)
4847                           || (roleDesignated == ptp->role)
4848                           || (roleMaster == ptp->role);
4849             if(!active_port || prt->operEdge)
4850             {
4851                 if(dry_run) /* state change */
4852                     return true;
4853                 TCSM_to_LEARNING(ptp, false /* actual run */);
4854                 return false;
4855             }
4856             if(prt->rcvdTcn)
4857             {
4858                 if(dry_run) /* state change */
4859                     return true;
4860                 TCSM_to_NOTIFIED_TCN(ptp);
4861                 return false;
4862             }
4863             if(ptp->rcvdTc)
4864             {
4865                 if(dry_run) /* state change */
4866                     return true;
4867                 TCSM_to_NOTIFIED_TC(ptp);
4868                 return false;
4869             }
4870             if(ptp->tcProp/* && !prt->operEdge */)
4871             {
4872                 if(dry_run) /* state change */
4873                     return true;
4874                 TCSM_to_PROPAGATING(ptp);
4875                 return false;
4876             }
4877             if(prt->rcvdTcAck)
4878             {
4879                 if(dry_run) /* state change */
4880                     return true;
4881                 TCSM_to_ACKNOWLEDGED(ptp);
4882                 return false;
4883             }
4884             return false;
4885     }
4886 
4887     return false;
4888 }
4889 
4890 /* Execute BEGIN state. We do not define BEGIN variable
4891  * but instead xxx_state_machines_begin execute begin state
4892  * abd do one step out of it
4893  */
4894 
4895 static void tree_state_machines_begin(tree_t *tree)
4896 {
4897     bridge_t *br = tree->bridge;
4898     per_tree_port_t *ptp;
4899 
4900     if(!br->bridgeEnabled)
4901         return;
4902 
4903     /* 13.32  Port Information state machine */
4904     FOREACH_PTP_IN_TREE(ptp, tree)
4905     {
4906         ptp->start_time = br->uptime; /* 12.8.2.2.3 b) */
4907         PISM_begin(ptp);
4908     }
4909 
4910     /* 13.33  Port Role Selection state machine */
4911     PRSSM_begin(tree);
4912 
4913     /* 13.34  Port Role Transitions state machine */
4914     FOREACH_PTP_IN_TREE(ptp, tree)
4915         PRTSM_begin(ptp);
4916     /* 13.35  Port State Transition state machine */
4917     FOREACH_PTP_IN_TREE(ptp, tree)
4918         PSTSM_begin(ptp);
4919     /* 13.36  Topology Change state machine */
4920     FOREACH_PTP_IN_TREE(ptp, tree)
4921         TCSM_begin(ptp);
4922 
4923     br_state_machines_run(br);
4924 }
4925 
4926 static void prt_state_machines_begin(port_t *prt)
4927 {
4928     bridge_t *br = prt->bridge;
4929     tree_t *tree;
4930     per_tree_port_t *ptp;
4931 
4932     if(!br->bridgeEnabled)
4933         return;
4934 
4935     /* 13.28  Port Receive state machine */
4936     PRSM_begin(prt);
4937     /* 13.29  Port Protocol Migration state machine */
4938     PPMSM_begin(prt);
4939     /* 13.30  Bridge Detection state machine */
4940     BDSM_begin(prt);
4941     /* 13.31  Port Transmit state machine */
4942     PTSM_begin(prt);
4943 
4944     /* 13.32  Port Information state machine */
4945     FOREACH_PTP_IN_PORT(ptp, prt)
4946     {
4947         ptp->start_time = br->uptime; /* 12.8.2.2.3 b) */
4948         PISM_begin(ptp);
4949     }
4950 
4951     /* 13.33  Port Role Selection state machine */
4952     FOREACH_TREE_IN_BRIDGE(tree, br)
4953         PRSSM_run(tree, false /* actual run */);
4954 
4955     /* 13.34  Port Role Transitions state machine */
4956     FOREACH_PTP_IN_PORT(ptp, prt)
4957         PRTSM_begin(ptp);
4958     /* 13.35  Port State Transition state machine */
4959     FOREACH_PTP_IN_PORT(ptp, prt)
4960         PSTSM_begin(ptp);
4961     /* 13.36  Topology Change state machine */
4962     FOREACH_PTP_IN_PORT(ptp, prt)
4963         TCSM_begin(ptp);
4964 
4965     br_state_machines_run(br);
4966 }
4967 
4968 static void br_state_machines_begin(bridge_t *br)
4969 {
4970     port_t *prt;
4971     per_tree_port_t *ptp;
4972     tree_t *tree;
4973 
4974     if(!br->bridgeEnabled)
4975         return;
4976 
4977     /* 13.28  Port Receive state machine */
4978     FOREACH_PORT_IN_BRIDGE(prt, br)
4979         PRSM_begin(prt);
4980     /* 13.29  Port Protocol Migration state machine */
4981     FOREACH_PORT_IN_BRIDGE(prt, br)
4982         PPMSM_begin(prt);
4983     /* 13.30  Bridge Detection state machine */
4984     FOREACH_PORT_IN_BRIDGE(prt, br)
4985         BDSM_begin(prt);
4986     /* 13.31  Port Transmit state machine */
4987     FOREACH_PORT_IN_BRIDGE(prt, br)
4988         PTSM_begin(prt);
4989 
4990     /* 13.32  Port Information state machine */
4991     FOREACH_PORT_IN_BRIDGE(prt, br)
4992     {
4993         FOREACH_PTP_IN_PORT(ptp, prt)
4994         {
4995             ptp->start_time = br->uptime; /* 12.8.2.2.3 b) */
4996             PISM_begin(ptp);
4997         }
4998     }
4999 
5000     /* 13.33  Port Role Selection state machine */
5001     FOREACH_TREE_IN_BRIDGE(tree, br)
5002         PRSSM_begin(tree);
5003 
5004     /* 13.34  Port Role Transitions state machine */
5005     FOREACH_PORT_IN_BRIDGE(prt, br)
5006     {
5007         FOREACH_PTP_IN_PORT(ptp, prt)
5008             PRTSM_begin(ptp);
5009     }
5010     /* 13.35  Port State Transition state machine */
5011     FOREACH_PORT_IN_BRIDGE(prt, br)
5012     {
5013         FOREACH_PTP_IN_PORT(ptp, prt)
5014             PSTSM_begin(ptp);
5015     }
5016     /* 13.36  Topology Change state machine */
5017     FOREACH_PORT_IN_BRIDGE(prt, br)
5018     {
5019         FOREACH_PTP_IN_PORT(ptp, prt)
5020             TCSM_begin(ptp);
5021     }
5022 
5023     br_state_machines_run(br);
5024 }
5025 
5026 /* Run each state machine.
5027  * Return false iff all state machines in dry run indicate that
5028  * state will not be changed. Otherwise return true.
5029  */
5030 static bool __br_state_machines_run(bridge_t *br, bool dry_run)
5031 {
5032     port_t *prt;
5033     per_tree_port_t *ptp;
5034     tree_t *tree;
5035 
5036     /* Check if bridge assurance timer expires */
5037     FOREACH_PORT_IN_BRIDGE(prt, br)
5038     {
5039         if(prt->portEnabled && assurancePort(prt)
5040            && (0 == prt->brAssuRcvdInfoWhile) && !prt->BaInconsistent
5041           )
5042         {
5043             if(dry_run) /* state change */
5044                 return true;
5045             prt->BaInconsistent = true;
5046             ERROR_PRTNAME(prt->bridge, prt, "Bridge assurance inconsistent");
5047         }
5048     }
5049 
5050     /* 13.28  Port Receive state machine */
5051     FOREACH_PORT_IN_BRIDGE(prt, br)
5052     {
5053         if(PRSM_run(prt, dry_run) && dry_run)
5054             return true;
5055     }
5056     /* 13.29  Port Protocol Migration state machine */
5057     FOREACH_PORT_IN_BRIDGE(prt, br)
5058     {
5059         if(PPMSM_run(prt, dry_run) && dry_run)
5060             return true;
5061     }
5062     /* 13.30  Bridge Detection state machine */
5063     FOREACH_PORT_IN_BRIDGE(prt, br)
5064     {
5065         if(BDSM_run(prt, dry_run) && dry_run)
5066             return true;
5067     }
5068     /* 13.31  Port Transmit state machine */
5069     FOREACH_PORT_IN_BRIDGE(prt, br)
5070     {
5071         if(PTSM_run(prt, dry_run) && dry_run)
5072             return true;
5073     }
5074 
5075     /* 13.32  Port Information state machine */
5076     FOREACH_PORT_IN_BRIDGE(prt, br)
5077     {
5078         FOREACH_PTP_IN_PORT(ptp, prt)
5079         {
5080             if(PISM_run(ptp, dry_run) && dry_run)
5081                 return true;
5082         }
5083     }
5084 
5085     /* 13.33  Port Role Selection state machine */
5086     FOREACH_TREE_IN_BRIDGE(tree, br)
5087     {
5088         if(PRSSM_run(tree, dry_run) && dry_run)
5089             return true;
5090     }
5091 
5092     /* 13.34  Port Role Transitions state machine */
5093     FOREACH_PORT_IN_BRIDGE(prt, br)
5094     {
5095         FOREACH_PTP_IN_PORT(ptp, prt)
5096         {
5097             if(PRTSM_run(ptp, dry_run) && dry_run)
5098                 return true;
5099         }
5100     }
5101     /* 13.35  Port State Transition state machine */
5102     FOREACH_PORT_IN_BRIDGE(prt, br)
5103     {
5104         FOREACH_PTP_IN_PORT(ptp, prt)
5105         {
5106             if(PSTSM_run(ptp, dry_run) && dry_run)
5107                 return true;
5108         }
5109     }
5110     /* 13.36  Topology Change state machine */
5111     FOREACH_PORT_IN_BRIDGE(prt, br)
5112     {
5113         FOREACH_PTP_IN_PORT(ptp, prt)
5114         {
5115             if(TCSM_run(ptp, dry_run) && dry_run)
5116                 return true;
5117         }
5118     }
5119 
5120     return false;
5121 }
5122 
5123 /* Run state machines until their state stabilizes.
5124  * Do not consume more than 1 second.
5125  */
5126 static void br_state_machines_run(bridge_t *br)
5127 {
5128     struct timespec tv, tv_end;
5129     signed long delta;
5130 
5131     if(!br->bridgeEnabled)
5132         return;
5133 
5134     clock_gettime(CLOCK_MONOTONIC, &tv_end);
5135     ++(tv_end.tv_sec);
5136 
5137     do {
5138         if(!__br_state_machines_run(br, true /* dry run */))
5139             return;
5140         __br_state_machines_run(br, false /* actual run */);
5141 
5142         /* Check for the timeout */
5143         clock_gettime(CLOCK_MONOTONIC, &tv);
5144         if(0 < (delta = tv.tv_sec - tv_end.tv_sec))
5145             return;
5146         if(0 == delta)
5147         {
5148             delta = tv.tv_nsec - tv_end.tv_nsec;
5149             if(0 < delta)
5150                 return;
5151         }
5152     } while(true);
5153 }
5154 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt