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