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