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