netif_utils: correctly close fd on read error
[project/ustp.git] / 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 }