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