]> git.ipfire.org Git - people/ms/mstpd.git/blame - mstp.c
Translate new ageing time to the kernel bridge code
[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 */
1e6d2d09
VD
1562 cist = true;
1563
1564 assign(mPri->RRootID, b->cistRRootID);
1565 assign(mPri->DesignatedPortID, b->cistPortID);
1566 assign(mPri->RootID, b->cistRootID);
1567 assign(mPri->ExtRootPathCost, b->cistExtRootPathCost);
1568 /* messageTimes */
58665938
VD
1569#define NEAREST_WHOLE_SECOND(msgTime) \
1570 ((128 > msgTime[1]) ? msgTime[0] : msgTime[0] + 1)
1571 mTimes->Forward_Delay = NEAREST_WHOLE_SECOND(b->ForwardDelay);
1572 mTimes->Max_Age = NEAREST_WHOLE_SECOND(b->MaxAge);
1573 mTimes->Message_Age = NEAREST_WHOLE_SECOND(b->MessageAge);
1574 mTimes->Hello_Time = NEAREST_WHOLE_SECOND(b->HelloTime);
1e6d2d09
VD
1575 if(protoMSTP > b->protocolVersion)
1576 { /* STP or RSTP Configuration BPDU */
1577 /* 13.26.6.NOTE: A Configuration BPDU implicitly conveys a
1578 * Designated Port Role */
1579 roleIsDesignated = true;
c6ed0c4c 1580 assign(mPri->IntRootPathCost, __constant_cpu_to_be32(0));
1e6d2d09
VD
1581 assign(mPri->DesignatedBridgeID, b->cistRRootID);
1582 /* messageTimes.remainingHops */
1583 assign(mTimes->remainingHops, prt->bridge->MaxHops);
1584 }
1585 else
1586 { /* MST BPDU */
16bfc77f
VD
1587 switch(BPDU_FLAGS_ROLE_GET(b->flags))
1588 {
1589 case encodedRoleAlternateBackup:
1590 case encodedRoleRoot:
1591 roleIsDesignated = false;
1592 break;
1593 case encodedRoleDesignated:
1594 roleIsDesignated = true;
1595 break;
1596 default:
1597 return OtherInfo;
1598 }
1e6d2d09
VD
1599 assign(mPri->IntRootPathCost, b->cistIntRootPathCost);
1600 assign(mPri->DesignatedBridgeID, b->cistBridgeID);
1601 /* messageTimes.remainingHops */
1602 assign(mTimes->remainingHops, b->cistRemainingHops);
1603 }
1604 }
1605 else
1606 { /* MSTI */
1607 if(protoMSTP > b->protocolVersion)
1608 return OtherInfo;
1609 msti_msg = ptp->rcvdMstiConfig;
1610 switch(BPDU_FLAGS_ROLE_GET(msti_msg->flags))
1611 {
1612 case encodedRoleAlternateBackup:
1613 case encodedRoleRoot:
1614 roleIsDesignated = false;
1615 break;
1616 case encodedRoleDesignated:
1617 roleIsDesignated = true;
1618 break;
1619 default:
1620 return OtherInfo;
1621 }
1622 cist = false;
1623
1624 assign(mPri->RRootID, msti_msg->mstiRRootID);
1625 assign(mPri->IntRootPathCost, msti_msg->mstiIntRootPathCost);
1626 /* Build MSTI DesignatedBridgeID */
1627 assign(mPri->DesignatedBridgeID, b->cistBridgeID);
1628 assign(mPri->DesignatedBridgeID.s.priority, ptp->MSTID);
1629 SET_PRIORITY_IN_IDENTIFIER(msti_msg->bridgeIdentifierPriority,
1630 mPri->DesignatedBridgeID);
1631 /* Build MSTI DesignatedPortID */
1632 assign(mPri->DesignatedPortID, b->cistPortID);
1633 SET_PRIORITY_IN_IDENTIFIER(msti_msg->portIdentifierPriority,
1634 mPri->DesignatedPortID);
1635 /* messageTimes */
1636 assign(mTimes->remainingHops, msti_msg->remainingHops);
1637 }
1638
1639 msg_Better_port = !betterorsamePriority(&(ptp->portPriority), mPri,
1640 0, 0, cist);
1641 if(roleIsDesignated)
1642 {
1643 /* a).1) */
1644 if(msg_Better_port
1645 || ((0 == memcmp(mPri->DesignatedBridgeID.s.mac_address,
1646 ptp->portPriority.DesignatedBridgeID.s.mac_address,
1647 ETH_ALEN)
1648 )
1649 && (0 == ((mPri->DesignatedPortID
1650 ^ ptp->portPriority.DesignatedPortID
1651 ) & __constant_cpu_to_be16(0x0FFF)
1652 )
1653 )
1654 )
1655 )
1656 return SuperiorDesignatedInfo;
1657
1658 /* a).2) */
1659 /* We already know that msgPriority _IS_NOT_BETTER_than portPriority.
1660 * So, if msgPriority _IS_SAME_OR_BETTER_than portPriority then
1661 * msgPriority _IS_SAME_as portPriority.
1662 */
1663 msg_SamePriorityAndTimers_port =
1664 samePriorityAndTimers(mPri, &(ptp->portPriority),
1665 mTimes, &(ptp->portTimes),
1666 cist);
1667 if((!msg_SamePriorityAndTimers_port)
1668 && betterorsamePriority(mPri, &(ptp->portPriority), 0, 0, cist)
1669 )
1670 return SuperiorDesignatedInfo;
1671
1672 /* b) */
1673 if(msg_SamePriorityAndTimers_port && (ioReceived == ptp->infoIs))
1674 return RepeatedDesignatedInfo;
1675
1676 /* c) */
1677 return InferiorDesignatedInfo;
1678 }
1679
1680 /* d) */
1681 if(!msg_Better_port)
1682 return InferiorRootAlternateInfo;
1683
1684 return OtherInfo;
1685}
1686
1687/* 13.26.7 recordAgreement */
1688static void recordAgreement(per_tree_port_t *ptp)
1689{
1690 bool cist_agreed, cist_proposing;
1691 per_tree_port_t *cist;
1692 port_t *prt = ptp->port;
1693 bpdu_t *b = &(prt->rcvdBpduData);
1694
1695 if(0 == ptp->MSTID)
1696 { /* CIST */
1697 if(rstpVersion(prt->bridge) && prt->operPointToPointMAC
1698 && (b->flags & (1 << offsetAgreement))
1699 )
1700 {
5453a511
VD
1701 ptp->agreed = true;
1702 ptp->proposing = false;
1e6d2d09
VD
1703 }
1704 else
5453a511 1705 ptp->agreed = false;
1e6d2d09
VD
1706 cist_agreed = ptp->agreed;
1707 cist_proposing = ptp->proposing;
1708 if(!prt->rcvdInternal)
1709 list_for_each_entry_continue(ptp, &prt->trees, port_list)
1710 {
5453a511
VD
1711 ptp->agreed = cist_agreed;
1712 ptp->proposing = cist_proposing;
1e6d2d09
VD
1713 }
1714 return;
1715 }
1716 /* MSTI */
1717 cist = GET_CIST_PTP_FROM_PORT(prt);
1718 if(prt->operPointToPointMAC
1719 && cmp(b->cistRootID, ==, cist->portPriority.RootID)
1720 && cmp(b->cistExtRootPathCost, ==, cist->portPriority.ExtRootPathCost)
1721 && cmp(b->cistRRootID, ==, cist->portPriority.RRootID)
1722 && (ptp->rcvdMstiConfig->flags & (1 << offsetAgreement))
1723 )
1724 {
5453a511
VD
1725 ptp->agreed = true;
1726 ptp->proposing = false;
1e6d2d09
VD
1727 }
1728 else
5453a511 1729 ptp->agreed = false;
1e6d2d09
VD
1730}
1731
1732/* 13.26.8 recordDispute */
1733static void recordDispute(per_tree_port_t *ptp)
1734{
1735 port_t *prt;
1736
1737 if(0 == ptp->MSTID)
1738 { /* CIST */
1739 prt = ptp->port;
1740 /* 802.1Q-2005 is somewhat unclear for the case (!prt->rcvdInternal):
1741 * if we should record dispute for all MSTIs unconditionally
1742 * or only when CIST Learning flag is set in BPDU.
1743 * I guess that in this case MSTIs should be in sync with CIST
1744 * so record dispute for the MSTIs only when the same is done for CIST.
1745 * Additional supporting argument to this guess is that in
1746 * setTcFlags() we do the same.
1747 * But that is only a guess and I could be wrong here ;)
1748 * Maybe 802.1Q-2011 clarifies this, but I don't have the text.
1749 */
1750 if(prt->rcvdBpduData.flags & (1 << offsetLearnig))
1751 {
5453a511
VD
1752 ptp->disputed = true;
1753 ptp->agreed = false;
1e6d2d09
VD
1754 if(!prt->rcvdInternal)
1755 list_for_each_entry_continue(ptp, &prt->trees, port_list)
1756 {
5453a511
VD
1757 ptp->disputed = true;
1758 ptp->agreed = false;
1e6d2d09
VD
1759 }
1760 }
1761 return;
1762 }
1763 /* MSTI */
1764 if(ptp->rcvdMstiConfig->flags & (1 << offsetLearnig))
1765 {
5453a511
VD
1766 ptp->disputed = true;
1767 ptp->agreed = false;
1e6d2d09
VD
1768 }
1769}
1770
1771/* 13.26.9 recordMastered */
1772static void recordMastered(per_tree_port_t *ptp)
1773{
1774 port_t *prt = ptp->port;
1775
1776 if(0 == ptp->MSTID)
1777 { /* CIST */
1778 if(!prt->rcvdInternal)
1779 list_for_each_entry_continue(ptp, &prt->trees, port_list)
5453a511 1780 ptp->mastered = false;
1e6d2d09
VD
1781 return;
1782 }
1783 /* MSTI */
1784 ptp->mastered = prt->operPointToPointMAC
1785 && (ptp->rcvdMstiConfig->flags & (1 << offsetMaster));
1786}
1787
1788/* 13.26.f) recordPriority */
1789static void recordPriority(per_tree_port_t *ptp)
1790{
1791 assign(ptp->portPriority, ptp->msgPriority);
1792}
1793
1794/* 13.26.10 recordProposal */
1795static void recordProposal(per_tree_port_t *ptp)
1796{
1797 bool cist_proposed;
1798 port_t *prt;
1799
1800 /* 802.1Q-2005 says to check if received message conveys
1801 * a Designated Port Role. But there is no need in this check,
1802 * as it is always true. This function is called only in two states:
1803 * PISM_SUPERIOR_DESIGNATED and PISM_REPEATED_DESIGNATED, which
1804 * can be entered only if rcvInfo returns
1805 * SuperiorDesignatedInfo or RepeatedDesignatedInfo.
1806 * Which in turn can only happen if message conveys designated role
1807 * (see rcvInfo).
1808 */
1809 if(0 == ptp->MSTID)
1810 { /* CIST */
1811 prt = ptp->port;
1812 if(prt->rcvdBpduData.flags & (1 << offsetProposal))
5453a511 1813 ptp->proposed = true;
1e6d2d09
VD
1814 cist_proposed = ptp->proposed;
1815 if(!prt->rcvdInternal)
1816 list_for_each_entry_continue(ptp, &prt->trees, port_list)
5453a511 1817 ptp->proposed = cist_proposed;
1e6d2d09
VD
1818 return;
1819 }
1820 /* MSTI */
1821 if(ptp->rcvdMstiConfig->flags & (1 << offsetProposal))
5453a511 1822 ptp->proposed = true;
1e6d2d09
VD
1823}
1824
1825/* 13.26.11 recordTimes */
1826static void recordTimes(per_tree_port_t *ptp)
1827{
1828 assign(ptp->portTimes, ptp->msgTimes);
58665938
VD
1829
1830 if(MIN_COMPAT_HELLO_TIME > ptp->portTimes.Hello_Time)
1831 ptp->portTimes.Hello_Time = MIN_COMPAT_HELLO_TIME;
1e6d2d09
VD
1832}
1833
1834/* 13.24.s) + 17.19.7 of 802.1D : fdbFlush */
1835static void set_fdbFlush(per_tree_port_t *ptp)
1836{
1837 port_t *prt = ptp->port;
1838
1839 if(prt->operEdge)
1840 {
5453a511 1841 ptp->fdbFlush = false;
1e6d2d09
VD
1842 return;
1843 }
1844
1845 bridge_t *br = prt->bridge;
1846
1847 if(rstpVersion(br))
1848 {
5453a511 1849 ptp->fdbFlush = true;
5323b89e 1850 ptp->calledFromFlushRoutine = true;
1e6d2d09 1851 MSTP_OUT_flush_all_fids(ptp);
5323b89e 1852 ptp->calledFromFlushRoutine = false;
1e6d2d09
VD
1853 }
1854 else
1855 {
1856 per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
58665938 1857 unsigned int FwdDelay = cist->designatedTimes.Forward_Delay;
1e6d2d09
VD
1858 /* Initiate rapid ageing */
1859 MSTP_OUT_set_ageing_time(br, FwdDelay);
1860 assign(br->rapidAgeingWhile, FwdDelay);
5453a511 1861 ptp->fdbFlush = false;
1e6d2d09
VD
1862 }
1863}
1864
1865/* 13.26.12 setRcvdMsgs */
1866static void setRcvdMsgs(port_t *prt)
1867{
1868 msti_configuration_message_t *msti_msg;
1869 int i;
1870 __be16 msg_MSTID;
1871 bool found;
1872 per_tree_port_t *ptp = GET_CIST_PTP_FROM_PORT(prt);
1873 ptp->rcvdMsg = true;
1874
1875 /* 802.1Q-2005 says:
1876 * "Make the received CST or CIST message available to the CIST Port
1877 * Information state machines"
1878 * No need to do something special here, we already have rcvdBpduData.
1879 */
1880
1881 if(prt->rcvdInternal)
1882 {
1883 list_for_each_entry_continue(ptp, &prt->trees, port_list)
1884 {
1885 found = false;
1886 /* Find if message for this MSTI is conveyed in the BPDU */
1887 for(i = 0, msti_msg = prt->rcvdBpduData.mstConfiguration;
1888 i < prt->rcvdBpduNumOfMstis;
1889 ++i, ++msti_msg)
1890 {
1891 msg_MSTID = msti_msg->mstiRRootID.s.priority
1892 & __constant_cpu_to_be16(0x0FFF);
1893 if(msg_MSTID == ptp->MSTID)
1894 {
1895 found = true;
1896 break;
1897 }
1898 }
1899 if(found)
1900 {
1901 ptp->rcvdMsg = true;
1902 /* 802.1Q-2005 says:
1903 * "Make available each MSTI message and the common parts of
1904 * the CIST message priority (the CIST Root Identifier,
1905 * External Root Path Cost and Regional Root Identifier)
1906 * to the Port Information state machine for that MSTI"
1907 * We set pointer to the MSTI configuration message for
1908 * fast access, while do not anything special for common
1909 * parts of the message, as the whole message is available
1910 * in rcvdBpduData.
1911 */
1912 ptp->rcvdMstiConfig = msti_msg;
1913 }
1914 }
1915 }
1916}
1917
1918/* 13.26.13 setReRootTree */
1919static void setReRootTree(tree_t *tree)
1920{
1921 per_tree_port_t *ptp;
1922
1923 FOREACH_PTP_IN_TREE(ptp, tree)
5453a511 1924 ptp->reRoot = true;
1e6d2d09
VD
1925}
1926
1927/* 13.26.14 setSelectedTree */
1928static void setSelectedTree(tree_t *tree)
1929{
1930 per_tree_port_t *ptp;
1931
1932 /*
1933 * 802.1Q-2005 says that I should check "reselect" var
1934 * and take no action if it is "true" for any of the ports.
1935 * But there is no need in this check as setSelectedTree is called
1936 * only from PRSSM_to_ROLE_SELECTION, which is atomic, and it is called
1937 * in this sequence (13.33):
1938 * clearReselectTree(tree);
1939 * updtRolesTree(tree);
1940 * setSelectedTree(tree);
1941 * And we know that clearReselectTree resets "reselect" for all ports
1942 * and updtRolesTree() does not change value of "reselect".
1943 */
1944 FOREACH_PTP_IN_TREE(ptp, tree)
5453a511 1945 ptp->selected = true;
1e6d2d09
VD
1946}
1947
1948/* 13.26.15 setSyncTree */
1949static void setSyncTree(tree_t *tree)
1950{
1951 per_tree_port_t *ptp;
1952
1953 FOREACH_PTP_IN_TREE(ptp, tree)
5453a511 1954 ptp->sync = true;
1e6d2d09
VD
1955}
1956
1957/* 13.26.16 setTcFlags */
1958static void setTcFlags(per_tree_port_t *ptp)
1959{
1960 __u8 cistFlags;
1961 port_t *prt;
1962
1963 if(0 == ptp->MSTID)
1964 { /* CIST */
1965 prt = ptp->port;
1966 cistFlags = prt->rcvdBpduData.flags;
1967 if(cistFlags & (1 << offsetTcAck))
5453a511 1968 prt->rcvdTcAck = true;
1e6d2d09
VD
1969 if(cistFlags & (1 << offsetTc))
1970 {
5453a511 1971 ptp->rcvdTc = true;
1e6d2d09
VD
1972 if(!prt->rcvdInternal)
1973 list_for_each_entry_continue(ptp, &prt->trees, port_list)
5453a511 1974 ptp->proposed = true;
1e6d2d09
VD
1975 }
1976 return;
1977 }
1978 /* MSTI */
1979 if(ptp->rcvdMstiConfig->flags & (1 << offsetTc))
5453a511 1980 ptp->rcvdTc = true;
1e6d2d09
VD
1981}
1982
1983/* 13.26.17 setTcPropTree */
1984static void setTcPropTree(per_tree_port_t *ptp)
1985{
1986 per_tree_port_t *ptp_1;
1987
1988 if(ptp->port->restrictedTcn)
1989 return;
1990
1991 FOREACH_PTP_IN_TREE(ptp_1, ptp->tree)
1992 {
1993 if(ptp != ptp_1)
5453a511 1994 ptp_1->tcProp = true;
1e6d2d09
VD
1995 }
1996}
1997
1998/* 13.26.18 syncMaster */
1999static void syncMaster(bridge_t *br)
2000{
2001 per_tree_port_t *ptp;
2002 tree_t *tree = GET_CIST_TREE(br);
2003
2004 /* For each MSTI */
2005 list_for_each_entry_continue(tree, &br->trees, bridge_list)
2006 {
2007 FOREACH_PTP_IN_TREE(ptp, tree)
2008 {
2009 /* for each Port that has infoInternal set */
2010 if(ptp->port->infoInternal)
2011 {
5453a511
VD
2012 ptp->agree = false;
2013 ptp->agreed = false;
2014 ptp->synced = false;
2015 ptp->sync = true;
1e6d2d09
VD
2016 }
2017 }
2018 }
2019}
2020
2021/* 13.26.19 txConfig */
2022static void txConfig(port_t *prt)
2023{
2024 bpdu_t b;
2025 per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
2026
2027 b.protocolIdentifier = 0;
2028 b.protocolVersion = protoSTP;
2029 b.bpduType = bpduTypeConfig;
2030 /* Standard says "tcWhile ... for the Port". Which one tcWhile?
2031 * I guess that this means tcWhile for the CIST.
2032 * But that is only a guess and I could be wrong here ;)
2033 * Maybe 802.1Q-2011 clarifies this, but I don't have the text.
2034 */
2035 b.flags = (0 != cist->tcWhile) ? (1 << offsetTc) : 0;
2036 if(prt->tcAck)
2037 b.flags |= (1 << offsetTcAck);
2038 assign(b.cistRootID, cist->designatedPriority.RootID);
2039 assign(b.cistExtRootPathCost, cist->designatedPriority.ExtRootPathCost);
2040 assign(b.cistRRootID, cist->designatedPriority.DesignatedBridgeID);
2041 assign(b.cistPortID, cist->designatedPriority.DesignatedPortID);
58665938
VD
2042 b.MessageAge[0] = cist->designatedTimes.Message_Age;
2043 b.MessageAge[1] = 0;
2044 b.MaxAge[0] = cist->designatedTimes.Max_Age;
2045 b.MaxAge[1] = 0;
2046 b.HelloTime[0] = cist->portTimes.Hello_Time; /* ! use portTimes ! */
2047 b.HelloTime[1] = 0;
2048 b.ForwardDelay[0] = cist->designatedTimes.Forward_Delay;
2049 b.ForwardDelay[1] = 0;
1e6d2d09
VD
2050
2051 MSTP_OUT_tx_bpdu(prt, &b, CONFIG_BPDU_SIZE);
2052}
2053
2054static inline __u8 message_role_from_port_role(per_tree_port_t *ptp)
2055{
2056 switch(ptp->role)
2057 {
2058 case roleRoot:
2059 return encodedRoleRoot;
2060 case roleDesignated:
2061 return encodedRoleDesignated;
2062 case roleAlternate:
2063 case roleBackup:
2064 return encodedRoleAlternateBackup;
2065 case roleMaster:
2066 return encodedRoleMaster;
2067 default:
2068 ERROR_PRTNAME(ptp->port->bridge, ptp->port,
2069 "Attempt to send from port with Disabled role");
2070 return encodedRoleAlternateBackup;
2071 }
2072}
2073
2074/* 13.26.20 txMstp */
2075static void txMstp(port_t *prt)
2076{
2077 bpdu_t b;
2078 bridge_t *br = prt->bridge;
2079 per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
2080 int msti_msgs_total_size;
2081 per_tree_port_t *ptp;
2082 msti_configuration_message_t *msti_msg;
2083
2084 b.protocolIdentifier = 0;
2085 b.bpduType = bpduTypeRST;
2086 /* Standard says "{tcWhile, agree, proposing, role} ... for the Port".
2087 * Which one {tcWhile, agree, proposing, role}?
2088 * I guess that this means {tcWhile, agree, proposing, role} for the CIST.
2089 * But that is only a guess and I could be wrong here ;)
2090 * Maybe 802.1Q-2011 clarifies this, but I don't have the text.
2091 */
2092 b.flags = BPDU_FLAGS_ROLE_SET(message_role_from_port_role(cist));
2093 if(0 != cist->tcWhile)
2094 b.flags |= (1 << offsetTc);
2095 if(cist->proposing)
2096 b.flags |= (1 << offsetProposal);
2097 if(cist->learning)
2098 b.flags |= (1 << offsetLearnig);
2099 if(cist->forwarding)
2100 b.flags |= (1 << offsetForwarding);
2101 if(cist->agree)
2102 b.flags |= (1 << offsetAgreement);
2103 assign(b.cistRootID, cist->designatedPriority.RootID);
2104 assign(b.cistExtRootPathCost, cist->designatedPriority.ExtRootPathCost);
2105 assign(b.cistRRootID, cist->designatedPriority.RRootID);
2106 assign(b.cistPortID, cist->designatedPriority.DesignatedPortID);
58665938
VD
2107 b.MessageAge[0] = cist->designatedTimes.Message_Age;
2108 b.MessageAge[1] = 0;
2109 b.MaxAge[0] = cist->designatedTimes.Max_Age;
2110 b.MaxAge[1] = 0;
2111 b.HelloTime[0] = cist->portTimes.Hello_Time; /* ! use portTimes ! */
2112 b.HelloTime[1] = 0;
2113 b.ForwardDelay[0] = cist->designatedTimes.Forward_Delay;
2114 b.ForwardDelay[1] = 0;
1e6d2d09
VD
2115
2116 b.version1_len = 0;
2117
2118 if(br->ForceProtocolVersion < protoMSTP)
2119 {
2120 b.protocolVersion = protoRSTP;
2121 MSTP_OUT_tx_bpdu(prt, &b, RST_BPDU_SIZE);
2122 return;
2123 }
2124
2125 b.protocolVersion = protoMSTP;
2126
2127 /* MST specific fields */
2128 assign(b.mstConfigurationIdentifier, br->MstConfigId);
2129 assign(b.cistIntRootPathCost, cist->designatedPriority.IntRootPathCost);
2130 assign(b.cistBridgeID, cist->designatedPriority.DesignatedBridgeID);
2131 assign(b.cistRemainingHops, cist->designatedTimes.remainingHops);
2132
2133 msti_msgs_total_size = 0;
2134 ptp = cist;
2135 msti_msg = b.mstConfiguration;
2136 /* 13.26.20.f) requires that msti configs should be inserted in
2137 * MSTID order. This is met by inserting trees in port's list of trees
2138 * in sorted (by MSTID) order (see MSTP_IN_create_msti) */
2139 list_for_each_entry_continue(ptp, &prt->trees, port_list)
2140 {
2141 msti_msg->flags =
2142 BPDU_FLAGS_ROLE_SET(message_role_from_port_role(ptp));
2143 if(0 != ptp->tcWhile)
2144 msti_msg->flags |= (1 << offsetTc);
2145 if(ptp->proposing)
2146 msti_msg->flags |= (1 << offsetProposal);
2147 if(ptp->learning)
2148 msti_msg->flags |= (1 << offsetLearnig);
2149 if(ptp->forwarding)
2150 msti_msg->flags |= (1 << offsetForwarding);
2151 if(ptp->agree)
2152 msti_msg->flags |= (1 << offsetAgreement);
2153 if(ptp->master)
2154 msti_msg->flags |= (1 << offsetMaster);
2155 assign(msti_msg->mstiRRootID, ptp->designatedPriority.RRootID);
2156 assign(msti_msg->mstiIntRootPathCost,
2157 ptp->designatedPriority.IntRootPathCost);
2158 msti_msg->bridgeIdentifierPriority =
2159 GET_PRIORITY_FROM_IDENTIFIER(ptp->designatedPriority.DesignatedBridgeID);
2160 msti_msg->portIdentifierPriority =
2161 GET_PRIORITY_FROM_IDENTIFIER(ptp->designatedPriority.DesignatedPortID);
2162 assign(msti_msg->remainingHops, ptp->designatedTimes.remainingHops);
2163
2164 msti_msgs_total_size += sizeof(msti_configuration_message_t);
2165 ++msti_msg;
2166 }
2167
2168 assign(b.version3_len, __cpu_to_be16(MST_BPDU_VER3LEN_WO_MSTI_MSGS
2169 + msti_msgs_total_size));
2170 MSTP_OUT_tx_bpdu(prt, &b, MST_BPDU_SIZE_WO_MSTI_MSGS
2171 + msti_msgs_total_size);
2172}
2173
2174/* 13.26.a) txTcn */
2175static void txTcn(port_t *prt)
2176{
2177 bpdu_t b;
2178
2179 b.protocolIdentifier = 0;
2180 b.protocolVersion = protoSTP;
2181 b.bpduType = bpduTypeTCN;
2182
2183 MSTP_OUT_tx_bpdu(prt, &b, TCN_BPDU_SIZE);
2184}
2185
2186/* 13.26.21 updtBPDUVersion */
2187static void updtBPDUVersion(port_t *prt)
2188{
2189 if(protoRSTP <= prt->rcvdBpduData.protocolVersion)
5453a511 2190 prt->rcvdRSTP = true;
1e6d2d09 2191 else
5453a511 2192 prt->rcvdSTP = true;
1e6d2d09
VD
2193}
2194
2195/* 13.26.22 updtRcvdInfoWhile */
2196static void updtRcvdInfoWhile(per_tree_port_t *ptp)
2197{
2198 port_t *prt = ptp->port;
2199 per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
58665938
VD
2200 unsigned int Message_Age = cist->portTimes.Message_Age;
2201 unsigned int Max_Age = cist->portTimes.Max_Age;
2202 unsigned int Hello_Time = cist->portTimes.Hello_Time;
1e6d2d09
VD
2203
2204 if((!prt->rcvdInternal && ((Message_Age + 1) <= Max_Age))
2205 || (prt->rcvdInternal && (cist->portTimes.remainingHops > 1))
2206 )
2207 ptp->rcvdInfoWhile = 3 * Hello_Time;
2208 else
2209 ptp->rcvdInfoWhile = 0;
2210}
2211
2212/* 13.26.24 updtRolesDisabledTree */
2213static void updtRolesDisabledTree(tree_t *tree)
2214{
2215 per_tree_port_t *ptp;
2216
2217 FOREACH_PTP_IN_TREE(ptp, tree)
5453a511 2218 ptp->selectedRole = roleDisabled;
1e6d2d09
VD
2219}
2220
2221/* 13.26.23 updtRolesTree */
2222static void updtRolesTree(tree_t *tree)
2223{
2224 per_tree_port_t *ptp, *root_ptp = NULL;
2225 port_priority_vector_t root_path_priority;
2226 bridge_identifier_t prevRRootID = tree->rootPriority.RRootID;
2227 __be32 prevExtRootPathCost = tree->rootPriority.ExtRootPathCost;
2228 bool cist = (0 == tree->MSTID);
2229 times_t *timesOfRootPort;
2230
2231 /* a), b) Select new root priority vector = {rootPriority, rootPortId} */
2232 /* Initial value = bridge priority vector = {BridgePriority, 0} */
2233 assign(tree->rootPriority, tree->BridgePriority);
2234 assign(tree->rootPortId, __constant_cpu_to_be16(0));
2235 /* Now check root path priority vectors of all ports in tree and see if
2236 * there is a better vector */
2237 FOREACH_PTP_IN_TREE(ptp, tree)
2238 {
2239 port_t *prt = ptp->port;
2240 /* 802.1Q says to calculate root priority vector only if port
2241 * is not Disabled, but check (infoIs == ioReceived) covers
2242 * the case (infoIs != ioDisabled).
2243 */
2244 if((ioReceived == ptp->infoIs) && !prt->restrictedRole
2245 && cmp(ptp->portPriority.DesignatedBridgeID, !=,
2246 tree->BridgeIdentifier)
2247 )
2248 {
2249 root_path_priority = ptp->portPriority;
2250 if(prt->rcvdInternal)
2251 {
2252 assign(root_path_priority.IntRootPathCost,
2253 __cpu_to_be32(__be32_to_cpu(root_path_priority.IntRootPathCost)
2254 + ptp->InternalPortPathCost)
2255 );
2256 }
2257 else
2258 {
2259 assign(root_path_priority.ExtRootPathCost,
2260 __cpu_to_be32(__be32_to_cpu(root_path_priority.ExtRootPathCost)
2261 + prt->ExternalPortPathCost)
2262 );
2263 assign(root_path_priority.IntRootPathCost,
2264 __constant_cpu_to_be32(0));
2265 }
2266 if(betterorsamePriority(&root_path_priority, &tree->rootPriority,
2267 ptp->portId, tree->rootPortId, cist))
2268 {
2269 assign(tree->rootPriority, root_path_priority);
2270 assign(tree->rootPortId, ptp->portId);
2271 root_ptp = ptp;
2272 }
2273 }
2274 }
2275
2276 /* 802.1q-2005 says, that at some point we need compare portTimes with
2277 * "... one for the Root Port ...". Bad IEEE! Why not mention explicit
2278 * var names??? (see 13.26.23.g) for instance)
2279 * So, now I should guess what will work for the timesOfRootPort.
2280 * Below is the result of my guess. I could be wrong, of course:
2281 * timesOfRootPort = root_ptp ? &root_ptp->portTimes
2282 * : &tree->BridgeTimes;
2283 * NOTE: Both Alex Rozin (author of rstplib) and Srinivas Aji (author
2284 * of rstpd) compare portTimes with designatedTimes instead of
2285 * timesOfRootPort. This differs from my interpretation of the standard
2286 * because designatedTimes have incremented Message_Age (or decremented
2287 * remainingHops if rcvdInternal).
2288 */
2289
2290 /* c) Set new rootTimes */
2291 if(root_ptp)
2292 {
2293 assign(tree->rootTimes, root_ptp->portTimes);
2294 port_t *prt = root_ptp->port;
2295 if(prt->rcvdInternal)
2296 {
2297 if(tree->rootTimes.remainingHops)
2298 --(tree->rootTimes.remainingHops);
2299 }
2300 else
58665938 2301 ++(tree->rootTimes.Message_Age);
1e6d2d09
VD
2302 timesOfRootPort = &root_ptp->portTimes;
2303 }
2304 else
2305 {
2306 assign(tree->rootTimes, tree->BridgeTimes);
2307 timesOfRootPort = &tree->BridgeTimes;
2308 }
2309
2310 FOREACH_PTP_IN_TREE(ptp, tree)
2311 {
2312 port_t *prt = ptp->port;
2313
2314 /* d) Set new designatedPriority */
2315 assign(ptp->designatedPriority, tree->rootPriority);
2316 assign(ptp->designatedPriority.DesignatedBridgeID,
2317 tree->BridgeIdentifier);
2318 assign(ptp->designatedPriority.DesignatedPortID, ptp->portId);
2319 /* I am not sure which condition to check here, as 802.1Q-2005 says:
2320 * "... If {Port} is attached to a LAN that has one or more STP Bridges
2321 * attached (as determined by the Port Protocol Migration state
2322 * machine) ..." -- why not to mention explicit var name? Bad IEEE.
2323 * But I guess that sendSTP (i.e. !sendRSTP) var will do ;)
2324 */
2325 if(cist && !prt->sendRSTP)
2326 assign(ptp->designatedPriority.RRootID, tree->BridgeIdentifier);
2327
2328 /* e) Set new designatedTimes */
2329 assign(ptp->designatedTimes, tree->rootTimes);
2330 }
2331
2332 /* syncMaster */
2333 if(cist && cmp(tree->rootPriority.RRootID, !=, prevRRootID)
2334 && ((0 != tree->rootPriority.ExtRootPathCost)
2335 || (0 != prevExtRootPathCost)
2336 )
2337 )
2338 syncMaster(tree->bridge);
2339
2340 FOREACH_PTP_IN_TREE(ptp, tree)
2341 {
2342 port_t *prt = ptp->port;
2343
2344 /* f) Set Disabled role */
2345 if(ioDisabled == ptp->infoIs)
2346 {
5453a511 2347 ptp->selectedRole = roleDisabled;
1e6d2d09
VD
2348 continue;
2349 }
2350
2351 if(!cist && (ioReceived == ptp->infoIs)
2352 && !prt->infoInternal)
2353 {
2354 /* g) Set role for the boundary port in MSTI */
2355 per_tree_port_t *cist_tree = GET_CIST_PTP_FROM_PORT(prt);
2356 if(roleRoot == cist_tree->selectedRole)
2357 {
5453a511 2358 ptp->selectedRole = roleMaster;
1e6d2d09
VD
2359 if(!samePriorityAndTimers(&ptp->portPriority,
2360 &ptp->designatedPriority,
2361 &ptp->portTimes,
2362 timesOfRootPort,
2363 /*cist*/ false))
5453a511 2364 ptp->updtInfo = true;
1e6d2d09
VD
2365 continue;
2366 }
2367 if(roleAlternate == cist_tree->selectedRole)
2368 {
5453a511 2369 ptp->selectedRole = roleAlternate;
1e6d2d09
VD
2370 if(!samePriorityAndTimers(&ptp->portPriority,
2371 &ptp->designatedPriority,
2372 &ptp->portTimes,
2373 timesOfRootPort,
2374 /*cist*/ false))
5453a511 2375 ptp->updtInfo = true;
1e6d2d09
VD
2376 continue;
2377 }
2378 }
2379 else /* if(cist || (ioReceived != ptp->infoIs) || prt->infoInternal) */
2380 {
2381 /* h) Set role for the aged info */
2382 if(ioAged == ptp->infoIs)
2383 {
5453a511
VD
2384 ptp->selectedRole = roleDesignated;
2385 ptp->updtInfo = true;
1e6d2d09
VD
2386 continue;
2387 }
2388 /* i) Set role for the mine info */
2389 if(ioMine == ptp->infoIs)
2390 {
5453a511 2391 ptp->selectedRole = roleDesignated;
1e6d2d09
VD
2392 if(!samePriorityAndTimers(&ptp->portPriority,
2393 &ptp->designatedPriority,
2394 &ptp->portTimes,
2395 timesOfRootPort,
2396 /*cist*/ false))
5453a511 2397 ptp->updtInfo = true;
1e6d2d09
VD
2398 continue;
2399 }
2400 if(ioReceived == ptp->infoIs)
2401 {
2402 /* j) Set Root role */
2403 if(root_ptp == ptp)
2404 {
5453a511
VD
2405 ptp->selectedRole = roleRoot;
2406 ptp->updtInfo = false;
1e6d2d09
VD
2407 continue;
2408 }
2409 if(betterorsamePriority(&ptp->portPriority,
2410 &ptp->designatedPriority,
2411 0, 0, cist))
2412 {
2413 if(cmp(ptp->portPriority.DesignatedBridgeID, !=,
2414 tree->BridgeIdentifier))
2415 {
2416 /* k) Set Alternate role */
5453a511 2417 ptp->selectedRole = roleAlternate;
1e6d2d09
VD
2418 }
2419 else
2420 {
2421 /* l) Set Backup role */
5453a511 2422 ptp->selectedRole = roleBackup;
1e6d2d09
VD
2423 }
2424 /* reset updtInfo for both k) and l) */
5453a511 2425 ptp->updtInfo = false;
1e6d2d09
VD
2426 continue;
2427 }
2428 else /* designatedPriority is better than portPriority */
2429 {
2430 /* m) Set Designated role */
5453a511
VD
2431 ptp->selectedRole = roleDesignated;
2432 ptp->updtInfo = true;
1e6d2d09
VD
2433 continue;
2434 }
2435 }
2436 }
2437 }
2438}
2439
2440/* 13.27 The Port Timers state machine */
2441
2442static void PTSM_tick(port_t *prt)
2443{
2444 per_tree_port_t *ptp;
2445
2446 if(prt->helloWhen)
2447 --(prt->helloWhen);
2448 if(prt->mdelayWhile)
2449 --(prt->mdelayWhile);
2450 if(prt->edgeDelayWhile)
2451 --(prt->edgeDelayWhile);
2452 if(prt->txCount)
2453 --(prt->txCount);
2454
2455 FOREACH_PTP_IN_PORT(ptp, prt)
2456 {
2457 if(ptp->fdWhile)
2458 --(ptp->fdWhile);
2459 if(ptp->rrWhile)
2460 --(ptp->rrWhile);
2461 if(ptp->rbWhile)
2462 --(ptp->rbWhile);
2463 if(ptp->tcWhile)
2464 {
2465 if(0 == --(ptp->tcWhile))
2466 set_TopologyChange(ptp->tree, false);
2467 }
2468 if(ptp->rcvdInfoWhile)
2469 --(ptp->rcvdInfoWhile);
2470 }
2471}
2472
2473/* 13.28 Port Receive state machine */
2474#define PRSM_begin(prt) PRSM_to_DISCARD(prt)
2475static void PRSM_to_DISCARD(port_t *prt/*, bool begin*/)
2476{
2477 prt->PRSM_state = PRSM_DISCARD;
2478
5453a511
VD
2479 prt->rcvdBpdu = false;
2480 prt->rcvdRSTP = false;
2481 prt->rcvdSTP = false;
1e6d2d09
VD
2482 clearAllRcvdMsgs(prt);
2483 assign(prt->edgeDelayWhile, prt->bridge->Migrate_Time);
2484
2485 /* No need to run, no one condition will be met
2486 * if(!begin)
2487 * PRSM_run(prt); */
2488}
2489
2490static void PRSM_to_RECEIVE(port_t *prt)
2491{
2492 prt->PRSM_state = PRSM_RECEIVE;
2493
2494 updtBPDUVersion(prt);
2495 prt->rcvdInternal = fromSameRegion(prt);
2496 setRcvdMsgs(prt);
5453a511
VD
2497 prt->operEdge = false;
2498 prt->rcvdBpdu = false;
1e6d2d09
VD
2499 assign(prt->edgeDelayWhile, prt->bridge->Migrate_Time);
2500
2501 /* No need to run, no one condition will be met
2502 PRSM_run(prt); */
2503}
2504
2505static void PRSM_run(port_t *prt)
2506{
2507 per_tree_port_t *ptp;
2508 bool rcvdAnyMsg;
2509
2510 if((prt->rcvdBpdu || (prt->edgeDelayWhile != prt->bridge->Migrate_Time))
2511 && !prt->portEnabled)
2512 {
2513 PRSM_to_DISCARD(prt);
2514 return;
2515 }
2516
2517 switch(prt->PRSM_state)
2518 {
2519 case PRSM_DISCARD:
2520 if(prt->rcvdBpdu && prt->portEnabled)
2521 PRSM_to_RECEIVE(prt);
2522 return;
2523 case PRSM_RECEIVE:
2524 rcvdAnyMsg = false;
2525 FOREACH_PTP_IN_PORT(ptp, prt)
2526 {
2527 if(ptp->rcvdMsg)
2528 {
2529 rcvdAnyMsg = true;
2530 break;
2531 }
2532 }
2533 if(prt->rcvdBpdu && prt->portEnabled && !rcvdAnyMsg)
2534 PRSM_to_RECEIVE(prt);
2535 return;
2536 }
2537}
2538
2539/* 13.29 Port Protocol Migration state machine */
2540
2541static void PPMSM_run(port_t *prt);
2542#define PPMSM_begin(prt) PPMSM_to_CHECKING_RSTP(prt)
2543
2544static void PPMSM_to_CHECKING_RSTP(port_t *prt/*, bool begin*/)
2545{
2546 prt->PPMSM_state = PPMSM_CHECKING_RSTP;
2547
2548 bridge_t *br = prt->bridge;
5453a511 2549 prt->mcheck = false;
1e6d2d09
VD
2550 prt->sendRSTP = rstpVersion(br);
2551 assign(prt->mdelayWhile, br->Migrate_Time);
2552
2553 /* No need to run, no one condition will be met
2554 * if(!begin)
2555 * PPMSM_run(prt); */
2556}
2557
2558static void PPMSM_to_SELECTING_STP(port_t *prt)
2559{
2560 prt->PPMSM_state = PPMSM_SELECTING_STP;
2561
5453a511 2562 prt->sendRSTP = false;
1e6d2d09
VD
2563 assign(prt->mdelayWhile, prt->bridge->Migrate_Time);
2564
2565 PPMSM_run(prt);
2566}
2567
2568static void PPMSM_to_SENSING(port_t *prt)
2569{
2570 prt->PPMSM_state = PPMSM_SENSING;
2571
5453a511
VD
2572 prt->rcvdRSTP = false;
2573 prt->rcvdSTP = false;
1e6d2d09
VD
2574
2575 PPMSM_run(prt);
2576}
2577
2578static void PPMSM_run(port_t *prt)
2579{
2580 bridge_t *br = prt->bridge;
2581
2582 switch(prt->PPMSM_state)
2583 {
2584 case PPMSM_CHECKING_RSTP:
2585 if((prt->mdelayWhile != br->Migrate_Time)
2586 && !prt->portEnabled)
2587 {
2588 PPMSM_to_CHECKING_RSTP(prt);
2589 return;
2590 }
2591 if(0 == prt->mdelayWhile)
2592 PPMSM_to_SENSING(prt);
2593 return;
2594 case PPMSM_SELECTING_STP:
2595 if(0 == prt->mdelayWhile || !prt->portEnabled || prt->mcheck)
2596 PPMSM_to_SENSING(prt);
2597 return;
2598 case PPMSM_SENSING:
2599 if(!prt->portEnabled || prt->mcheck
2600 || (rstpVersion(br) && !prt->sendRSTP && prt->rcvdRSTP))
2601 {
2602 PPMSM_to_CHECKING_RSTP(prt);
2603 return;
2604 }
2605 if(prt->sendRSTP && prt->rcvdSTP)
2606 PPMSM_to_SELECTING_STP(prt);
2607 return;
2608 }
2609}
2610
2611/* 13.30 Bridge Detection state machine */
2612static void BDSM_to_EDGE(port_t *prt/*, bool begin*/)
2613{
2614 prt->BDSM_state = BDSM_EDGE;
2615
5453a511 2616 prt->operEdge = true;
1e6d2d09
VD
2617
2618 /* No need to run, no one condition will be met
2619 * if(!begin)
2620 * BDSM_run(prt); */
2621}
2622
2623static void BDSM_to_NOT_EDGE(port_t *prt/*, bool begin*/)
2624{
2625 prt->BDSM_state = BDSM_NOT_EDGE;
2626
5453a511 2627 prt->operEdge = false;
1e6d2d09
VD
2628
2629 /* No need to run, no one condition will be met
2630 * if(!begin)
2631 * BDSM_run(prt); */
2632}
2633
2634static void BDSM_begin(port_t *prt/*, bool begin*/)
2635{
2636 if(prt->AdminEdgePort)
2637 BDSM_to_EDGE(prt/*, begin*/);
2638 else
2639 BDSM_to_NOT_EDGE(prt/*, begin*/);
2640}
2641
2642static void BDSM_run(port_t *prt)
2643{
2644 per_tree_port_t *cist;
2645
2646 switch(prt->BDSM_state)
2647 {
2648 case BDSM_EDGE:
2649 if((!prt->portEnabled && !prt->AdminEdgePort) || !prt->operEdge)
2650 BDSM_to_NOT_EDGE(prt);
2651 return;
2652 case BDSM_NOT_EDGE:
2653 cist = GET_CIST_PTP_FROM_PORT(prt);
2654 /* NOTE: 802.1Q-2005 is not clear, which of the per-tree
2655 * "proposing" flags to use here, or one should combine
2656 * them all for all trees? Maybe 802.1Q-2011 clarifies
2657 * this, but I don't have the text.
2658 * So, I decide that it will be the "proposing" flag
2659 * from CIST tree - it seems like a good bet.
2660 */
2661 if((!prt->portEnabled && prt->AdminEdgePort)
2662 || ((0 == prt->edgeDelayWhile) && prt->AutoEdge && prt->sendRSTP
2663 && cist->proposing)
2664 )
2665 BDSM_to_EDGE(prt);
2666 return;
2667 }
2668}
2669
2670/* 13.31 Port Transmit state machine */
2671
2672static void PTSM_run(port_t *prt);
2673#define PTSM_begin(prt) PTSM_to_TRANSMIT_INIT((prt), true)
2674
2675static void PTSM_to_TRANSMIT_INIT(port_t *prt, bool begin)
2676{
2677 prt->PTSM_state = PTSM_TRANSMIT_INIT;
2678
5453a511
VD
2679 prt->newInfo = true;
2680 prt->newInfoMsti = true;
1e6d2d09
VD
2681 assign(prt->txCount, 0u);
2682
2683 if(!begin && prt->portEnabled) /* prevent infinite loop */
2684 PTSM_run(prt);
2685}
2686
2687static void PTSM_to_TRANSMIT_CONFIG(port_t *prt)
2688{
2689 prt->PTSM_state = PTSM_TRANSMIT_CONFIG;
2690
5453a511 2691 prt->newInfo = false;
1e6d2d09
VD
2692 txConfig(prt);
2693 ++(prt->txCount);
5453a511 2694 prt->tcAck = false;
1e6d2d09
VD
2695
2696 PTSM_run(prt);
2697}
2698
2699static void PTSM_to_TRANSMIT_TCN(port_t *prt)
2700{
2701 prt->PTSM_state = PTSM_TRANSMIT_TCN;
2702
5453a511 2703 prt->newInfo = false;
1e6d2d09
VD
2704 txTcn(prt);
2705 ++(prt->txCount);
2706
2707 PTSM_run(prt);
2708}
2709
2710static void PTSM_to_TRANSMIT_RSTP(port_t *prt)
2711{
2712 prt->PTSM_state = PTSM_TRANSMIT_RSTP;
2713
5453a511
VD
2714 prt->newInfo = false;
2715 prt->newInfoMsti = false;
1e6d2d09
VD
2716 txMstp(prt);
2717 ++(prt->txCount);
5453a511 2718 prt->tcAck = false;
1e6d2d09
VD
2719
2720 PTSM_run(prt);
2721}
2722
2723static void PTSM_to_TRANSMIT_PERIODIC(port_t *prt)
2724{
2725 prt->PTSM_state = PTSM_TRANSMIT_PERIODIC;
2726
2727 per_tree_port_t *ptp = GET_CIST_PTP_FROM_PORT(prt);
2728 bool cistDesignatedOrTCpropagatingRootPort =
2729 (roleDesignated == ptp->role)
2730 || ((roleRoot == ptp->role) && (0 != ptp->tcWhile));
2731 bool mstiDesignatedOrTCpropagatingRootPort;
2732
2733 mstiDesignatedOrTCpropagatingRootPort = false;
2734 list_for_each_entry_continue(ptp, &prt->trees, port_list)
2735 {
2736 if((roleDesignated == ptp->role)
2737 || ((roleRoot == ptp->role) && (0 != ptp->tcWhile))
2738 )
2739 {
2740 mstiDesignatedOrTCpropagatingRootPort = true;
2741 break;
2742 }
2743 }
2744
2745 prt->newInfo = prt->newInfo || cistDesignatedOrTCpropagatingRootPort;
2746 prt->newInfoMsti = prt->newInfoMsti
2747 || mstiDesignatedOrTCpropagatingRootPort;
2748
2749 PTSM_run(prt);
2750}
2751
2752static void PTSM_to_IDLE(port_t *prt)
2753{
2754 prt->PTSM_state = PTSM_IDLE;
2755
2756 per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
58665938 2757 prt->helloWhen = cist->portTimes.Hello_Time;
1e6d2d09
VD
2758
2759 PTSM_run(prt);
2760}
2761
2762static void PTSM_run(port_t *prt)
2763{
2764 /* bool allTransmitReady; */
2765 per_tree_port_t *ptp;
2766 port_role_t cistRole;
2767 bool mstiMasterPort;
2768
2769 if(!prt->portEnabled)
2770 {
2771 PTSM_to_TRANSMIT_INIT(prt, false);
2772 return;
2773 }
2774
2775 switch(prt->PTSM_state)
2776 {
2777 case PTSM_TRANSMIT_INIT:
2778 /* return; */
2779 case PTSM_TRANSMIT_CONFIG:
2780 /* return; */
2781 case PTSM_TRANSMIT_TCN:
2782 /* return; */
2783 case PTSM_TRANSMIT_RSTP:
2784 /* return; */
2785 case PTSM_TRANSMIT_PERIODIC:
2786 PTSM_to_IDLE(prt); /* UnConditional Transition */
2787 return;
2788 case PTSM_IDLE:
2789 /* allTransmitReady = true; */
2790 ptp = GET_CIST_PTP_FROM_PORT(prt);
2791 if(!ptp->selected || ptp->updtInfo)
2792 {
2793 /* allTransmitReady = false; */
2794 return;
2795 }
5453a511 2796 cistRole = ptp->role;
1e6d2d09
VD
2797 mstiMasterPort = false;
2798 list_for_each_entry_continue(ptp, &prt->trees, port_list)
2799 {
2800 if(!ptp->selected || ptp->updtInfo)
2801 {
2802 /* allTransmitReady = false; */
2803 return;
2804 }
2805 if(roleMaster == ptp->role)
2806 mstiMasterPort = true;
2807 }
2808 if(0 == prt->helloWhen)
2809 {
2810 PTSM_to_TRANSMIT_PERIODIC(prt);
2811 return;
2812 }
2813 if(!(prt->txCount < prt->bridge->Transmit_Hold_Count))
2814 return;
2815 if(prt->sendRSTP)
2816 { /* implement MSTP */
2817 if(prt->newInfo || (prt->newInfoMsti && !mstiMasterPort))
2818 {
2819 PTSM_to_TRANSMIT_RSTP(prt);
2820 return;
2821 }
2822 }
2823 else
2824 { /* fallback to STP */
2825 if(prt->newInfo && (roleDesignated == cistRole))
2826 {
2827 PTSM_to_TRANSMIT_CONFIG(prt);
2828 return;
2829 }
2830 if(prt->newInfo && (roleRoot == cistRole))
2831 {
2832 PTSM_to_TRANSMIT_TCN(prt);
2833 return;
2834 }
2835 }
2836 return;
2837 }
2838}
2839
2840/* 13.32 Port Information state machine */
2841
2842#ifdef PISM_ENABLE_LOG
2843#define PISM_LOG(_fmt, _args...) SMLOG_MSTINAME(ptp, _fmt, ##_args)
2844#else
2845#define PISM_LOG(_fmt, _args...) {}
2846#endif /* PISM_ENABLE_LOG */
2847
2848static void PISM_run(per_tree_port_t *ptp);
2849#define PISM_begin(ptp) PISM_to_DISABLED((ptp), true)
2850
2851static void PISM_to_DISABLED(per_tree_port_t *ptp, bool begin)
2852{
2853 PISM_LOG("");
2854 ptp->PISM_state = PISM_DISABLED;
2855
5453a511
VD
2856 ptp->rcvdMsg = false;
2857 ptp->proposing = false;
2858 ptp->proposed = false;
2859 ptp->agree = false;
2860 ptp->agreed = false;
1e6d2d09 2861 assign(ptp->rcvdInfoWhile, 0u);
5453a511
VD
2862 ptp->infoIs = ioDisabled;
2863 ptp->reselect = true;
2864 ptp->selected = false;
1e6d2d09
VD
2865
2866 if(!begin)
2867 PISM_run(ptp);
2868}
2869
2870static void PISM_to_AGED(per_tree_port_t *ptp)
2871{
2872 PISM_LOG("");
2873 ptp->PISM_state = PISM_AGED;
2874
5453a511
VD
2875 ptp->infoIs = ioAged;
2876 ptp->reselect = true;
2877 ptp->selected = false;
1e6d2d09
VD
2878
2879 PISM_run(ptp);
2880}
2881
2882static void PISM_to_UPDATE(per_tree_port_t *ptp)
2883{
2884 PISM_LOG("");
2885 ptp->PISM_state = PISM_UPDATE;
2886
5453a511
VD
2887 ptp->proposing = false;
2888 ptp->proposed = false;
1e6d2d09
VD
2889 ptp->agreed = ptp->agreed && betterorsameInfo(ptp, ioMine);
2890 ptp->synced = ptp->synced && ptp->agreed;
2891 assign(ptp->portPriority, ptp->designatedPriority);
2892 assign(ptp->portTimes, ptp->designatedTimes);
5453a511
VD
2893 ptp->updtInfo = false;
2894 ptp->infoIs = ioMine;
1e6d2d09
VD
2895 /* newInfoXst = TRUE; */
2896 port_t *prt = ptp->port;
2897 if(0 == ptp->MSTID)
5453a511 2898 prt->newInfo = true;
1e6d2d09 2899 else
5453a511 2900 prt->newInfoMsti = true;
1e6d2d09
VD
2901
2902 PISM_run(ptp);
2903}
2904
2905static void PISM_to_SUPERIOR_DESIGNATED(per_tree_port_t *ptp)
2906{
2907 PISM_LOG("");
2908 ptp->PISM_state = PISM_SUPERIOR_DESIGNATED;
2909
2910 port_t *prt = ptp->port;
2911
5453a511
VD
2912 prt->infoInternal = prt->rcvdInternal;
2913 ptp->agreed = false;
2914 ptp->proposing = false;
1e6d2d09
VD
2915 recordProposal(ptp);
2916 setTcFlags(ptp);
2917 ptp->agree = ptp->agree && betterorsameInfo(ptp, ioReceived);
2918 recordAgreement(ptp);
2919 ptp->synced = ptp->synced && ptp->agreed;
2920 recordPriority(ptp);
2921 recordTimes(ptp);
2922 updtRcvdInfoWhile(ptp);
5453a511
VD
2923 ptp->infoIs = ioReceived;
2924 ptp->reselect = true;
2925 ptp->selected = false;
2926 ptp->rcvdMsg = false;
1e6d2d09
VD
2927
2928 PISM_run(ptp);
2929}
2930
2931static void PISM_to_REPEATED_DESIGNATED(per_tree_port_t *ptp)
2932{
2933 PISM_LOG("");
2934 ptp->PISM_state = PISM_REPEATED_DESIGNATED;
2935
2936 port_t *prt = ptp->port;
2937
5453a511 2938 prt->infoInternal = prt->rcvdInternal;
1e6d2d09
VD
2939 recordProposal(ptp);
2940 setTcFlags(ptp);
2941 recordAgreement(ptp);
2942 updtRcvdInfoWhile(ptp);
5453a511 2943 ptp->rcvdMsg = false;
1e6d2d09
VD
2944
2945 PISM_run(ptp);
2946}
2947
2948static void PISM_to_INFERIOR_DESIGNATED(per_tree_port_t *ptp)
2949{
2950 PISM_LOG("");
2951 ptp->PISM_state = PISM_INFERIOR_DESIGNATED;
2952
2953 recordDispute(ptp);
5453a511 2954 ptp->rcvdMsg = false;
1e6d2d09
VD
2955
2956 PISM_run(ptp);
2957}
2958
2959static void PISM_to_NOT_DESIGNATED(per_tree_port_t *ptp)
2960{
2961 PISM_LOG("");
2962 ptp->PISM_state = PISM_NOT_DESIGNATED;
2963
2964 recordAgreement(ptp);
2965 setTcFlags(ptp);
5453a511 2966 ptp->rcvdMsg = false;
1e6d2d09
VD
2967
2968 PISM_run(ptp);
2969}
2970
2971static void PISM_to_OTHER(per_tree_port_t *ptp)
2972{
2973 PISM_LOG("");
2974 ptp->PISM_state = PISM_OTHER;
2975
5453a511 2976 ptp->rcvdMsg = false;
1e6d2d09
VD
2977
2978 PISM_run(ptp);
2979}
2980
2981static void PISM_to_CURRENT(per_tree_port_t *ptp)
2982{
2983 PISM_LOG("");
2984 ptp->PISM_state = PISM_CURRENT;
2985
2986 PISM_run(ptp);
2987}
2988
2989static void PISM_to_RECEIVE(per_tree_port_t *ptp)
2990{
2991 PISM_LOG("");
2992 ptp->PISM_state = PISM_RECEIVE;
2993
2994 ptp->rcvdInfo = rcvInfo(ptp);
2995 recordMastered(ptp);
2996
2997 PISM_run(ptp);
2998}
2999
3000static void PISM_run(per_tree_port_t *ptp)
3001{
3002 bool rcvdXstMsg, updtXstInfo;
3003 port_t *prt = ptp->port;
3004
3005 if((!prt->portEnabled) && (ioDisabled != ptp->infoIs))
3006 {
3007 PISM_to_DISABLED(ptp, false);
3008 return;
3009 }
3010
3011 switch(ptp->PISM_state)
3012 {
3013 case PISM_DISABLED:
3014 if(prt->portEnabled)
3015 {
3016 PISM_to_AGED(ptp);
3017 return;
3018 }
3019 if(ptp->rcvdMsg)
3020 PISM_to_DISABLED(ptp, false);
3021 return;
3022 case PISM_AGED:
3023 if(ptp->selected && ptp->updtInfo)
3024 PISM_to_UPDATE(ptp);
3025 return;
3026 case PISM_UPDATE:
3027 /* return; */
3028 case PISM_SUPERIOR_DESIGNATED:
3029 /* return; */
3030 case PISM_REPEATED_DESIGNATED:
3031 /* return; */
3032 case PISM_INFERIOR_DESIGNATED:
3033 /* return; */
3034 case PISM_NOT_DESIGNATED:
3035 /* return; */
3036 case PISM_OTHER:
3037 PISM_to_CURRENT(ptp);
3038 return;
3039 case PISM_CURRENT:
3040 /*
3041 * Although 802.1Q-2005 does not define rcvdXstMsg and updtXstInfo
3042 * from 802.1s we can conclude that they are:
3043 * - rcvdXstMsg = rcvdCistMsg, if tree is CIST
3044 * rcvdMstiMsg, if tree is MSTI.
3045 * - updtXstInfo = updtCistInfo, if tree is CIST
3046 * updtMstiInfo, if tree is MSTI.
3047 */
3048 if(0 == ptp->MSTID)
3049 { /* CIST */
3050 rcvdXstMsg = ptp->rcvdMsg; /* 13.25.12 */
3051 updtXstInfo = ptp->updtInfo; /* 13.25.16 */
3052 }
3053 else
3054 { /* MSTI */
3055 per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
3056 rcvdXstMsg = !cist->rcvdMsg && ptp->rcvdMsg; /* 13.25.13 */
3057 updtXstInfo = ptp->updtInfo || cist->updtInfo; /* 13.25.17 */
3058 }
3059 if(rcvdXstMsg && !updtXstInfo)
3060 {
3061 PISM_to_RECEIVE(ptp);
3062 return;
3063 }
3064 if((ioReceived == ptp->infoIs) && (0 == ptp->rcvdInfoWhile)
3065 && !ptp->updtInfo && !rcvdXstMsg)
3066 {
3067 PISM_to_AGED(ptp);
3068 return;
3069 }
3070 if(ptp->selected && ptp->updtInfo)
3071 PISM_to_UPDATE(ptp);
3072 return;
3073 case PISM_RECEIVE:
3074 switch(ptp->rcvdInfo)
3075 {
3076 case SuperiorDesignatedInfo:
3077 PISM_to_SUPERIOR_DESIGNATED(ptp);
3078 return;
3079 case RepeatedDesignatedInfo:
3080 PISM_to_REPEATED_DESIGNATED(ptp);
3081 return;
3082 case InferiorDesignatedInfo:
3083 PISM_to_INFERIOR_DESIGNATED(ptp);
3084 return;
3085 case InferiorRootAlternateInfo:
3086 PISM_to_NOT_DESIGNATED(ptp);
3087 return;
3088 case OtherInfo:
3089 PISM_to_OTHER(ptp);
3090 return;
3091 }
3092 return;
3093 }
3094}
3095
3096/* 13.33 Port Role Selection state machine */
3097
3098static void PRSSM_run(tree_t *tree);
3099#define PRSSM_begin(tree) PRSSM_to_INIT_TREE(tree)
3100
3101static void PRSSM_to_INIT_TREE(tree_t *tree/*, bool begin*/)
3102{
3103 tree->PRSSM_state = PRSSM_INIT_TREE;
3104
3105 updtRolesDisabledTree(tree);
3106
3107 /* No need to check, as we assume begin = true here
3108 * because transition to this state can be initiated only by BEGIN var.
3109 * In other words, this function is called via xxx_begin macro only.
3110 * if(!begin)
3111 * PRSSM_run(prt); */
3112}
3113
3114static void PRSSM_to_ROLE_SELECTION(tree_t *tree)
3115{
3116 tree->PRSSM_state = PRSSM_ROLE_SELECTION;
3117
3118 clearReselectTree(tree);
3119 updtRolesTree(tree);
3120 setSelectedTree(tree);
3121
3122 /* No need to run, no one condition will be met
3123 PRSSM_run(tree); */
3124}
3125
3126static void PRSSM_run(tree_t *tree)
3127{
3128 per_tree_port_t *ptp;
3129
3130 switch(tree->PRSSM_state)
3131 {
3132 case PRSSM_INIT_TREE:
3133 PRSSM_to_ROLE_SELECTION(tree);
3134 return;
3135 case PRSSM_ROLE_SELECTION:
3136 FOREACH_PTP_IN_TREE(ptp, tree)
3137 if(ptp->reselect)
3138 {
3139 PRSSM_to_ROLE_SELECTION(tree);
3140 return;
3141 }
3142 return;
3143 }
3144}
3145
3146/* 13.34 Port Role Transitions state machine */
3147
3148#ifdef PRTSM_ENABLE_LOG
3149#define PRTSM_LOG(_fmt, _args...) SMLOG_MSTINAME(ptp, _fmt, ##_args)
3150#else
3151#define PRTSM_LOG(_fmt, _args...) {}
3152#endif /* PRTSM_ENABLE_LOG */
3153
3154static void PRTSM_runr(per_tree_port_t *ptp, bool recursive_call);
3155#define PRTSM_run(ptp) PRTSM_runr((ptp), false)
3156#define PRTSM_begin(ptp) PRTSM_to_INIT_PORT(ptp)
3157
3158 /* Disabled Port role transitions */
3159
3160static void PRTSM_to_INIT_PORT(per_tree_port_t *ptp/*, bool begin*/)
3161{
3162 PRTSM_LOG("");
3163 ptp->PRTSM_state = PRTSM_INIT_PORT;
3164
3165 unsigned int MaxAge, FwdDelay;
3166 per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(ptp->port);
3167
5453a511
VD
3168 ptp->role = roleDisabled;
3169 ptp->learn = false;
3170 ptp->forward = false;
3171 ptp->synced = false;
3172 ptp->sync = true;
3173 ptp->reRoot = true;
1e6d2d09 3174 /* 13.25.6 */
58665938 3175 FwdDelay = cist->designatedTimes.Forward_Delay;
1e6d2d09
VD
3176 assign(ptp->rrWhile, FwdDelay);
3177 /* 13.25.8 */
58665938 3178 MaxAge = cist->designatedTimes.Max_Age;
1e6d2d09
VD
3179 assign(ptp->fdWhile, MaxAge);
3180 assign(ptp->rbWhile, 0u);
3181
3182 /* No need to check, as we assume begin = true here
3183 * because transition to this state can be initiated only by BEGIN var.
3184 * In other words, this function is called via xxx_begin macro only.
3185 * if(!begin)
3186 * PRTSM_runr(ptp, false); */
3187}
3188
3189static void PRTSM_to_DISABLE_PORT(per_tree_port_t *ptp)
3190{
3191 PRTSM_LOG("");
3192 ptp->PRTSM_state = PRTSM_DISABLE_PORT;
3193
3194 /* Although 802.1Q-2005 says here to do role = selectedRole
3195 * I have difficulties with it in the next scenario:
3196 * 1) port was designated (role == selectedRole == roleDesignated)
3197 * 2) some config event occurs, e.g. MAC address changes and
3198 * br_state_machines_begin is called
3199 * 3) role == selectedRole == roleDisabled, PRTSM_state = PRTSM_INIT_PORT
3200 * Because port was not actually down, on the next run
3201 * Port Role Selection state machine sets selectedRole = roleDesignated
3202 * and updtInfo = true:
3203 * 4) we have unconditional transition to DISABLE_PORT, and because
3204 * updtInfo = true we can not follow transition to DESIGNATED_PORT
3205 * 5) if we follow standard, role = selectedRole = roleDesignated and
3206 * on the next run we have transition to the DISABLED_PORT
3207 * And there we stuck. role == selectedRole, so we can not transit to
3208 * DESIGNATED_PORT (it requires role != selectedRole ).
3209 *
3210 * Solution: do not follow the standard, and do role = roleDisabled
3211 * instead of role = selectedRole.
3212 */
5453a511
VD
3213 ptp->role = roleDisabled;
3214 ptp->learn = false;
3215 ptp->forward = false;
1e6d2d09
VD
3216
3217 PRTSM_runr(ptp, true);
3218}
3219
3220static void PRTSM_to_DISABLED_PORT(per_tree_port_t *ptp, unsigned int MaxAge)
3221{
3222 PRTSM_LOG("");
3223 ptp->PRTSM_state = PRTSM_DISABLED_PORT;
3224
3225 assign(ptp->fdWhile, MaxAge);
5453a511 3226 ptp->synced = true;
1e6d2d09 3227 assign(ptp->rrWhile, 0u);
5453a511
VD
3228 ptp->sync = false;
3229 ptp->reRoot = false;
1e6d2d09
VD
3230
3231 PRTSM_runr(ptp, true);
3232}
3233
3234 /* MasterPort role transitions */
3235
3236static void PRTSM_to_MASTER_PROPOSED(per_tree_port_t *ptp)
3237{
3238 PRTSM_LOG("");
3239 ptp->PRTSM_state = PRTSM_MASTER_PROPOSED;
3240
3241 setSyncTree(ptp->tree);
5453a511 3242 ptp->proposed = false;
1e6d2d09
VD
3243
3244 PRTSM_runr(ptp, true);
3245}
3246
3247static void PRTSM_to_MASTER_AGREED(per_tree_port_t *ptp)
3248{
3249 PRTSM_LOG("");
3250 ptp->PRTSM_state = PRTSM_MASTER_AGREED;
3251
5453a511
VD
3252 ptp->proposed = false;
3253 ptp->sync = false;
3254 ptp->agree = true;
1e6d2d09
VD
3255
3256 PRTSM_runr(ptp, true);
3257}
3258
3259static void PRTSM_to_MASTER_SYNCED(per_tree_port_t *ptp)
3260{
3261 PRTSM_LOG("");
3262 ptp->PRTSM_state = PRTSM_MASTER_SYNCED;
3263
3264 assign(ptp->rrWhile, 0u);
5453a511
VD
3265 ptp->synced = true;
3266 ptp->sync = false;
1e6d2d09
VD
3267
3268 PRTSM_runr(ptp, true);
3269}
3270
3271static void PRTSM_to_MASTER_RETIRED(per_tree_port_t *ptp)
3272{
3273 PRTSM_LOG("");
3274 ptp->PRTSM_state = PRTSM_MASTER_RETIRED;
3275
5453a511 3276 ptp->reRoot = false;
1e6d2d09
VD
3277
3278 PRTSM_runr(ptp, true);
3279}
3280
3281static void PRTSM_to_MASTER_FORWARD(per_tree_port_t *ptp)
3282{
3283 PRTSM_LOG("");
3284 ptp->PRTSM_state = PRTSM_MASTER_FORWARD;
3285
5453a511 3286 ptp->forward = true;
1e6d2d09 3287 assign(ptp->fdWhile, 0u);
5453a511 3288 ptp->agreed = ptp->port->sendRSTP;
1e6d2d09
VD
3289
3290 PRTSM_runr(ptp, true);
3291}
3292
3293static void PRTSM_to_MASTER_LEARN(per_tree_port_t *ptp, unsigned int forwardDelay)
3294{
3295 PRTSM_LOG("");
3296 ptp->PRTSM_state = PRTSM_MASTER_LEARN;
3297
5453a511 3298 ptp->learn = true;
1e6d2d09
VD
3299 assign(ptp->fdWhile, forwardDelay);
3300
3301 PRTSM_runr(ptp, true);
3302}
3303
3304static void PRTSM_to_MASTER_DISCARD(per_tree_port_t *ptp, unsigned int forwardDelay)
3305{
3306 PRTSM_LOG("");
3307 ptp->PRTSM_state = PRTSM_MASTER_DISCARD;
3308
5453a511
VD
3309 ptp->learn = false;
3310 ptp->forward = false;
3311 ptp->disputed = false;
1e6d2d09
VD
3312 assign(ptp->fdWhile, forwardDelay);
3313
3314 PRTSM_runr(ptp, true);
3315}
3316
3317static void PRTSM_to_MASTER_PORT(per_tree_port_t *ptp)
3318{
3319 PRTSM_LOG("");
3320 ptp->PRTSM_state = PRTSM_MASTER_PORT;
3321
5453a511 3322 ptp->role = roleMaster;
1e6d2d09
VD
3323
3324 PRTSM_runr(ptp, true);
3325}
3326
3327 /* RootPort role transitions */
3328
3329static void PRTSM_to_ROOT_PROPOSED(per_tree_port_t *ptp)
3330{
3331 PRTSM_LOG("");
3332 ptp->PRTSM_state = PRTSM_ROOT_PROPOSED;
3333
3334 setSyncTree(ptp->tree);
5453a511 3335 ptp->proposed = false;
1e6d2d09
VD
3336
3337 PRTSM_runr(ptp, true);
3338}
3339
3340static void PRTSM_to_ROOT_AGREED(per_tree_port_t *ptp)
3341{
3342 PRTSM_LOG("");
3343 ptp->PRTSM_state = PRTSM_ROOT_AGREED;
3344
5453a511
VD
3345 ptp->proposed = false;
3346 ptp->sync = false;
3347 ptp->agree = true;
1e6d2d09
VD
3348 /* newInfoXst = TRUE; */
3349 port_t *prt = ptp->port;
3350 if(0 == ptp->MSTID)
5453a511 3351 prt->newInfo = true;
1e6d2d09 3352 else
5453a511 3353 prt->newInfoMsti = true;
1e6d2d09
VD
3354
3355 PRTSM_runr(ptp, true);
3356}
3357
3358static void PRTSM_to_ROOT_SYNCED(per_tree_port_t *ptp)
3359{
3360 PRTSM_LOG("");
3361 ptp->PRTSM_state = PRTSM_ROOT_SYNCED;
3362
5453a511
VD
3363 ptp->synced = true;
3364 ptp->sync = false;
1e6d2d09
VD
3365
3366 PRTSM_runr(ptp, true);
3367}
3368
3369static void PRTSM_to_REROOT(per_tree_port_t *ptp)
3370{
3371 PRTSM_LOG("");
3372 ptp->PRTSM_state = PRTSM_REROOT;
3373
3374 setReRootTree(ptp->tree);
3375
3376 PRTSM_runr(ptp, true);
3377}
3378
3379static void PRTSM_to_ROOT_FORWARD(per_tree_port_t *ptp)
3380{
3381 PRTSM_LOG("");
3382 ptp->PRTSM_state = PRTSM_ROOT_FORWARD;
3383
3384 assign(ptp->fdWhile, 0u);
5453a511 3385 ptp->forward = true;
1e6d2d09
VD
3386
3387 PRTSM_runr(ptp, true);
3388}
3389
3390static void PRTSM_to_ROOT_LEARN(per_tree_port_t *ptp, unsigned int forwardDelay)
3391{
3392 PRTSM_LOG("");
3393 ptp->PRTSM_state = PRTSM_ROOT_LEARN;
3394
3395 assign(ptp->fdWhile, forwardDelay);
5453a511 3396 ptp->learn = true;
1e6d2d09
VD
3397
3398 PRTSM_runr(ptp, true);
3399}
3400
3401static void PRTSM_to_REROOTED(per_tree_port_t *ptp)
3402{
3403 PRTSM_LOG("");
3404 ptp->PRTSM_state = PRTSM_REROOTED;
3405
5453a511 3406 ptp->reRoot = false;
1e6d2d09
VD
3407
3408 PRTSM_runr(ptp, true);
3409}
3410
3411static void PRTSM_to_ROOT_PORT(per_tree_port_t *ptp, unsigned int FwdDelay)
3412{
3413 PRTSM_LOG("");
3414 ptp->PRTSM_state = PRTSM_ROOT_PORT;
3415
5453a511 3416 ptp->role = roleRoot;
1e6d2d09
VD
3417 assign(ptp->rrWhile, FwdDelay);
3418
3419 PRTSM_runr(ptp, true);
3420}
3421
3422 /* DesignatedPort role transitions */
3423
3424static void PRTSM_to_DESIGNATED_PROPOSE(per_tree_port_t *ptp)
3425{
3426 PRTSM_LOG("");
3427 ptp->PRTSM_state = PRTSM_DESIGNATED_PROPOSE;
3428
3429 port_t *prt = ptp->port;
3430
5453a511 3431 ptp->proposing = true;
1e6d2d09
VD
3432 /* newInfoXst = TRUE; */
3433 if(0 == ptp->MSTID)
3434 { /* CIST */
3435 /* 13.25.8. This tree is CIST. */
58665938 3436 unsigned int MaxAge = ptp->designatedTimes.Max_Age;
1e6d2d09
VD
3437 /* 13.25.c) -> 17.20.4 of 802.1D : EdgeDelay */
3438 unsigned int EdgeDelay = prt->operPointToPointMAC ?
3439 prt->bridge->Migrate_Time
3440 : MaxAge;
3441 assign(prt->edgeDelayWhile, EdgeDelay);
5453a511 3442 prt->newInfo = true;
1e6d2d09
VD
3443 }
3444 else
5453a511 3445 prt->newInfoMsti = true;
1e6d2d09
VD
3446
3447 PRTSM_runr(ptp, true);
3448}
3449
3450static void PRTSM_to_DESIGNATED_AGREED(per_tree_port_t *ptp)
3451{
3452 PRTSM_LOG("");
3453 ptp->PRTSM_state = PRTSM_DESIGNATED_AGREED;
3454
5453a511
VD
3455 ptp->proposed = false;
3456 ptp->sync = false;
3457 ptp->agree = true;
1e6d2d09
VD
3458 /* newInfoXst = TRUE; */
3459 port_t *prt = ptp->port;
3460 if(0 == ptp->MSTID)
5453a511 3461 prt->newInfo = true;
1e6d2d09 3462 else
5453a511 3463 prt->newInfoMsti = true;
1e6d2d09
VD
3464
3465 PRTSM_runr(ptp, true);
3466}
3467
3468static void PRTSM_to_DESIGNATED_SYNCED(per_tree_port_t *ptp)
3469{
3470 PRTSM_LOG("");
3471 ptp->PRTSM_state = PRTSM_DESIGNATED_SYNCED;
3472
3473 assign(ptp->rrWhile, 0u);
5453a511
VD
3474 ptp->synced = true;
3475 ptp->sync = false;
1e6d2d09
VD
3476
3477 PRTSM_runr(ptp, true);
3478}
3479
3480static void PRTSM_to_DESIGNATED_RETIRED(per_tree_port_t *ptp)
3481{
3482 PRTSM_LOG("");
3483 ptp->PRTSM_state = PRTSM_DESIGNATED_RETIRED;
3484
5453a511 3485 ptp->reRoot = false;
1e6d2d09
VD
3486
3487 PRTSM_runr(ptp, true);
3488}
3489
3490static void PRTSM_to_DESIGNATED_FORWARD(per_tree_port_t *ptp)
3491{
3492 PRTSM_LOG("");
3493 ptp->PRTSM_state = PRTSM_DESIGNATED_FORWARD;
3494
5453a511 3495 ptp->forward = true;
1e6d2d09 3496 assign(ptp->fdWhile, 0u);
5453a511 3497 ptp->agreed = ptp->port->sendRSTP;
1e6d2d09
VD
3498
3499 PRTSM_runr(ptp, true);
3500}
3501
3502static void PRTSM_to_DESIGNATED_LEARN(per_tree_port_t *ptp, unsigned int forwardDelay)
3503{
3504 PRTSM_LOG("");
3505 ptp->PRTSM_state = PRTSM_DESIGNATED_LEARN;
3506
5453a511 3507 ptp->learn = true;
1e6d2d09
VD
3508 assign(ptp->fdWhile, forwardDelay);
3509
3510 PRTSM_runr(ptp, true);
3511}
3512
3513static void PRTSM_to_DESIGNATED_DISCARD(per_tree_port_t *ptp, unsigned int forwardDelay)
3514{
3515 PRTSM_LOG("");
3516 ptp->PRTSM_state = PRTSM_DESIGNATED_DISCARD;
3517
5453a511
VD
3518 ptp->learn = false;
3519 ptp->forward = false;
3520 ptp->disputed = false;
1e6d2d09
VD
3521 assign(ptp->fdWhile, forwardDelay);
3522
3523 PRTSM_runr(ptp, true);
3524}
3525
3526static void PRTSM_to_DESIGNATED_PORT(per_tree_port_t *ptp)
3527{
3528 PRTSM_LOG("");
3529 ptp->PRTSM_state = PRTSM_DESIGNATED_PORT;
3530
5453a511 3531 ptp->role = roleDesignated;
1e6d2d09
VD
3532
3533 PRTSM_runr(ptp, true);
3534}
3535
3536 /* AlternatePort and BackupPort role transitions */
3537
3538static void PRTSM_to_BLOCK_PORT(per_tree_port_t *ptp)
3539{
3540 PRTSM_LOG("");
3541 ptp->PRTSM_state = PRTSM_BLOCK_PORT;
3542
5453a511
VD
3543 ptp->role = ptp->selectedRole;
3544 ptp->learn = false;
3545 ptp->forward = false;
1e6d2d09
VD
3546
3547 PRTSM_runr(ptp, true);
3548}
3549
3550static void PRTSM_to_BACKUP_PORT(per_tree_port_t *ptp, unsigned int HelloTime)
3551{
3552 PRTSM_LOG("");
3553 ptp->PRTSM_state = PRTSM_BACKUP_PORT;
3554
3555 assign(ptp->rbWhile, 2 * HelloTime);
3556
3557 PRTSM_runr(ptp, true);
3558}
3559
3560static void PRTSM_to_ALTERNATE_PROPOSED(per_tree_port_t *ptp)
3561{
3562 PRTSM_LOG("");
3563 ptp->PRTSM_state = PRTSM_ALTERNATE_PROPOSED;
3564
3565 setSyncTree(ptp->tree);
5453a511 3566 ptp->proposed = false;
1e6d2d09
VD
3567
3568 PRTSM_runr(ptp, true);
3569}
3570
3571static void PRTSM_to_ALTERNATE_AGREED(per_tree_port_t *ptp)
3572{
3573 PRTSM_LOG("");
3574 ptp->PRTSM_state = PRTSM_ALTERNATE_AGREED;
3575
5453a511
VD
3576 ptp->proposed = false;
3577 ptp->agree = true;
1e6d2d09
VD
3578 /* newInfoXst = TRUE; */
3579 port_t *prt = ptp->port;
3580 if(0 == ptp->MSTID)
5453a511 3581 prt->newInfo = true;
1e6d2d09 3582 else
5453a511 3583 prt->newInfoMsti = true;
1e6d2d09
VD
3584
3585 PRTSM_runr(ptp, true);
3586}
3587
3588static void PRTSM_to_ALTERNATE_PORT(per_tree_port_t *ptp, unsigned int forwardDelay)
3589{
3590 PRTSM_LOG("");
3591 ptp->PRTSM_state = PRTSM_ALTERNATE_PORT;
3592
3593 assign(ptp->fdWhile, forwardDelay);
5453a511 3594 ptp->synced = true;
1e6d2d09 3595 assign(ptp->rrWhile, 0u);
5453a511
VD
3596 ptp->sync = false;
3597 ptp->reRoot = false;
1e6d2d09
VD
3598
3599 PRTSM_runr(ptp, true);
3600}
3601
3602static void PRTSM_runr(per_tree_port_t *ptp, bool recursive_call)
3603{
3604 /* Following vars do not need recalculating on recursive calls */
3605 static unsigned int MaxAge, FwdDelay, forwardDelay, HelloTime;
3606 static port_t *prt;
3607 static tree_t *tree;
3608 static per_tree_port_t *cist;
3609 /* Following vars are recalculated on each state transition */
3610 bool allSynced, reRooted;
3611 /* Following vars are auxiliary and don't depend on recursive_call */
3612 per_tree_port_t *ptp_1;
3613
3614 if(!recursive_call)
3615 { /* calculate these intermediate vars only first time in chain of
3616 * recursive calls */
3617 prt = ptp->port;
3618 tree = ptp->tree;
3619
3620 cist = GET_CIST_PTP_FROM_PORT(prt);
3621
3622 /* 13.25.6 */
58665938 3623 FwdDelay = cist->designatedTimes.Forward_Delay;
1e6d2d09
VD
3624
3625 /* 13.25.7 */
58665938 3626 HelloTime = cist->portTimes.Hello_Time;
1e6d2d09
VD
3627
3628 /* 13.25.d) -> 17.20.5 of 802.1D */
3629 forwardDelay = prt->sendRSTP ? HelloTime : FwdDelay;
3630
3631 /* 13.25.8 */
58665938 3632 MaxAge = cist->designatedTimes.Max_Age;
1e6d2d09
VD
3633 }
3634
3635 PRTSM_LOG("role = %d, selectedRole = %d, selected = %d, updtInfo = %d",
3636 ptp->role, ptp->selectedRole, ptp->selected, ptp->updtInfo);
3637 if((ptp->role != ptp->selectedRole) && ptp->selected && !ptp->updtInfo)
3638 {
3639 switch(ptp->selectedRole)
3640 {
3641 case roleDisabled:
3642 PRTSM_to_DISABLE_PORT(ptp);
3643 return;
3644 case roleMaster:
3645 PRTSM_to_MASTER_PORT(ptp);
3646 return;
3647 case roleRoot:
3648 PRTSM_to_ROOT_PORT(ptp, FwdDelay);
3649 return;
3650 case roleDesignated:
3651 PRTSM_to_DESIGNATED_PORT(ptp);
3652 return;
3653 case roleAlternate:
3654 case roleBackup:
3655 PRTSM_to_BLOCK_PORT(ptp);
3656 return;
3657 }
3658 }
3659
3660 /* 13.25.1 */
3661 allSynced = true;
3662 FOREACH_PTP_IN_TREE(ptp_1, tree)
3663 {
3664 /* a) */
3665 if(!ptp_1->selected
3666 || (ptp_1->role != ptp_1->selectedRole)
3667 || ptp_1->updtInfo
3668 )
3669 {
3670 allSynced = false;
3671 break;
3672 }
3673
3674 /* b) */
3675 switch(ptp->role)
3676 {
3677 case roleRoot:
3678 case roleAlternate:
3679 if((roleRoot != ptp_1->role) && !ptp_1->synced)
3680 allSynced = false;
3681 break;
3682 case roleDesignated:
3683 case roleMaster:
3684 if((ptp != ptp_1) && !ptp_1->synced)
3685 allSynced = false;
3686 break;
3687 default:
3688 allSynced = false;
3689 }
3690 if(!allSynced)
3691 break;
3692 }
3693
3694 switch(ptp->PRTSM_state)
3695 {
3696 /* Disabled Port role transitions */
3697 case PRTSM_INIT_PORT:
3698 PRTSM_to_DISABLE_PORT(ptp);
3699 return;
3700 case PRTSM_DISABLE_PORT:
3701 if(ptp->selected && !ptp->updtInfo
3702 && !ptp->learning && !ptp->forwarding
3703 )
3704 PRTSM_to_DISABLED_PORT(ptp, MaxAge);
3705 return;
3706 case PRTSM_DISABLED_PORT:
3707 if(ptp->selected && !ptp->updtInfo
3708 && (ptp->sync || ptp->reRoot || !ptp->synced
3709 || (ptp->fdWhile != MaxAge))
3710 )
3711 PRTSM_to_DISABLED_PORT(ptp, MaxAge);
3712 return;
3713 /* MasterPort role transitions */
3714 case PRTSM_MASTER_PROPOSED:
3715 /* return; */
3716 case PRTSM_MASTER_AGREED:
3717 /* return; */
3718 case PRTSM_MASTER_SYNCED:
3719 /* return; */
3720 case PRTSM_MASTER_RETIRED:
3721 /* return; */
3722 case PRTSM_MASTER_FORWARD:
3723 /* return; */
3724 case PRTSM_MASTER_LEARN:
3725 /* return; */
3726 case PRTSM_MASTER_DISCARD:
3727 PRTSM_to_MASTER_PORT(ptp);
3728 return;
3729 case PRTSM_MASTER_PORT:
3730 if(!(ptp->selected && !ptp->updtInfo))
3731 return;
3732 if(ptp->reRoot && (0 == ptp->rrWhile))
3733 {
3734 PRTSM_to_MASTER_RETIRED(ptp);
3735 return;
3736 }
3737 if((!ptp->learning && !ptp->forwarding && !ptp->synced)
3738 || (ptp->agreed && !ptp->synced)
3739 || (prt->operEdge && !ptp->synced)
3740 || (ptp->sync && ptp->synced)
3741 )
3742 {
3743 PRTSM_to_MASTER_SYNCED(ptp);
3744 return;
3745 }
3746 if((allSynced && !ptp->agree)
3747 || (ptp->proposed && ptp->agree)
3748 )
3749 {
3750 PRTSM_to_MASTER_AGREED(ptp);
3751 return;
3752 }
3753 if(ptp->proposed && !ptp->agree)
3754 {
3755 PRTSM_to_MASTER_PROPOSED(ptp);
3756 return;
3757 }
3758 if(((0 == ptp->fdWhile) || allSynced)
3759 && ptp->learn && !ptp->forward
3760 )
3761 {
3762 PRTSM_to_MASTER_FORWARD(ptp);
3763 return;
3764 }
3765 if(((0 == ptp->fdWhile) || allSynced)
3766 && !ptp->learn
3767 )
3768 {
3769 PRTSM_to_MASTER_LEARN(ptp, forwardDelay);
3770 return;
3771 }
3772 if(((ptp->sync && !ptp->synced)
3773 || (ptp->reRoot && (0 != ptp->rrWhile))
3774 || ptp->disputed
3775 )
3776 && !prt->operEdge && (ptp->learn || ptp->forward)
3777 )
3778 {
3779 PRTSM_to_MASTER_DISCARD(ptp, forwardDelay);
3780 return;
3781 }
3782 return;
3783 /* RootPort role transitions */
3784 case PRTSM_ROOT_PROPOSED:
3785 /* return; */
3786 case PRTSM_ROOT_AGREED:
3787 /* return; */
3788 case PRTSM_ROOT_SYNCED:
3789 /* return; */
3790 case PRTSM_REROOT:
3791 /* return; */
3792 case PRTSM_ROOT_FORWARD:
3793 /* return; */
3794 case PRTSM_ROOT_LEARN:
3795 /* return; */
3796 case PRTSM_REROOTED:
3797 PRTSM_to_ROOT_PORT(ptp, FwdDelay);
3798 return;
3799 case PRTSM_ROOT_PORT:
3800 if(!(ptp->selected && !ptp->updtInfo))
3801 return;
3802 if(!ptp->forward && !ptp->reRoot)
3803 {
3804 PRTSM_to_REROOT(ptp);
3805 return;
3806 }
3807 if((ptp->agreed && !ptp->synced) || (ptp->sync && ptp->synced))
3808 {
3809 PRTSM_to_ROOT_SYNCED(ptp);
3810 return;
3811 }
3812 if((allSynced && !ptp->agree) || (ptp->proposed && ptp->agree))
3813 {
3814 PRTSM_to_ROOT_AGREED(ptp);
3815 return;
3816 }
3817 if(ptp->proposed && !ptp->agree)
3818 {
3819 PRTSM_to_ROOT_PROPOSED(ptp);
3820 return;
3821 }
3822 /* 17.20.10 of 802.1D : reRooted */
3823 reRooted = true;
3824 FOREACH_PTP_IN_TREE(ptp_1, tree)
3825 {
3826 if((ptp != ptp_1) && (0 != ptp_1->rrWhile))
3827 {
3828 reRooted = false;
3829 break;
3830 }
3831 }
3832 if((0 == ptp->fdWhile)
3833 || (reRooted && (0 == ptp->rbWhile) && rstpVersion(prt->bridge))
3834 )
3835 {
3836 if(!ptp->learn)
3837 {
3838 PRTSM_to_ROOT_LEARN(ptp, forwardDelay);
3839 return;
3840 }
3841 else if(!ptp->forward)
3842 {
3843 PRTSM_to_ROOT_FORWARD(ptp);
3844 return;
3845 }
3846 }
3847 if(ptp->reRoot && ptp->forward)
3848 {
3849 PRTSM_to_REROOTED(ptp);
3850 return;
3851 }
3852 if(ptp->rrWhile != FwdDelay)
3853 {
3854 PRTSM_to_ROOT_PORT(ptp, FwdDelay);
3855 return;
3856 }
3857 return;
3858 /* DesignatedPort role transitions */
3859 case PRTSM_DESIGNATED_PROPOSE:
3860 /* return; */
3861 case PRTSM_DESIGNATED_AGREED:
3862 /* return; */
3863 case PRTSM_DESIGNATED_SYNCED:
3864 /* return; */
3865 case PRTSM_DESIGNATED_RETIRED:
3866 /* return; */
3867 case PRTSM_DESIGNATED_FORWARD:
3868 /* return; */
3869 case PRTSM_DESIGNATED_LEARN:
3870 /* return; */
3871 case PRTSM_DESIGNATED_DISCARD:
3872 PRTSM_to_DESIGNATED_PORT(ptp);
3873 return;
3874 case PRTSM_DESIGNATED_PORT:
3875 if(!(ptp->selected && !ptp->updtInfo))
3876 return;
3877 if(ptp->reRoot && (0 == ptp->rrWhile))
3878 {
3879 PRTSM_to_DESIGNATED_RETIRED(ptp);
3880 return;
3881 }
3882 if((!ptp->learning && !ptp->forwarding && !ptp->synced)
3883 || (ptp->agreed && !ptp->synced)
3884 || (prt->operEdge && !ptp->synced)
3885 || (ptp->sync && ptp->synced)
3886 )
3887 {
3888 PRTSM_to_DESIGNATED_SYNCED(ptp);
3889 return;
3890 }
3891 if(allSynced && (ptp->proposed || !ptp->agree))
3892 {
3893 PRTSM_to_DESIGNATED_AGREED(ptp);
3894 return;
3895 }
3896 if(!ptp->forward && !ptp->agreed && !ptp->proposing
3897 && !prt->operEdge)
3898 {
3899 PRTSM_to_DESIGNATED_PROPOSE(ptp);
3900 return;
3901 }
3902 if(((0 == ptp->fdWhile) || ptp->agreed || prt->operEdge)
3903 && ((0 == ptp->rrWhile) || !ptp->reRoot) && !ptp->sync
3904 )
3905 {
3906 if(!ptp->learn)
3907 {
3908 PRTSM_to_DESIGNATED_LEARN(ptp, forwardDelay);
3909 return;
3910 }
3911 else if(!ptp->forward)
3912 {
3913 PRTSM_to_DESIGNATED_FORWARD(ptp);
3914 return;
3915 }
3916 }
3917 if(((ptp->sync && !ptp->synced)
3918 || (ptp->reRoot && (0 != ptp->rrWhile))
3919 || ptp->disputed
3920 )
3921 && !prt->operEdge && (ptp->learn || ptp->forward)
3922 )
3923 {
3924 PRTSM_to_DESIGNATED_DISCARD(ptp, forwardDelay);
3925 return;
3926 }
3927 return;
3928 /* AlternatePort and BackupPort role transitions */
3929 case PRTSM_BLOCK_PORT:
3930 if(ptp->selected && !ptp->updtInfo
3931 && !ptp->learning && !ptp->forwarding
3932 )
3933 PRTSM_to_ALTERNATE_PORT(ptp, forwardDelay);
3934 return;
3935 case PRTSM_BACKUP_PORT:
3936 /* return; */
3937 case PRTSM_ALTERNATE_PROPOSED:
3938 /* return; */
3939 case PRTSM_ALTERNATE_AGREED:
3940 PRTSM_to_ALTERNATE_PORT(ptp, forwardDelay);
3941 return;
3942 case PRTSM_ALTERNATE_PORT:
3943 if(!(ptp->selected && !ptp->updtInfo))
3944 return;
3945 if((allSynced && !ptp->agree) || (ptp->proposed && ptp->agree))
3946 {
3947 PRTSM_to_ALTERNATE_AGREED(ptp);
3948 return;
3949 }
3950 if(ptp->proposed && !ptp->agree)
3951 {
3952 PRTSM_to_ALTERNATE_PROPOSED(ptp);
3953 return;
3954 }
3955 if((ptp->rbWhile != 2 * HelloTime) && (roleBackup == ptp->role))
3956 {
3957 PRTSM_to_BACKUP_PORT(ptp, HelloTime);
3958 return;
3959 }
3960 if((ptp->fdWhile != forwardDelay) || ptp->sync || ptp->reRoot
3961 || !ptp->synced)
3962 {
3963 PRTSM_to_ALTERNATE_PORT(ptp, forwardDelay);
3964 return;
3965 }
3966 return;
3967 }
3968}
3969
3970/* 13.35 Port State Transition state machine */
3971
3972static void PSTSM_run(per_tree_port_t *ptp);
3973#define PSTSM_begin(ptp) PSTSM_to_DISCARDING((ptp), true)
3974
3975static void PSTSM_to_DISCARDING(per_tree_port_t *ptp, bool begin)
3976{
3977 ptp->PSTSM_state = PSTSM_DISCARDING;
3978
3979 /* This effectively sets BLOCKING state:
3980 disableLearning();
3981 disableForwarding();
3982 */
3983 if(BR_STATE_BLOCKING != ptp->state)
3984 MSTP_OUT_set_state(ptp, BR_STATE_BLOCKING);
5453a511
VD
3985 ptp->learning = false;
3986 ptp->forwarding = false;
1e6d2d09
VD
3987
3988 if(!begin)
3989 PSTSM_run(ptp);
3990}
3991
3992static void PSTSM_to_LEARNING(per_tree_port_t *ptp)
3993{
3994 ptp->PSTSM_state = PSTSM_LEARNING;
3995
3996 /* enableLearning(); */
3997 if(BR_STATE_LEARNING != ptp->state)
3998 MSTP_OUT_set_state(ptp, BR_STATE_LEARNING);
5453a511 3999 ptp->learning = true;
1e6d2d09
VD
4000
4001 PSTSM_run(ptp);
4002}
4003
4004static void PSTSM_to_FORWARDING(per_tree_port_t *ptp)
4005{
4006 ptp->PSTSM_state = PSTSM_FORWARDING;
4007
4008 /* enableForwarding(); */
4009 if(BR_STATE_FORWARDING != ptp->state)
4010 MSTP_OUT_set_state(ptp, BR_STATE_FORWARDING);
5453a511 4011 ptp->forwarding = true;
1e6d2d09
VD
4012
4013 /* No need to run, no one condition will be met
4014 PSTSM_run(ptp); */
4015}
4016
4017static void PSTSM_run(per_tree_port_t *ptp)
4018{
4019 switch(ptp->PSTSM_state)
4020 {
4021 case PSTSM_DISCARDING:
4022 if(ptp->learn)
4023 PSTSM_to_LEARNING(ptp);
4024 return;
4025 case PSTSM_LEARNING:
4026 if(!ptp->learn)
4027 PSTSM_to_DISCARDING(ptp, false);
4028 else if(ptp->forward)
4029 PSTSM_to_FORWARDING(ptp);
4030 return;
4031 case PSTSM_FORWARDING:
4032 if(!ptp->forward)
4033 PSTSM_to_DISCARDING(ptp, false);
4034 return;
4035 }
4036}
4037
4038/* 13.36 Topology Change state machine */
4039
4040#define TCSM_begin(ptp) TCSM_to_INACTIVE((ptp), true)
4041
4042static void TCSM_to_INACTIVE(per_tree_port_t *ptp, bool begin)
4043{
4044 ptp->TCSM_state = TCSM_INACTIVE;
4045
4046 set_fdbFlush(ptp);
4047 assign(ptp->tcWhile, 0u);
4048 set_TopologyChange(ptp->tree, false);
4049 if(0 == ptp->MSTID) /* CIST */
5453a511 4050 ptp->port->tcAck = false;
1e6d2d09
VD
4051
4052 if(!begin)
4053 TCSM_run(ptp);
4054}
4055
4056static void TCSM_to_LEARNING(per_tree_port_t *ptp)
4057{
4058 ptp->TCSM_state = TCSM_LEARNING;
4059
4060 if(0 == ptp->MSTID) /* CIST */
4061 {
4062 port_t *prt = ptp->port;
5453a511
VD
4063 prt->rcvdTcn = false;
4064 prt->rcvdTcAck = false;
1e6d2d09 4065 }
5453a511
VD
4066 ptp->rcvdTc = false;
4067 ptp->tcProp = false;
1e6d2d09
VD
4068
4069 TCSM_run(ptp);
4070}
4071
4072static void TCSM_to_DETECTED(per_tree_port_t *ptp)
4073{
4074 ptp->TCSM_state = TCSM_DETECTED;
4075
4076 newTcWhile(ptp);
4077 setTcPropTree(ptp);
4078 /* newInfoXst = TRUE; */
4079 port_t *prt = ptp->port;
4080 if(0 == ptp->MSTID)
5453a511 4081 prt->newInfo = true;
1e6d2d09 4082 else
5453a511 4083 prt->newInfoMsti = true;
1e6d2d09
VD
4084
4085 TCSM_run(ptp);
4086}
4087
4088static void TCSM_to_NOTIFIED_TCN(per_tree_port_t *ptp)
4089{
4090 ptp->TCSM_state = TCSM_NOTIFIED_TCN;
4091
4092 newTcWhile(ptp);
4093
4094 TCSM_run(ptp);
4095}
4096
4097static void TCSM_to_NOTIFIED_TC(per_tree_port_t *ptp)
4098{
4099 ptp->TCSM_state = TCSM_NOTIFIED_TC;
4100
5453a511 4101 ptp->rcvdTc = false;
1e6d2d09
VD
4102 if(0 == ptp->MSTID) /* CIST */
4103 {
4104 port_t *prt = ptp->port;
5453a511 4105 prt->rcvdTcn = false;
1e6d2d09 4106 if(roleDesignated == ptp->role)
5453a511 4107 prt->tcAck = true;
1e6d2d09
VD
4108 }
4109 setTcPropTree(ptp);
4110
4111 TCSM_run(ptp);
4112}
4113
4114static void TCSM_to_PROPAGATING(per_tree_port_t *ptp)
4115{
4116 ptp->TCSM_state = TCSM_PROPAGATING;
4117
4118 newTcWhile(ptp);
4119 set_fdbFlush(ptp);
5453a511 4120 ptp->tcProp = false;
1e6d2d09
VD
4121
4122 TCSM_run(ptp);
4123}
4124
4125static void TCSM_to_ACKNOWLEDGED(per_tree_port_t *ptp)
4126{
4127 ptp->TCSM_state = TCSM_ACKNOWLEDGED;
4128
4129 assign(ptp->tcWhile, 0u);
4130 set_TopologyChange(ptp->tree, false);
5453a511 4131 ptp->port->rcvdTcAck = false;
1e6d2d09
VD
4132
4133 TCSM_run(ptp);
4134}
4135
4136static void TCSM_to_ACTIVE(per_tree_port_t *ptp)
4137{
4138 ptp->TCSM_state = TCSM_ACTIVE;
4139
4140 TCSM_run(ptp);
4141}
4142
4143static void TCSM_run(per_tree_port_t *ptp)
4144{
4145 bool active_port;
4146 port_t *prt = ptp->port;
4147
4148 switch(ptp->TCSM_state)
4149 {
4150 case TCSM_INACTIVE:
4151 if(ptp->learn && !ptp->fdbFlush)
4152 TCSM_to_LEARNING(ptp);
4153 return;
4154 case TCSM_LEARNING:
4155 active_port = (roleRoot == ptp->role)
4156 || (roleDesignated == ptp->role)
4157 || (roleMaster == ptp->role);
4158 if(active_port && ptp->forward && !prt->operEdge)
4159 {
4160 TCSM_to_DETECTED(ptp);
4161 return;
4162 }
4163 if(ptp->rcvdTc || prt->rcvdTcn || prt->rcvdTcAck || ptp->tcProp)
4164 {
4165 TCSM_to_LEARNING(ptp);
4166 return;
4167 }
4168 else if(!active_port && !(ptp->learn || ptp->learning))
4169 TCSM_to_INACTIVE(ptp, false);
4170 return;
4171 case TCSM_NOTIFIED_TCN:
4172 TCSM_to_NOTIFIED_TC(ptp);
4173 return;
4174 case TCSM_DETECTED:
4175 /* return; */
4176 case TCSM_NOTIFIED_TC:
4177 /* return; */
4178 case TCSM_PROPAGATING:
4179 /* return; */
4180 case TCSM_ACKNOWLEDGED:
4181 TCSM_to_ACTIVE(ptp);
4182 return;
4183 case TCSM_ACTIVE:
4184 active_port = (roleRoot == ptp->role)
4185 || (roleDesignated == ptp->role)
4186 || (roleMaster == ptp->role);
4187 if(!active_port || prt->operEdge)
4188 {
4189 TCSM_to_LEARNING(ptp);
4190 return;
4191 }
4192 if(prt->rcvdTcn)
4193 {
4194 TCSM_to_NOTIFIED_TCN(ptp);
4195 return;
4196 }
4197 if(ptp->rcvdTc)
4198 {
4199 TCSM_to_NOTIFIED_TC(ptp);
4200 return;
4201 }
4202 if(ptp->tcProp/* && !prt->operEdge */)
4203 {
4204 TCSM_to_PROPAGATING(ptp);
4205 return;
4206 }
4207 if(prt->rcvdTcAck)
4208 {
4209 TCSM_to_ACKNOWLEDGED(ptp);
4210 return;
4211 }
4212 return;
4213 }
4214}
4215
4216/* Execute BEGIN state. We do not define BEGIN variable
4217 * but instead xxx_state_machines_begin execute begin state
4218 * abd do one step out of it
4219 */
4220
4221static void tree_state_machines_begin(tree_t *tree)
4222{
4223 bridge_t *br = tree->bridge;
4224 per_tree_port_t *ptp;
4225
4226 if(!br->bridgeEnabled)
4227 return;
4228
4229 /* 13.32 Port Information state machine */
4230 FOREACH_PTP_IN_TREE(ptp, tree)
4231 {
4232 ptp->start_time = br->uptime; /* 12.8.2.2.3 b) */
4233 PISM_begin(ptp);
4234 }
4235
4236 /* 13.33 Port Role Selection state machine */
4237 PRSSM_begin(tree);
4238
4239 /* 13.34 Port Role Transitions state machine */
4240 FOREACH_PTP_IN_TREE(ptp, tree)
4241 PRTSM_begin(ptp);
4242 /* 13.35 Port State Transition state machine */
4243 FOREACH_PTP_IN_TREE(ptp, tree)
4244 PSTSM_begin(ptp);
4245 /* 13.36 Topology Change state machine */
4246 FOREACH_PTP_IN_TREE(ptp, tree)
4247 TCSM_begin(ptp);
4248
4249 br_state_machines_run(br);
4250}
4251
4252static void prt_state_machines_begin(port_t *prt)
4253{
4254 bridge_t *br = prt->bridge;
4255 tree_t *tree;
4256 per_tree_port_t *ptp;
4257
4258 if(!br->bridgeEnabled)
4259 return;
4260
4261 /* 13.28 Port Receive state machine */
4262 PRSM_begin(prt);
4263 /* 13.29 Port Protocol Migration state machine */
4264 PPMSM_begin(prt);
4265 /* 13.30 Bridge Detection state machine */
4266 BDSM_begin(prt);
4267 /* 13.31 Port Transmit state machine */
4268 PTSM_begin(prt);
4269
4270 /* 13.32 Port Information state machine */
4271 FOREACH_PTP_IN_PORT(ptp, prt)
4272 {
4273 ptp->start_time = br->uptime; /* 12.8.2.2.3 b) */
4274 PISM_begin(ptp);
4275 }
4276
4277 /* 13.33 Port Role Selection state machine */
4278 FOREACH_TREE_IN_BRIDGE(tree, br)
4279 PRSSM_run(tree);
4280
4281 /* 13.34 Port Role Transitions state machine */
4282 FOREACH_PTP_IN_PORT(ptp, prt)
4283 PRTSM_begin(ptp);
4284 /* 13.35 Port State Transition state machine */
4285 FOREACH_PTP_IN_PORT(ptp, prt)
4286 PSTSM_begin(ptp);
4287 /* 13.36 Topology Change state machine */
4288 FOREACH_PTP_IN_PORT(ptp, prt)
4289 TCSM_begin(ptp);
4290
4291 br_state_machines_run(br);
4292}
4293
4294static void br_state_machines_begin(bridge_t *br)
4295{
4296 port_t *prt;
4297 per_tree_port_t *ptp;
4298 tree_t *tree;
4299
4300 if(!br->bridgeEnabled)
4301 return;
4302
4303 /* 13.28 Port Receive state machine */
4304 FOREACH_PORT_IN_BRIDGE(prt, br)
4305 PRSM_begin(prt);
4306 /* 13.29 Port Protocol Migration state machine */
4307 FOREACH_PORT_IN_BRIDGE(prt, br)
4308 PPMSM_begin(prt);
4309 /* 13.30 Bridge Detection state machine */
4310 FOREACH_PORT_IN_BRIDGE(prt, br)
4311 BDSM_begin(prt);
4312 /* 13.31 Port Transmit state machine */
4313 FOREACH_PORT_IN_BRIDGE(prt, br)
4314 PTSM_begin(prt);
4315
4316 /* 13.32 Port Information state machine */
4317 FOREACH_PORT_IN_BRIDGE(prt, br)
4318 {
4319 FOREACH_PTP_IN_PORT(ptp, prt)
4320 {
4321 ptp->start_time = br->uptime; /* 12.8.2.2.3 b) */
4322 PISM_begin(ptp);
4323 }
4324 }
4325
4326 /* 13.33 Port Role Selection state machine */
4327 FOREACH_TREE_IN_BRIDGE(tree, br)
4328 PRSSM_begin(tree);
4329
4330 /* 13.34 Port Role Transitions state machine */
4331 FOREACH_PORT_IN_BRIDGE(prt, br)
4332 {
4333 FOREACH_PTP_IN_PORT(ptp, prt)
4334 PRTSM_begin(ptp);
4335 }
4336 /* 13.35 Port State Transition state machine */
4337 FOREACH_PORT_IN_BRIDGE(prt, br)
4338 {
4339 FOREACH_PTP_IN_PORT(ptp, prt)
4340 PSTSM_begin(ptp);
4341 }
4342 /* 13.36 Topology Change state machine */
4343 FOREACH_PORT_IN_BRIDGE(prt, br)
4344 {
4345 FOREACH_PTP_IN_PORT(ptp, prt)
4346 TCSM_begin(ptp);
4347 }
4348
4349 br_state_machines_run(br);
4350}
4351
4352/* Run each state machine */
4353static void br_state_machines_run(bridge_t *br)
4354{
4355 port_t *prt;
4356 per_tree_port_t *ptp;
4357 tree_t *tree;
4358
4359 if(!br->bridgeEnabled)
4360 return;
4361
4362 /* 13.28 Port Receive state machine */
4363 FOREACH_PORT_IN_BRIDGE(prt, br)
4364 PRSM_run(prt);
4365 /* 13.29 Port Protocol Migration state machine */
4366 FOREACH_PORT_IN_BRIDGE(prt, br)
4367 PPMSM_run(prt);
4368 /* 13.30 Bridge Detection state machine */
4369 FOREACH_PORT_IN_BRIDGE(prt, br)
4370 BDSM_run(prt);
4371 /* 13.31 Port Transmit state machine */
4372 FOREACH_PORT_IN_BRIDGE(prt, br)
4373 PTSM_run(prt);
4374
4375 /* 13.32 Port Information state machine */
4376 FOREACH_PORT_IN_BRIDGE(prt, br)
4377 {
4378 FOREACH_PTP_IN_PORT(ptp, prt)
4379 PISM_run(ptp);
4380 }
4381
4382 /* 13.33 Port Role Selection state machine */
4383 FOREACH_TREE_IN_BRIDGE(tree, br)
4384 PRSSM_run(tree);
4385
4386 /* 13.34 Port Role Transitions state machine */
4387 FOREACH_PORT_IN_BRIDGE(prt, br)
4388 {
4389 FOREACH_PTP_IN_PORT(ptp, prt)
4390 PRTSM_run(ptp);
4391 }
4392 /* 13.35 Port State Transition state machine */
4393 FOREACH_PORT_IN_BRIDGE(prt, br)
4394 {
4395 FOREACH_PTP_IN_PORT(ptp, prt)
4396 PSTSM_run(ptp);
4397 }
4398 /* 13.36 Topology Change state machine */
4399 FOREACH_PORT_IN_BRIDGE(prt, br)
4400 {
4401 FOREACH_PTP_IN_PORT(ptp, prt)
4402 TCSM_run(ptp);
4403 }
4404}