]> git.ipfire.org Git - people/ms/rstp.git/blame - rstp.c
Add in new RSTP implementation to replace RSTPLIB.
[people/ms/rstp.git] / rstp.c
CommitLineData
32594105
SA
1/*****************************************************************************
2 Copyright (c) 2007 EMC Corporation.
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2 of the License, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 more details.
13
14 You should have received a copy of the GNU General Public License along with
15 this program; if not, write to the Free Software Foundation, Inc., 59
16 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18 The full GNU General Public License is included in this distribution in the
19 file called LICENSE.
20
21 Authors: Srinivas Aji <Aji_Srinivas@emc.com>
22
23******************************************************************************/
24
25/*********************************************************************
26 This is an implementation of the Rapid Spanning Tree Protocol
27 based on the 802.1D-2004 standard.
28 Section references in the code refer to those parts of the standard.
29*********************************************************************/
30
31#include "rstp.h"
32#include <string.h>
33
34/* Macros to be used in the later parts */
35
36#ifndef NULL
37#define NULL ((void *)0)
38#endif
39
40#define FALSE 0
41#define TRUE 1
42
43
44/* memcmp wrapper, validating size equality of _a and _b */
45#define CMP(_a, _op, _b) \
46(memcmp(&(_a), &(_b), \
47 __builtin_choose_expr(sizeof(_a) == sizeof(_b), \
48 sizeof(_a), \
49 (void)0) \
50) _op 0)
51
52#define ZERO(_a) memset(&(_a), 0, sizeof(_a))
53
54/* memcmp wrapper, validating size equality of _a and _b */
55#define CPY(_a, _b) \
56(memcpy(&(_a), &(_b), \
57 __builtin_choose_expr(sizeof(_a) == sizeof(_b), \
58 sizeof(_a), \
59 (void)0) \
60))
61
62#define BRIDGE_ARGS_PROTO Bridge *BRIDGE
63#define PORT_ARGS_PROTO Bridge *BRIDGE, Port *PORT
64
65#define BRIDGE_ARGS BRIDGE
66#define PORT_ARGS BRIDGE, PORT
67#define FPORT_ARGS BRIDGE, FPORT
68
69/* Define BRIDGE and PORT as global pointers to a struct type which holds
70 a user_ref of value NULL, and no other fields.
71 That way log messages can use BRIDGE and PORT always.
72*/
73
74struct _dummy_user_ref_struct
75{
76 void *user_ref;
77} _user_ref_var = { .user_ref = NULL},
78 *BRIDGE = &_user_ref_var, *PORT = &_user_ref_var;
79
80/* Logging macros */
81#define DEBUG(fmt...) \
82 STP_OUT_logmsg(BRIDGE->user_ref, PORT->user_ref, STP_LOG_LEVEL_DEBUG, fmt)
83#define ERROR(fmt...) \
84 STP_OUT_logmsg(BRIDGE->user_ref, PORT->user_ref, STP_LOG_LEVEL_ERROR, fmt)
85#define ABORT(fmt...) \
86 ({ ERROR(fmt); *(int *)0 = 1; })
87
88/***************** Macros for state machine descriptions ********************/
89
90#define _CAT2(x, y) x ## _ ## y
91
92#define _CAT(x, y) _CAT2(x, y)
93
94#define __STR(x) #x
95
96#define _STR(x) __STR(x)
97
98#define STN(_state) _CAT(STM_NAME, _state)
99
100#define STM_LABEL(_state) _CAT(STM_NAME, label_ ## _state)
101
102#define STM_FUNC(_name) _CAT(_name, func)
103
104/* BEGIN is state 0 in all state machines */
105#define STATES(_args...) enum _CAT(STM_NAME, states) { STN(BEGIN), _args }
106
107/* Transition definition */
108#define TR(_cond, _new_state) \
109({ \
110 if (_cond) { \
111 new_state = STN(_new_state); \
112 DEBUG("%s: Transition to state %s\n", _STR(STM_NAME), #_new_state); \
113 goto STM_LABEL(_new_state); \
114 } \
115})
116
117/* Global conditions: Transition conditions that don't depend on current state.
118 In our state machine, they get checked after those that are specific to the
119 current state
120 new_state is the state we last transitioned to, or 0 if no transition
121 was made.
122*/
123#define GC(_transitions...) \
124 global_conditions: \
125 _transitions \
126 return new_state
127
128/* Transitions from BEGIN state */
129#define BG(_transitions...) \
130 case STN(BEGIN): \
131 _transitions \
132 ABORT("%s: No where to go from BEGIN\n", _STR(STM_NAME)); \
133 return -1
134
135/* State definition */
136#define ST(_state, _actions, _transitions) \
137 STM_LABEL(_state): \
138 _actions \
139 if (single_step) \
140 return new_state; \
141 case STN(_state): \
142 _transitions \
143 goto global_conditions
144
145#define STM_FUNC_DECL(_type, _args...) \
146 int _type(int state, int single_step, _args)
147
148/* This just barely works. _args expansion includes commas */
149//int STM_FUNC(STM_NAME)(int state, int single_step, _args)
150
151#define STM(_args, _contents...) \
152static STM_FUNC_DECL(STM_FUNC(STM_NAME), _args) \
153{ \
154 int new_state = 0; \
155 switch (state) { \
156 _contents \
157 default: \
158 ABORT("%s: Got unknown state %d\n", __func__, state); \
159 return -1; \
160 } \
161}
162
163#define UCT 1
164
165#define STM_RUN(_state, _transitioned, _func, _args...) \
166({ \
167 int new_state = _func(*_state, 0/*no single step*/, _args); \
168 if (new_state > 0) { \
169 *_state = new_state; \
170 *_transitioned = TRUE; \
171 } \
172})
173
174#define STM_BEGIN(_state, _func, _args...) \
175({ \
176 *_state = _func(0/*BEGIN*/, 1/*single step*/, _args); \
177})
178
179/************ End of Macros for state machine descriptions ***********/
180
181//Types:
182
183typedef unsigned char uchar;
184
185typedef unsigned int uint;
186
187typedef unsigned short uint16;
188
189typedef uint bool;
190
191typedef struct _BridgeId {
192 uchar bridge_identifier_priority[2];
193 uchar bridge_address[6];
194} __attribute__((packed)) BridgeId;
195
196typedef struct {
197 uchar cost[4];
198} PathCost;
199
200static inline uint path_cost_to_uint(PathCost x)
201{
202 return
203 (x.cost[0] << 24) + (x.cost[1] << 16) + (x.cost[2] << 8) + (x.cost[3]);
204
205}
206
207
208static inline PathCost path_cost_add(PathCost x, uint y)
209{
210 uint z = y +
211 (x.cost[0] << 24) + (x.cost[1] << 16) + (x.cost[2] << 8) + (x.cost[3]);
212
213 PathCost r = {
214 .cost = {
215 (z >> 24), (z >> 16) & 0xff, (z >> 8) & 0xff, z & 0xff
216 }
217 };
218
219 return r;
220}
221
222typedef struct _PortId {
223 uchar port_id[2]; /* 4 high bits of priority and 12 for Id */
224} __attribute__((packed)) PortId;
225
226static inline uint port_id_to_uint(PortId p)
227{
228 return (p.port_id[0] << 8) + p.port_id[1];
229}
230
231typedef struct _PriorityVector
232{
233 BridgeId root_bridge_id;
234 PathCost root_path_cost;
235 BridgeId designated_bridge_id;
236 PortId designated_port_id;
237 PortId bridge_port_id;
238} __attribute__((packed)) PriorityVector;
239
240/* First 4 components of priority vector */
241typedef struct _PriorityVector4
242{
243 BridgeId root_bridge_id;
244 PathCost root_path_cost;
245 BridgeId designated_bridge_id;
246 PortId designated_port_id;
247} __attribute__((packed)) PriorityVector4;
248
249
250typedef struct _Times
251{
252 uint forward_delay;
253 uint hello_time;
254 uint max_age;
255 uint message_age;
256} Times;
257
258typedef enum _RcvdInfo {
259 SuperiorDesignatedInfo,
260 RepeatedDesignatedInfo,
261 InferiorDesignatedInfo,
262 InferiorRootAlternateInfo,
263 OtherInfo
264} RcvdInfo;
265
266
267typedef enum _InfoType {
268 Mine,
269 Aged,
270 Received,
271 Disabled
272} InfoType;
273
274// We accomodate the port role encoding in BPDUs as per 9.2.9
275// into this type, defining an extra value of AltBackupPort valid
276// only in a BPDU.
277typedef enum _Role {
278 UnknownPort = 0,
279 AltBackupPort = 1,
280 RootPort = 2,
281 DesignatedPort = 3,
282 AlternatePort,
283 BackupPort,
284 DisabledPort
285} Role;
286
287typedef enum _BPDUType {
288 BPDUTypeConfig = 0,
289 BPDUTypeRST = 2,
290 BPDUTypeTCN = 128
291} BPDUType;
292
293/* Defining fields directly in struct _Port */
294#if 0
295typedef struct _ParsedBPDU
296{
297 uint version;
298 BPDUType bpdu_type;
299
300 /* flags */
301 bool bpdu_tcFlag; // bit 1 of flags
302 bool bpdu_proposalFlag; // bit 2 of flags
303 Role bpdu_role; // bits 3 & 4 of flags
304 bool bpdu_learningFlag; // bit 5 of flags
305 bool bpdu_forwardingFlag; // bit 6 of flags
306 bool bpdu_agreementFlag; // bit 7 of flags
307 bool bpdu_tcAckFlag; // bit 8 of flags
308
309 PriorityVector4 bpdu_priority;
310 Times bpdu_times;
311
312} ParsedBPDU;
313#endif
314
315#define STP_PROTOCOL_ID 0
316
317typedef struct _RawBPDU
318{
319 uint16 protocol_id;
320 uchar version;
321 uchar type;
322 /* TCN has only upto this */
323 uchar TCN_END[0];
324 uchar flags;
325#define BPDU_FLAG_TC 0x1
326#define BPDU_FLAG_PROPOSAL 0x2
327#define BPDU_FLAG_ROLE_MASK 0xc
328#define BPDU_FLAG_ROLE(role) ((role << 2) & BPDU_FLAG_ROLE_MASK)
329#define BPDU_FLAG_ROLE_GET(flags) ((flags & BPDU_FLAG_ROLE_MASK)>>2)
330#define BPDU_FLAG_LEARNING 0x10
331#define BPDU_FLAG_FORWARDING 0x20
332#define BPDU_FLAG_AGREEMENT 0x40
333#define BPDU_FLAG_TC_ACK 0x80
334
335 PriorityVector4 priority;
336
337 uchar message_age[2];
338 uchar max_age[2];
339 uchar hello_time[2];
340 uchar forward_delay[2];
341 /* End of Config BPDU */
342 uchar CONFIG_END[0];
343
344 uchar version1_len;
345 /* End of RST BPDU */
346 uchar RST_END[0];
347
348} __attribute__((packed)) RawBPDU;
349
350
351
352/* Values for adminPointToPointMAC */
353typedef enum _AdminP2P {
354 P2PForceFalse = STP_ADMIN_P2P_FORCE_FALSE,
355 P2PForceTrue = STP_ADMIN_P2P_FORCE_TRUE,
356 P2PAuto = STP_ADMIN_P2P_AUTO
357} AdminP2P;
358
359
360/* Number of per bridge and per port state machines. */
361#define NUM_BRIDGE_STATE_MACHINES 1
362#define NUM_PORT_STATE_MACHINES 9
363
364/*
365 Framework:
366
367 Wherever this is a bridge context, we assume the variable BRIDGE points
368 to it. And wherever there is a port context, we assume PORT points to it.
369
370 We define all the fields in the Bridge and Port structures as starting
371 with and underscore and have
372 #define bridgevar BRIDGE->_bridgevar
373 and
374 #define portvar PORT->_portvar
375
376 This way we keep the expressions in the state machines fairly close
377 to the way they appear in the standard's figures.
378
379*/
380
381typedef STP_Port Port;
382typedef STP_Bridge Bridge;
383
384/* Per Bridge: */
385
386struct STP_Bridge_
387{
388
389 /* Administratively set variables */
390
391 // 17.13.2
392 // - Not defining - Never used.
393 // This is meant to control FDB, we don't deal with that here.
394 /*int _AgeingTime;*/
395 /*#define AgeingTime BRIDGE->_AgeingTime*/
396
397 // 17.13.4
398 // This is item a) in the list in the introduction in 17.13
399 // As per that, we need to reinitialize state machines by asserting BEGIN
400 // when changing this
401 uint _ForceProtocolVersion;
402#define ForceProtocolVersion BRIDGE->_ForceProtocolVersion
403
404 // 17.13.5
405 // - Not defining - this is part of BridgeTimes below
406 /*int BridgeForwardDelay;*/
407#define BridgeForwardDelay BridgeTimes.forward_delay
408
409 // 17.13.6
410 // - Not defining - this is part of BridgeTimes below
411 /*int BridgeHelloTime;*/
412#define BridgeHelloTime BridgeTimes.hello_time
413
414 // 17.13.7 BridgeIdentifierPriority
415 // This is item b) in the list in the introduction in 17.13
416 // As per that, we need to clear selected and set reselect when changing this
417 // Let us do it for all ports since this is a bridge variable.
418 // - Not defining this variable, since it is part of BridgeIdentifier below
419 /*int BridgeIdentifierPriority;*/
420#define BridgeIdentifierPriority BridgeIdentifier.bridge_identifier_priority
421
422 // 17.13.8
423 // - Not defining - this is part of BridgeTimes below
424 /*int BridgeMaxAge;*/
425#define BridgeMaxAge BridgeTimes.max_age
426
427 // 17.13.9
428 uint _MigrateTime;
429#define MigrateTime BRIDGE->_MigrateTime
430
431 // 17.13.12
432 uint _TransmitHoldCount;
433
434#define TransmitHoldCount BRIDGE->_TransmitHoldCount
435
436 /* Other (not administrative) */
437
438 // 17.18.1
439 // Condition that initializes all state machines.
440 // But machines will spin if it continues to be asserted,
441 // So we make it state that we put all machines in instead of asserting this
442 /*bool BEGIN;*/
443
444 // 17.18.1
445 // The high 16 bits are BridgeIdentifierPriority, noted above (17.13.7)
446 // We need to change the rest of BridgeIdentifier if the bridge MAC address
447 // changes. Standard doesn't expect this. Let us reinitialize state machines
448 // when this happens
449 BridgeId _BridgeIdentifier; /* Includes priority */
450#define BridgeIdentifier BRIDGE->_BridgeIdentifier
451
452 // 17.18.3, = B:0:B:0:0 - where B is Bridge Identifier
453 // - Not defining it .
454 // - We will compute it from BridgeIdentifier in updtRolesTree()
455 /*PriorityVector _BridgePriority;*/
456 /*#define BridgePriority BRIDGE->_BridgePriority*/
457
458 // 17.18.4
459 // This holds BridgeForwardDelay, BridgeHelloTime, BridgeMaxAge
460 // in .forward_delay, .hello_time, .max_age
461 // .message_age is 0
462 Times _BridgeTimes;
463#define BridgeTimes BRIDGE->_BridgeTimes
464
465 // 17.18.5
466 // 5th component of root priority vector
467 PortId _rootPortId;
468#define rootPortId BRIDGE->_rootPortId
469
470 // 17.18.6
471 // first 4 components of root priority vector
472 PriorityVector4 _rootPriority;
473#define rootPriority BRIDGE->_rootPriority
474
475 // 17.18.7
476 /* operational times got from portTimes for root port, or from BridgeTimes */
477 Times _rootTimes;
478#define rootTimes BRIDGE->_rootTimes
479
480
481
482 uint time_since_tc;
483 uint tc_count;
484 uint tcWhile_count; /* How many port tcWhile's are nonzero */
485
486 /* Our stuff - not from standard */
487
488 Port *port_list;
489 void *user_ref;
490 bool stp_on;
491 int state[NUM_BRIDGE_STATE_MACHINES];
492};
493
494
495/* Per port */
496
497struct STP_Port_
498{
499
500 /* Administrative variables: */
501
502 // 17.13.2
503 bool _AdminEdgePort;
504#define AdminEdgePort PORT->_AdminEdgePort
505
506 // 17.13.3
507 bool _AutoEdgePort;
508#define AutoEdgePort PORT->_AutoEdgePort
509
510 // 17.13.10
511 // This is item c) in the introduction to 17.13
512 // As per that, we need to clear selected and set reselect for this port
513 // when changing this
514 // - Not defining this, it is part of portId below
515 /*int PortIdentifierPriority;*/
516 // Not defining macro either, it is just 4 high bits in portId.port_id[0]
517 /*#define PortIdentifierPriority portId.portIdPriority*/
518
519 // 17.13.11
520 // This is item d) in the introduction to 17.13
521 // As per that, we need to clear selected and set reselect for this port
522 // when changing this
523 // PortPathCost defined below ref 17.19.20, is set to this if this is
524 // non-zero, else it is auto-set based on link speed.
525 uint _AdminPortPathCost;
526#define AdminPortPathCost PORT->_AdminPortPathCost
527
528 // 6.4.2
529 // MAC Enabled: Controlled by ifconfig <portif> up/down
530 // We will sense and act on it
531 // We set port enabled in that case
532
533 // 6.4.3
534 AdminP2P _adminPointToPointMAC;
535#define adminPointToPointMAC PORT->_adminPointToPointMAC
536
537 bool _operPointToPointMAC;
538#define operPointToPointMAC PORT->_operPointToPointMAC
539
540 /* Timers: */
541
542 // 17.17.1
543 uint _edgeDelayWhile;
544#define edgeDelayWhile PORT->_edgeDelayWhile
545
546 // 17.17.2
547 uint _fdWhile;
548#define fdWhile PORT->_fdWhile
549
550 // 17.17.3
551 uint _helloWhen;
552#define helloWhen PORT->_helloWhen
553
554 // 17.17.4
555 uint _mdelayWhile;
556#define mdelayWhile PORT->_mdelayWhile
557
558 // 17.17.5
559 uint _rbWhile;
560#define rbWhile PORT->_rbWhile
561
562 // 17.17.6
563 uint _rcvdInfoWhile;
564#define rcvdInfoWhile PORT->_rcvdInfoWhile
565
566 // 17.17.7
567 uint _rrWhile;
568#define rrWhile PORT->_rrWhile
569
570 // 17.17.8
571 uint _tcWhile;
572#define tcWhile PORT->_tcWhile
573
574 // 17.19.1
575 // - Not defining - Never used
576 // This is supposed to control FDB ageing on a per port basis.
577 // We don't control FDB ageing here.
578 /*int _ageingTime;*/
579 /*#define ageingTime PORT->_ageingTime*/
580
581 // 17.19.2
582 bool _agree;
583#define agree PORT->_agree
584
585 // 17.19.3
586 bool _agreed;
587#define agreed PORT->_agreed
588
589 // 17.19.4
590 // First 4 components of designated priority vector
591 // 5th component is portId
592 PriorityVector4 _designatedPriority;
593#define designatedPriority PORT->_designatedPriority
594
595 // 17.19.5
596 Times _designatedTimes;
597#define designatedTimes PORT->_designatedTimes
598
599 // 17.19.6
600 bool _disputed;
601#define disputed PORT->_disputed
602
603 // 17.19.8
604 /* Set by topology change state machine to instruct filtering database to
605 remove all entries for this Port, immediately if rstpVersion is TRUE
606 or by rapid ageing if stpVersion is TRUE. Reset by filtering database once
607 entries are removed if rstpVersion is TRUE and immediately if
608 stpVersion is TRUE
609 */
610 bool _fdbFlush;
611#define fdbFlush PORT->_fdbFlush
612
613 // 17.19.8
614 bool _forward;
615#define forward PORT->_forward
616
617 // 17.19.9
618 // Setting this will involve and OUT action
619 bool _forwarding;
620#define forwarding PORT->_forwarding
621
622 // 17.19.10
623 // Indicates origin/state of portInfo held for the port
624 InfoType _infoIs;
625#define infoIs PORT->_infoIs
626
627 // 17.19.11
628 bool _learn;
629#define learn PORT->_learn
630
631 // 17.19.12
632 bool _learning;
633#define learning PORT->_learning
634
635 // 17.19.13
636 bool _mcheck;
637#define mcheck PORT->_mcheck
638
639 // 17.19.14
640 // First 4 components of priority vector from received BPDU
641 PriorityVector4 _msgPriority;
642#define msgPriority PORT->_msgPriority
643
644 // 17.19.15
645 // Times from received BPDU
646 Times _msgTimes;
647#define msgTimes PORT->_msgTimes
648
649 // 17.19.16
650 /* Set if a BPDU is to be transmitted,
651 reset by Port Transmit state machine */
652 bool _newInfo;
653#define newInfo PORT->_newInfo
654
655 // 17.19.17
656 bool _operEdge;
657#define operEdge PORT->_operEdge
658
659 // 17.19.18
660 bool _portEnabled;
661#define portEnabled PORT->_portEnabled
662
663 // 17.19.19
664 PortId _portId;
665#define portId PORT->_portId
666
667 // 17.19.20
668 uint _PortPathCost;
669#define PortPathCost PORT->_PortPathCost
670
671 // 17.19.21
672 // First 4 components of port's priority vector
673 PriorityVector4 _portPriority;
674#define portPriority PORT->_portPriority
675
676 // 17.19.22
677 /* Ports timer parameter values */
678 Times _portTimes;
679#define portTimes PORT->_portTimes
680
681 // 17.19.23
682 bool _proposed;
683#define proposed PORT->_proposed
684
685 // 17.19.24
686 bool _proposing;
687#define proposing PORT->_proposing
688
689 // 17.19.25
690 /* Set by system when Config, TCN, or RST BPDU is received
691 - IN action sets this */
692 /* CORR: rcvdBPDU
693 But all other references in the doc use rcvdBpdu, so using that. */
694 bool _rcvdBpdu;
695#define rcvdBpdu PORT->_rcvdBpdu
696
697 // 17.19.26
698 /* Result of rcvInfo() procedure */
699 RcvdInfo _rcvdInfo;
700#define rcvdInfo PORT->_rcvdInfo
701
702 // 17.19.27
703 bool _rcvdMsg;
704#define rcvdMsg PORT->_rcvdMsg
705
706 // 17.19.28
707 bool _rcvdRSTP;
708#define rcvdRSTP PORT->_rcvdRSTP
709
710 // 17.19.29
711 bool _rcvdSTP;
712#define rcvdSTP PORT->_rcvdSTP
713
714 // 17.19.30
715 bool _rcvdTc;
716#define rcvdTc PORT->_rcvdTc
717
718 // 17.19.31
719 bool _rcvdTcAck;
720#define rcvdTcAck PORT->_rcvdTcAck
721
722 // 17.19.32
723 bool _rcvdTcn;
724#define rcvdTcn PORT->_rcvdTcn
725
726 // 17.19.33
727 bool _reRoot;
728#define reRoot PORT->_reRoot
729
730 // 17.19.34
731 bool _reselect;
732#define reselect PORT->_reselect
733
734 // 17.19.35
735 Role _role;
736#define role PORT->_role
737
738 // 17.19.36
739 bool _selected;
740#define selected PORT->_selected
741
742 // 17.19.37
743 Role _selectedRole;
744#define selectedRole PORT->_selectedRole
745
746 // 17.19.38
747 bool _sendRSTP;
748#define sendRSTP PORT->_sendRSTP
749
750 // 17.19.39
751 bool _sync;
752#define sync PORT->_sync
753
754 // 17.19.40
755 bool _synced;
756#define synced PORT->_synced
757
758 // 17.19.41
759 bool _tcAck;
760#define tcAck PORT->_tcAck
761
762 // 17.19.42
763 bool _tcProp;
764#define tcProp PORT->_tcProp
765
766 // 17.19.43
767 bool _tick;
768#define tick PORT->_tick
769
770 // 17.19.44
771 uint _txCount;
772#define txCount PORT->_txCount
773
774 // 17.19.45
775 bool _updtInfo;
776#define updtInfo PORT->_updtInfo
777
778 // Not in 17.19, but is used. When we receive a BPDU,
779 // fill in the received BPDU into this.
780 // We just define the fields directly
781
782 uint _bpduVersion;
783#define bpduVersion PORT->_bpduVersion
784 BPDUType _bpduType;
785#define bpduType PORT->_bpduType
786 /* flags */
787 bool _bpduTcFlag; // bit 1 of flags
788#define bpduTcFlag PORT->_bpduTcFlag
789 bool _bpduProposalFlag; // bit 2 of flags
790#define bpduProposalFlag PORT->_bpduProposalFlag
791 Role _bpduRole; // bits 3 & 4 of flags
792#define bpduRole PORT->_bpduRole
793 bool _bpduLearningFlag; // bit 5 of flags
794#define bpduLearningFlag PORT->_bpduLearningFlag
795 bool _bpduForwardingFlag; // bit 6 of flags
796#define bpduForwardingFlag PORT->_bpduForwardingFlag
797 bool _bpduAgreementFlag; // bit 7 of flags
798#define bpduAgreementFlag PORT->_bpduAgreementFlag
799 bool _bpduTcAckFlag; // bit 8 of flags
800#define bpduTcAckFlag PORT->_bpduTcAckFlag
801 PriorityVector4 _bpduPriority;
802#define bpduPriority PORT->_bpduPriority
803 Times _bpduTimes;
804#define bpduTimes PORT->_bpduTimes
805
806
807
808
809 /* Our stuff - not from standard */
810
811 Port *port_next;
812 Bridge *bridge;
813 void *user_ref;
814
815 uint port_state_flags;
816 uint speed;
817 bool duplex;
818 int state[NUM_PORT_STATE_MACHINES];
819
820};
821
822/* Macros for iterating over all ports */
823
824/* Check whether expr is true for all ports */
825/* PORT is not defined by this but is possible defined where this is used.
826 FPORT iterates over all ports, so we need to use the FPORT->_field
827 (with underscore) for for port variables in the expression here */
828#define ForAllPort(expr) \
829({ \
830 Port *FPORT; \
831 bool res = TRUE; \
832 for (FPORT = BRIDGE->port_list; FPORT != NULL; FPORT = FPORT->port_next) \
833 if (!(expr)) { \
834 res = FALSE; \
835 break; \
836 } \
837 res; \
838})
839
840/* Execute statements for all ports */
841/* PORT is not defined by this but is possible defined where this is used.
842 FPORT iterates over all ports, so we need to use the FPORT->_field
843 (with underscore) for for port variables in statements here */
844#define ForAllPortDo(statements...) \
845({ \
846 Port *FPORT; \
847 for (FPORT = BRIDGE->port_list; FPORT != NULL; FPORT = FPORT->port_next) \
848 { statements } \
849})
850
851// 17.20 ----- conditions and paramaters
852
853// 17.20.1
854#define AdminEdge AdminEdgePort
855
856// 17.20.2
857#define AutoEdge AutoEdgePort
858
859// 17.20.3
860#define allSynced ForAllPort(FPORT->_synced || FPORT->_role == RootPort)
861
862// 17.20.4
863#define EdgeDelay (operPointToPointMAC?MigrateTime:MaxAge)
864
865// 17.20.5
866#define forwardDelay (sendRSTP?HelloTime:FwdDelay)
867
868// 17.20.6
869#define FwdDelay designatedTimes.forward_delay
870
871// 17.20.7
872#define HelloTime designatedTimes.hello_time
873
874// 17.20.8
875#define MaxAge designatedTimes.max_age
876
877// 17.20.9
878// #define MigrateTime MigrateTime
879
880// 17.20.10
881#define reRooted ForAllPort(FPORT == PORT || FPORT->_rrWhile == 0)
882
883// 17.20.11
884#define rstpVersion (ForceProtocolVersion >= 2)
885
886// 17.20.12
887#define stpVersion (ForceProtocolVersion < 2)
888
889// 17.20.13
890#define TxHoldCount TransmitHoldCount
891
892// 17.21 ---------- Procedures ------------------------
893
894// 17.21.1
895// Definition has an argument newInfoIs, but the uses don't seem to have it.
896// So we define it without that, assuming newInfoIs == infoIs
897static bool betterorsameInfo(PORT_ARGS_PROTO/*, InfoType newInfoIs*/)
898{
899 return
900 (/*newInfoIs == Received &&*/ infoIs == Received &&
901 CMP(msgPriority, <=, portPriority)) ||
902 (/*newInfoIs == Mine &&*/ infoIs == Mine &&
903 CMP(designatedPriority, <=, portPriority));
904}
905
906// 17.21.2
907static void clearReselectTree(BRIDGE_ARGS_PROTO)
908{
909 ForAllPortDo(
910 FPORT->_reselect = FALSE;
911 );
912}
913
914// 17.21.3
915static void disableForwarding(PORT_ARGS_PROTO)
916{
917 /* Cause forwarding process to stop forwarding frames through this port. */
918 /* Complete before returning */
919 /* OUT */
920 PORT->port_state_flags &= ~STP_PORT_STATE_FLAG_FORWARDING;
921 STP_OUT_port_set_state(PORT->user_ref, PORT->port_state_flags);
922}
923
924// 17.21.4
925static void disableLearning(PORT_ARGS_PROTO)
926{
927 /* Cause port to stop learning process */
928 /* Complete before returning */
929 /* OUT */
930 PORT->port_state_flags &= ~STP_PORT_STATE_FLAG_LEARNING;
931 STP_OUT_port_set_state(PORT->user_ref, PORT->port_state_flags);
932}
933
934// 17.21.5
935static void enableForwarding(PORT_ARGS_PROTO)
936{
937 /* Cause port to start forwarding. */
938 /* Complete before returning */
939 /* OUT */
940 PORT->port_state_flags |= STP_PORT_STATE_FLAG_FORWARDING;
941 STP_OUT_port_set_state(PORT->user_ref, PORT->port_state_flags);
942}
943
944// 17.21.6
945static void enableLearning(PORT_ARGS_PROTO)
946{
947 /* Cause port to start learning. */
948 /* Complete before returning */
949 /* OUT */
950 PORT->port_state_flags |= STP_PORT_STATE_FLAG_LEARNING;
951 STP_OUT_port_set_state(PORT->user_ref, PORT->port_state_flags);
952}
953
954/**** Topology change tracking and reporting ****/
955
956// change is +1 if a tcWhile became non-zero, -1 if a tcWhile became 0.
957static inline void update_tcWhile_count(BRIDGE_ARGS_PROTO, int change)
958{
959 if (change == 0)
960 return;
961
962 if (BRIDGE->tcWhile_count == 0) {
963 /* Becoming nonzero from zero */
964 BRIDGE->tc_count++;
965 BRIDGE->time_since_tc = 0;
966 }
967
968 BRIDGE->tcWhile_count+= change;
969}
970
971#define set_tcWhile(_val) \
972({ uint _oldval = tcWhile; tcWhile = _val; \
973 update_tcWhile_count(BRIDGE_ARGS, (tcWhile?1:0) - (_oldval?1:0)); })
974
975
976// 17.21.7
977static void newTcWhile(PORT_ARGS_PROTO)
978{
979 if (tcWhile == 0) {
980 if (sendRSTP /* CORR: sendRstp */) {
981 // Standard wants tcWhile rounded up
982 // We rounded HelloTime up when we received it.
983 //tcWhile = HelloTime + 1;
984 set_tcWhile(HelloTime + 1);
985
986 newInfo = TRUE;
987 }
988 else {
989 //tcWhile = rootTimes.max_age + rootTimes.forward_delay;
990 set_tcWhile(rootTimes.max_age + rootTimes.forward_delay);
991 }
992 }
993}
994
995// 17.21.8
996static RcvdInfo rcvInfo(PORT_ARGS_PROTO)
997{
998
999 if (bpduType == BPDUTypeTCN) {
1000 // DIFF:
1001 // 802.1D-2004 doesn't say explicitly what we should do in this case
1002 // 802.1w-2001 says return OtherInfo, but just doing that won't ever
1003 // set rcvdTcn, or cause any effect
1004 // 802.1Q-2005 (MST, based on 802.1D-2004) says set rcvdTcn here
1005 // Going by that, we set rcvdTcn and return OtherInfo
1006
1007 rcvdTcn = TRUE;
1008 return OtherInfo;
1009 }
1010
1011 /* Note: config BPDU conveys Designated Port Role */
1012 if (bpduType == BPDUTypeConfig)
1013 bpduRole = DesignatedPort;
1014
1015 msgPriority = bpduPriority;
1016 msgTimes = bpduTimes;
1017
1018 if (bpduRole == DesignatedPort) {
1019 // a)
1020 if (CMP(msgPriority, <, portPriority) // 1)
1021 || (CMP(msgPriority, ==, portPriority) // 2)
1022 && CMP(msgTimes, !=, portTimes)))
1023 return SuperiorDesignatedInfo;
1024
1025 // b)
1026 if (CMP(msgPriority, ==, portPriority) && CMP(msgTimes, ==, portTimes))
1027 return RepeatedDesignatedInfo;
1028
1029 // c)
1030 if (CMP(msgPriority, >, portPriority))
1031 return InferiorDesignatedInfo;
1032 }
1033
1034 if ((bpduRole == RootPort || bpduRole == AltBackupPort) &&
1035 CMP(msgPriority, >=, portPriority))
1036 return InferiorRootAlternateInfo;
1037
1038 return OtherInfo;
1039
1040}
1041
1042// 17.21.9
1043static void recordAgreement(PORT_ARGS_PROTO)
1044{
1045 if (rstpVersion && operPointToPointMAC && bpduAgreementFlag) {
1046 agreed = TRUE;
1047 proposing = FALSE;
1048 }
1049 else
1050 agreed = FALSE;
1051}
1052
1053// 17.21.10
1054// No one sets disputed in 802.1D-2004, but see below.
1055static void recordDispute(PORT_ARGS_PROTO)
1056{
1057 if (// bpduType == BPDUTypeRST && -- Implied since only RST has this flag
1058 bpduLearningFlag) {
1059 // DIFF: Going by 802.1Q-2005, and the remark at the end of
1060 // 17.29.3 Designated Port states, about setting disputed,
1061 // it looks like what we need is
1062
1063 disputed = TRUE;
1064 agreed = FALSE;
1065
1066 // and not, as written in 17.21.10,
1067 // agreed = TRUE;
1068 // proposing = FALSE;
1069 }
1070}
1071
1072// 17.21.11
1073static void recordProposal(PORT_ARGS_PROTO)
1074{
1075 if (bpduRole == DesignatedPort && bpduProposalFlag)
1076 proposed = TRUE;
1077}
1078
1079// 17.21.12
1080static void recordPriority(PORT_ARGS_PROTO)
1081{
1082 portPriority = msgPriority;
1083}
1084
1085#define MIN_COMPAT_HELLO_TIME 1
1086
1087// 17.21.13
1088static void recordTimes(PORT_ARGS_PROTO)
1089{
1090 portTimes = msgTimes;
1091 if (portTimes.hello_time < MIN_COMPAT_HELLO_TIME)
1092 portTimes.hello_time = MIN_COMPAT_HELLO_TIME;
1093}
1094
1095/* Per bridge */
1096// 17.21.14
1097static void setSyncTree(BRIDGE_ARGS_PROTO)
1098{
1099 ForAllPortDo(
1100 FPORT->_sync = TRUE;
1101 );
1102}
1103
1104// 17.21.15
1105static void setReRootTree(BRIDGE_ARGS_PROTO)
1106{
1107 ForAllPortDo(
1108 FPORT->_reRoot = TRUE;
1109 );
1110
1111}
1112
1113// 17.21.16
1114static void setSelectedTree(BRIDGE_ARGS_PROTO)
1115{
1116 if (ForAllPort(FPORT->_reselect == FALSE))
1117 ForAllPortDo(
1118 FPORT->_selected = TRUE;
1119 );
1120}
1121
1122// 17.21.17
1123static void setTcFlags(PORT_ARGS_PROTO)
1124{
1125 if (bpduType == BPDUTypeTCN)
1126 rcvdTcn = TRUE;
1127 else { // Only other types are Config and RST
1128 //if (bpduType == BPDUTypeConfig || bpduType == BPDUTypeRST) {
1129 if (bpduTcFlag)
1130 rcvdTc = TRUE;
1131 if (bpduTcAckFlag)
1132 rcvdTcAck = TRUE;
1133 }
1134}
1135
1136// 17.21.18
1137static void setTcPropTree(PORT_ARGS_PROTO)
1138{
1139 ForAllPortDo(
1140 if (FPORT != PORT) FPORT->_tcProp = TRUE;
1141 );
1142}
1143
1144/***** txConfig(), txRstp() and txTcn() *****/
1145
1146/* To decide how much of the RawBPDU to actually transmit */
1147#define offsetof(_TYPE, _MEMBER) __builtin_offsetof (_TYPE, _MEMBER)
1148//#define offsetof(_TYPE, _MEMBER) ((int)&((_TYPE *)0)->_MEMBER)
1149
1150/* To fill the times values in the BPDU in txConfig() and txRstp() */
1151
1152static void set_bpdu_time(uchar time[2], int t)
1153{
1154 time[0] = t;
1155 time[1] = 0;
1156}
1157
1158static void set_bpdu_times(RawBPDU *b, Times *t)
1159{
1160 set_bpdu_time(b->message_age, t->message_age);
1161 set_bpdu_time(b->max_age, t->max_age);
1162 set_bpdu_time(b->hello_time, t->hello_time);
1163 set_bpdu_time(b->forward_delay, t->forward_delay);
1164}
1165
1166// 17.21.19
1167static void txConfig(PORT_ARGS_PROTO)
1168{
1169 RawBPDU b;
1170
1171 b.protocol_id = STP_PROTOCOL_ID;
1172 b.version = 0;
1173 b.type = BPDUTypeConfig;
1174
1175 b.priority = designatedPriority;
1176
1177 int flags = 0;
1178 flags |= (tcWhile != 0) ? BPDU_FLAG_TC : 0;
1179 flags |= (tcAck) ? BPDU_FLAG_TC_ACK : 0;
1180 b.flags = flags;
1181
1182 set_bpdu_times(&b, &designatedTimes);
1183
1184 DEBUG("Sending Config BPDU\n");
1185 STP_OUT_tx_bpdu(PORT->user_ref, &b, offsetof(RawBPDU, CONFIG_END));
1186}
1187
1188// 17.21.20
1189static void txRstp(PORT_ARGS_PROTO)
1190{
1191 RawBPDU b;
1192
1193 b.protocol_id = STP_PROTOCOL_ID;
1194 b.version = 2;
1195 b.type = BPDUTypeRST;
1196
1197 b.priority = designatedPriority;
1198
1199 int flags = 0;
1200
1201 int r = role;
1202 if (r == AlternatePort || r == BackupPort)
1203 r = AltBackupPort;
1204
1205 flags |= BPDU_FLAG_ROLE(r);
1206
1207 flags |= agree ? BPDU_FLAG_AGREEMENT : 0;
1208 flags |= proposing ? BPDU_FLAG_PROPOSAL : 0;
1209 flags |= (tcWhile != 0) ? BPDU_FLAG_TC : 0;
1210 // tcAckFlag = 0;
1211 flags |= learning ? BPDU_FLAG_LEARNING : 0;
1212 flags |= forwarding ? BPDU_FLAG_FORWARDING : 0;
1213
1214 b.flags = flags;
1215
1216 set_bpdu_times(&b, &designatedTimes);
1217
1218 b.version1_len = 0;
1219
1220 DEBUG("Sending RST BPDU\n");
1221 STP_OUT_tx_bpdu(PORT->user_ref, &b, offsetof(RawBPDU, RST_END));
1222}
1223
1224// 17.21.21
1225static void txTcn(PORT_ARGS_PROTO)
1226{
1227 RawBPDU b;
1228
1229 b.protocol_id = STP_PROTOCOL_ID;
1230 b.version = 0;
1231 b.type = BPDUTypeTCN;
1232
1233 DEBUG("Sending TCN BPDU\n");
1234 STP_OUT_tx_bpdu(PORT->user_ref, &b, offsetof(RawBPDU, TCN_END));
1235}
1236
1237// 17.21.22
1238static void updtBPDUVersion(PORT_ARGS_PROTO)
1239{
1240 if ((bpduVersion == 0 || bpduVersion == 1)
1241 && (bpduType == BPDUTypeTCN || bpduType == BPDUTypeConfig))
1242 rcvdSTP = TRUE;
1243 if (bpduType == BPDUTypeRST)
1244 rcvdRSTP = TRUE;
1245}
1246
1247// 17.21.23
1248static void updtRcvdInfoWhile(PORT_ARGS_PROTO)
1249{
1250 if (portTimes.message_age + 1 <= portTimes.max_age)
1251 rcvdInfoWhile = 3*portTimes.hello_time;
1252 else
1253 rcvdInfoWhile = 0;
1254}
1255
1256// 17.21.24
1257static void updtRoleDisabledTree(BRIDGE_ARGS_PROTO)
1258{
1259 ForAllPortDo(
1260 FPORT->_selectedRole = DisabledPort;
1261 );
1262}
1263
1264// 17.21.25
1265static void updtRolesTree(BRIDGE_ARGS_PROTO)
1266{
1267 // Computes Spanning Tree Priority vectors (17.5, 17.6) and timer values
1268 /*
1269 Sets:
1270 bridge's rootTimes,
1271 designatedPriority for each port, designatedTimes for each Port
1272 For each port: selectedRole, possibly updtInfo
1273 */
1274
1275 // a), b)
1276 PriorityVector4 root_priority;
1277 PortId root_port_id;
1278 Port *root_port = NULL;
1279
1280 /* Initialize root_priority to computed BridgePriority = B:0:B:0:0 */
1281 ZERO(root_priority);
1282 ZERO(root_port_id);
1283 root_priority.root_bridge_id = BridgeIdentifier;
1284 root_priority.designated_bridge_id = BridgeIdentifier;
1285
1286 ForAllPortDo(Port *PORT = FPORT; // So we can use port var names directly
1287 if (infoIs == Received &&
1288 CMP(portPriority.designated_bridge_id, !=, BridgeIdentifier)
1289 ) {
1290 // a) - root path priority vector
1291 PriorityVector4 root_path_priority = portPriority;
1292 root_path_priority.root_path_cost =
1293 path_cost_add(root_path_priority.root_path_cost,
1294 PortPathCost);
1295 if (CMP(root_path_priority, <, root_priority)
1296 || (CMP(root_path_priority, ==, root_priority)
1297 && CMP(portId, <, root_port_id))) {
1298 root_priority = root_path_priority;
1299 root_port_id = portId;
1300 root_port = PORT;
1301 }
1302 }
1303 );
1304 // Now root_priority is the minimum of BridgePriority and
1305 // of all the root path priorities where designated bridge id is not our ID
1306 // b)
1307 rootPriority = root_priority;
1308 rootPortId = root_port_id;
1309
1310 // c)
1311 if (root_port != NULL) {
1312 rootTimes = root_port->_portTimes;
1313 rootTimes.message_age += 1; /* Rounded to whole second */
1314 }
1315 else
1316 rootTimes = BridgeTimes;
1317
1318
1319 ForAllPortDo(Port *PORT = FPORT;
1320 // d)
1321 designatedPriority.root_bridge_id =
1322 root_priority.root_bridge_id;
1323 designatedPriority.root_path_cost =
1324 root_priority.root_path_cost;
1325 designatedPriority.designated_bridge_id = BridgeIdentifier;
1326 designatedPriority.designated_port_id = portId;
1327
1328 // e)
1329 designatedTimes = rootTimes;
1330 designatedTimes.hello_time = BridgeTimes.hello_time;
1331 );
1332
1333
1334 ForAllPortDo(Port *PORT = FPORT;
1335 switch (infoIs) {
1336 case Disabled: // f)
1337 selectedRole = DisabledPort;
1338 break;
1339 case Aged: // g)
1340 updtInfo = TRUE;
1341 selectedRole = DesignatedPort;
1342 break;
1343 case Mine: // h)
1344 selectedRole = DesignatedPort;
1345 if (CMP(designatedPriority, !=, portPriority) ||
1346 // Maybe below should be root_port->_portTimes instead
1347 // of designatedTimes, going literally by h)?
1348 // designatedTimes same as that that except for hello_time
1349 CMP(designatedTimes, !=, portTimes))
1350 updtInfo = TRUE;
1351 break;
1352
1353 case Received:
1354 if (PORT == root_port) { // i)
1355 selectedRole = RootPort;
1356 updtInfo = FALSE;
1357 }
1358 // In j), k), l) standard says designated priority is (or
1359 // is not) _higher_ than the port priority
1360 // Seems like it should mean _better_, numerically
1361 // higher is worse. We assume it means better, i.e.
1362 // numerically lower
1363 else if (CMP(designatedPriority, >=, portPriority)) {
1364 if (CMP(portPriority.designated_bridge_id,
1365 !=, BridgeIdentifier)) { // j)
1366 selectedRole = AlternatePort;
1367 }
1368 else { // k)
1369 /* portPriority.designated_bridge_id is our bridge */
1370 selectedRole = BackupPort;
1371 }
1372 updtInfo = FALSE; // for j) and k)
1373 }
1374 else { // l)
1375 /* Not root port and designatedPriority < portPriority */
1376 selectedRole = DesignatedPort;
1377 updtInfo = TRUE;
1378 }
1379 break;
1380 default:
1381 ERROR("Unknown value for infoIs: %d\n", infoIs);
1382 }
1383 DEBUG("Selected Role Set to: %d\n", selectedRole);
1384 );
1385}
1386
1387
1388
1389/* Other procedures we need */
1390
1391/* Call this whenever we set fdbFlush to TRUE. This will flush the
1392 fdb data on the specfied port. */
1393static void do_fdbFlush(PORT_ARGS_PROTO)
1394{
1395 if (rstpVersion) {
1396 STP_OUT_port_fdb_flush(PORT->user_ref);
1397 fdbFlush = FALSE; /* done */
1398 }
1399 else {
1400 /* We need to reduce ageing time to FwdDelay for time FwdDelay */
1401 /* Let us just flush them right away.
1402 Bridge doesn't let us set ageing by port anyway */
1403 STP_OUT_port_fdb_flush(PORT->user_ref);
1404 fdbFlush = FALSE;
1405 }
1406}
1407
1408/**************************** STATE MACHINES *******************************/
1409
1410/*--------------- State machine diagrams ------------------------*/
1411
1412// 17.22 - Fig. 17-13
1413
1414#define STM_NAME PortTimers
1415
1416STATES(STN(ONE_SECOND), STN(TICK));
1417
1418#define dec(x) if (x) x = x - 1
1419
1420STM(PORT_ARGS_PROTO,
1421
1422 GC();
1423
1424 BG(
1425 TR(UCT, ONE_SECOND);
1426 );
1427
1428 ST(ONE_SECOND,
1429 tick = FALSE;
1430 ,
1431 TR(tick == TRUE, TICK);
1432 );
1433
1434 ST(TICK,
1435 dec(helloWhen);
1436 //dec(tcWhile);
1437 if (tcWhile) {
1438 set_tcWhile(tcWhile - 1);
1439 }
1440 dec(fdWhile);
1441 dec(rcvdInfoWhile);
1442 dec(rrWhile);
1443 dec(rbWhile);
1444 dec(mdelayWhile);
1445 dec(edgeDelayWhile);
1446 dec(txCount);
1447 ,
1448 TR(UCT, ONE_SECOND);
1449 );
1450
1451 );
1452
1453#undef STM_NAME
1454
1455/*------------------------------------------------------------*/
1456
1457// 17.23 - Fig. 17-14
1458
1459#define STM_NAME PortReceive
1460
1461STATES(STN(DISCARD), STN(RECEIVE));
1462
1463STM(PORT_ARGS_PROTO,
1464
1465 GC(
1466 TR((rcvdBpdu || (edgeDelayWhile != MigrateTime)) && !portEnabled,
1467 DISCARD);
1468 );
1469
1470 BG(
1471 TR(UCT, DISCARD);
1472 );
1473
1474 ST(DISCARD,
1475 rcvdBpdu = rcvdRSTP = rcvdSTP = FALSE;
1476 rcvdMsg = FALSE;
1477 edgeDelayWhile = MigrateTime;
1478 ,
1479 TR(rcvdBpdu && portEnabled, RECEIVE);
1480 );
1481
1482 ST(RECEIVE,
1483 updtBPDUVersion(PORT_ARGS);
1484 operEdge = rcvdBpdu = FALSE;
1485 rcvdMsg = TRUE;
1486 edgeDelayWhile = MigrateTime;
1487 ,
1488 TR(rcvdBpdu && portEnabled && !rcvdMsg, RECEIVE);
1489 );
1490
1491 );
1492
1493#undef STM_NAME
1494
1495/*------------------------------------------------------------*/
1496
1497// 17.24 - Fig. 17-15
1498
1499#define STM_NAME PortProtocolMigration
1500
1501STATES(STN(CHECKING_RSTP), STN(SELECTING_STP), STN(SENSING));
1502
1503STM(PORT_ARGS_PROTO,
1504
1505 GC();
1506
1507 BG(
1508 TR(UCT, CHECKING_RSTP);
1509 );
1510
1511 ST(CHECKING_RSTP,
1512 mcheck = FALSE;
1513 sendRSTP = (rstpVersion);
1514 mdelayWhile = MigrateTime;
1515 ,
1516 TR(mdelayWhile == 0, SENSING);
1517 TR((mdelayWhile != MigrateTime) && !portEnabled, CHECKING_RSTP);
1518 );
1519
1520 ST(SELECTING_STP,
1521 sendRSTP = FALSE;
1522 mdelayWhile = MigrateTime;
1523 ,
1524 TR(mdelayWhile == 0 || !portEnabled || mcheck, SENSING);
1525 );
1526
1527 ST(SENSING,
1528 rcvdRSTP = rcvdSTP = FALSE;
1529 ,
1530 TR(!portEnabled || mcheck || ((rstpVersion) && !sendRSTP && rcvdRSTP),
1531 CHECKING_RSTP);
1532 TR(sendRSTP && rcvdSTP, SELECTING_STP);
1533 );
1534
1535 );
1536
1537#undef STM_NAME
1538
1539/*------------------------------------------------------------*/
1540
1541// 17.25 - Fig. 17-16
1542
1543#define STM_NAME BridgeDetection
1544
1545STATES(STN(EDGE), STN(NOT_EDGE));
1546
1547STM(PORT_ARGS_PROTO,
1548
1549 GC();
1550
1551 BG(
1552 TR(AdminEdge, EDGE);
1553 TR(!AdminEdge, NOT_EDGE);
1554 );
1555
1556 ST(EDGE,
1557 operEdge = TRUE;
1558 ,
1559 TR((!portEnabled && !AdminEdge) || !operEdge, NOT_EDGE);
1560 );
1561
1562 ST(NOT_EDGE,
1563 operEdge = FALSE;
1564 ,
1565 TR((!portEnabled && AdminEdge) ||
1566 ((edgeDelayWhile == 0) && AutoEdge
1567 && sendRSTP /* CORR: sendRstp */ && proposing),
1568 EDGE);
1569 );
1570
1571 );
1572
1573#undef STM_NAME
1574
1575/*------------------------------------------------------------*/
1576
1577// 17.26 - Fig. 17-17
1578
1579#define xc selected && !updtInfo
1580#define xc2 newInfo && (txCount < TxHoldCount) && (helloWhen != 0)
1581
1582#define STM_NAME PortTransmit
1583
1584STATES(STN(TRANSMIT_INIT), STN(TRANSMIT_PERIODIC), STN(TRANSMIT_CONFIG),
1585 STN(TRANSMIT_TCN), STN(TRANSMIT_RSTP), STN(IDLE));
1586
1587STM(PORT_ARGS_PROTO,
1588
1589 GC();
1590
1591 BG(
1592 TR(UCT, TRANSMIT_INIT);
1593 );
1594
1595 ST(TRANSMIT_INIT,
1596 newInfo = TRUE;
1597 txCount = 0;
1598 ,
1599 TR(UCT, IDLE);
1600 );
1601
1602 ST(TRANSMIT_PERIODIC,
1603 newInfo = newInfo || (role == DesignatedPort ||
1604 (role == RootPort && (tcWhile != 0)));
1605 ,
1606 TR(UCT, IDLE);
1607 );
1608
1609 ST(TRANSMIT_CONFIG,
1610 newInfo = FALSE;
1611 txConfig(PORT_ARGS);
1612 txCount += 1;
1613 tcAck = FALSE;
1614 ,
1615 TR(UCT, IDLE);
1616 );
1617
1618 ST(TRANSMIT_TCN,
1619 newInfo = FALSE;
1620 txTcn(PORT_ARGS);
1621 txCount += 1;
1622 ,
1623 TR(UCT, IDLE);
1624 );
1625
1626 ST(TRANSMIT_RSTP,
1627 newInfo = FALSE;
1628 txRstp(PORT_ARGS);
1629 txCount += 1;
1630 tcAck = FALSE;
1631 ,
1632 TR(UCT, IDLE);
1633 );
1634
1635 ST(IDLE,
1636 helloWhen = HelloTime;
1637 ,
1638 TR(helloWhen == 0 && xc, TRANSMIT_PERIODIC);
1639 TR(sendRSTP && xc2 && xc, TRANSMIT_RSTP);
1640 TR(!sendRSTP && role == RootPort && xc2 && xc, TRANSMIT_TCN);
1641 TR(!sendRSTP && role == DesignatedPort && xc2 && xc, TRANSMIT_CONFIG);
1642 );
1643
1644 );
1645
1646#undef STM_NAME
1647
1648/*------------------------------------------------------------*/
1649
1650// 17.27 - Fig. 17-18
1651
1652#define STM_NAME PortInformation
1653
1654STATES(STN(DISABLED), STN(AGED), STN(UPDATE),
1655 STN(SUPERIOR_DESIGNATED), STN(REPEATED_DESIGNATED),
1656 STN(INFERIOR_DESIGNATED), STN(NOT_DESIGNATED),
1657 STN(OTHER), STN(CURRENT), STN(RECEIVE)
1658 );
1659
1660STM(PORT_ARGS_PROTO,
1661
1662 GC(
1663 TR(!portEnabled && (infoIs != Disabled), DISABLED);
1664 );
1665
1666 BG(
1667 TR(UCT, DISABLED);
1668 );
1669
1670 ST(DISABLED,
1671 rcvdMsg = FALSE;
1672 proposing = proposed = agree = agreed = FALSE;
1673 rcvdInfoWhile = 0;
1674 infoIs = Disabled; reselect = TRUE; selected = FALSE;
1675 ,
1676 TR(portEnabled, AGED);
1677 TR(rcvdMsg, DISABLED);
1678 );
1679
1680 ST(AGED,
1681 infoIs = Aged;
1682 reselect = TRUE; selected = FALSE;
1683 ,
1684 TR(selected && updtInfo, UPDATE);
1685 );
1686
1687 ST(UPDATE,
1688 proposing = proposed = FALSE;
1689 agreed = agreed && betterorsameInfo(PORT_ARGS);
1690 synced = synced && agreed;
1691 portPriority = designatedPriority;
1692 portTimes = designatedTimes;
1693 updtInfo = FALSE; infoIs = Mine; newInfo = TRUE;
1694 ,
1695 TR(UCT, CURRENT);
1696 );
1697
1698 ST(SUPERIOR_DESIGNATED,
1699 agreed = proposing = FALSE;
1700 recordProposal(PORT_ARGS);
1701 setTcFlags(PORT_ARGS);
1702 agreed = agreed && betterorsameInfo(PORT_ARGS);
1703 recordPriority(PORT_ARGS);
1704 recordTimes(PORT_ARGS);
1705 updtRcvdInfoWhile(PORT_ARGS);
1706 infoIs = Received; reselect = TRUE; selected = FALSE;
1707 rcvdMsg = FALSE;
1708 ,
1709 TR(UCT, CURRENT);
1710 );
1711
1712 ST(REPEATED_DESIGNATED,
1713 recordProposal(PORT_ARGS);
1714 setTcFlags(PORT_ARGS);
1715 updtRcvdInfoWhile(PORT_ARGS);
1716 rcvdMsg = FALSE;
1717 ,
1718 TR(UCT, CURRENT);
1719 );
1720
1721 ST(INFERIOR_DESIGNATED,
1722 recordDispute(PORT_ARGS);
1723 rcvdMsg = FALSE;
1724 ,
1725 TR(UCT, CURRENT);
1726 );
1727
1728 ST(NOT_DESIGNATED,
1729 recordAgreement(PORT_ARGS);
1730 setTcFlags(PORT_ARGS);
1731 rcvdMsg = FALSE;
1732 ,
1733 TR(UCT, CURRENT);
1734 );
1735
1736 ST(OTHER,
1737 rcvdMsg = FALSE;
1738 ,
1739 TR(UCT, CURRENT);
1740 );
1741
1742 ST(CURRENT,
1743 ,
1744 TR(selected && updtInfo, UPDATE);
1745 TR((infoIs == Received) && (rcvdInfoWhile == 0) &&
1746 !updtInfo && !rcvdMsg,
1747 AGED);
1748 TR(rcvdMsg && !updtInfo, RECEIVE);
1749 );
1750
1751 ST(RECEIVE,
1752 rcvdInfo = rcvInfo(PORT_ARGS);
1753 ,
1754 TR(rcvdInfo == SuperiorDesignatedInfo, SUPERIOR_DESIGNATED);
1755 TR(rcvdInfo == RepeatedDesignatedInfo, REPEATED_DESIGNATED);
1756 TR(rcvdInfo == InferiorDesignatedInfo, INFERIOR_DESIGNATED);
1757 TR(rcvdInfo == InferiorRootAlternateInfo, NOT_DESIGNATED);
1758 TR(rcvdInfo == OtherInfo, OTHER);
1759 );
1760
1761 );
1762
1763#undef STM_NAME
1764
1765/*------------------------------------------------------------*/
1766
1767// 17.28 - Fig. 17-19
1768
1769#define STM_NAME PortRoleSelection
1770
1771STATES(STN(INIT_BRIDGE), STN(ROLE_SELECTION));
1772
1773STM(BRIDGE_ARGS_PROTO,
1774
1775 GC();
1776
1777 BG(
1778 TR(UCT, INIT_BRIDGE);
1779 );
1780
1781 ST(INIT_BRIDGE,
1782 updtRoleDisabledTree(BRIDGE_ARGS);
1783 ,
1784 TR(UCT, ROLE_SELECTION);
1785 );
1786
1787 ST(ROLE_SELECTION,
1788 clearReselectTree(BRIDGE_ARGS);
1789 updtRolesTree(BRIDGE_ARGS);
1790 setSelectedTree(BRIDGE_ARGS);
1791 ,
1792 TR(!ForAllPort(!FPORT->_reselect), ROLE_SELECTION);
1793 );
1794
1795 );
1796
1797#undef STM_NAME
1798
1799/*------------------------------------------------------------*/
1800
1801// 17.29
1802// 17.29.1 - Fig. 17-20 Disabled Port role transitions
1803// 17.29.2 - Fig. 17-21 Root Port role transitions
1804// 17.29.3 - Fig. 17-22 Designated Port role transitions
1805// 17.29.4 - Fig. 17-23 Alternate and Backup Port role transitions
1806
1807#define STM_NAME PortRoleTransitions
1808
1809STATES(/* Disabled Port role transitions */
1810 STN(INIT_PORT), STN(DISABLE_PORT), STN(DISABLED_PORT),
1811 /* Root Port role transitions */
1812 STN(ROOT_PROPOSED), STN(ROOT_AGREED), STN(ROOT_SYNCED),
1813 STN(REROOT), STN(REROOTED),
1814 STN(ROOT_LEARN), STN(ROOT_FORWARD), STN(ROOT_PORT),
1815 /* Designated port role transitions */
1816 STN(DESIGNATED_PROPOSE), STN(DESIGNATED_SYNCED),
1817 STN(DESIGNATED_RETIRED), STN(DESIGNATED_DISCARD),
1818 STN(DESIGNATED_LEARN), STN(DESIGNATED_FORWARD), STN(DESIGNATED_PORT),
1819 /* Alternate and Backup Port role transitions */
1820 STN(BLOCK_PORT), STN(ALTERNATE_PROPOSED), STN(ALTERNATE_AGREED),
1821 STN(BACKUP_PORT), STN(ALTERNATE_PORT)
1822 );
1823
1824STM(PORT_ARGS_PROTO,
1825
1826 GC(
1827 // Let us put role != selectedRole first in the && of conditions
1828 // Since this will be false in the stable state, so condition check
1829 // will fail quicker.
1830
1831 /* Disabled Port role transitions */
1832 TR((role != selectedRole) && (selectedRole == DisabledPort) && xc,
1833 DISABLE_PORT);
1834 /* Root Port role transitions */
1835 TR((role != selectedRole) && (selectedRole == RootPort) && xc,
1836 ROOT_PORT);
1837 /* Designated port role transitions */
1838 TR((role != selectedRole) && (selectedRole == DesignatedPort) && xc,
1839 DESIGNATED_PORT);
1840 /* Alternate and Backup Port role transitions */
1841 TR((role !=selectedRole) && ((selectedRole == AlternatePort)
1842 || (selectedRole == BackupPort)) && xc,
1843 BLOCK_PORT);
1844 );
1845
1846
1847 /* Disabled Port role transitions */
1848
1849 BG(
1850 TR(UCT, INIT_PORT);
1851 );
1852
1853 ST(INIT_PORT,
1854 role = DisabledPort;
1855 learn = forward = FALSE;
1856 synced = FALSE;
1857 sync = reRoot = TRUE;
1858 rrWhile = FwdDelay;
1859 fdWhile= MaxAge;
1860 rbWhile = 0;
1861 ,
1862 TR(UCT, DISABLE_PORT);
1863 );
1864
1865 ST(DISABLE_PORT,
1866 role = selectedRole;
1867 learn = forward = FALSE;
1868 ,
1869 TR(!learning && !forwarding && xc, DISABLED_PORT);
1870 );
1871
1872 ST(DISABLED_PORT,
1873 fdWhile = MaxAge;
1874 synced = TRUE;
1875 rrWhile = 0;
1876 sync = reRoot = FALSE;
1877 ,
1878 TR((fdWhile != MaxAge || sync || reRoot || !synced) && xc,
1879 DISABLED_PORT);
1880 );
1881
1882 /* Root Port role transitions */
1883
1884 ST(ROOT_PROPOSED,
1885 setSyncTree(BRIDGE_ARGS);
1886 proposed = FALSE;
1887 ,
1888 TR(UCT, ROOT_PORT);
1889 );
1890
1891 ST(ROOT_AGREED,
1892 proposed = sync = FALSE;
1893 agree = TRUE;
1894 newInfo = TRUE;
1895 ,
1896 TR(UCT, ROOT_PORT);
1897 );
1898
1899 // This state is there in the 802.1Q-2005 state machine
1900 ST(ROOT_SYNCED,
1901 synced = TRUE;
1902 sync = FALSE;
1903 ,
1904 TR(UCT, ROOT_PORT);
1905 );
1906
1907 ST(REROOT,
1908 setReRootTree(BRIDGE_ARGS);
1909 ,
1910 TR(UCT, ROOT_PORT);
1911 );
1912
1913 ST(REROOTED,
1914 reRoot = FALSE;
1915 ,
1916 TR(UCT, ROOT_PORT);
1917 );
1918
1919 ST(ROOT_LEARN,
1920 fdWhile = forwardDelay;
1921 learn = TRUE;
1922 ,
1923 TR(UCT, ROOT_PORT);
1924 );
1925
1926 ST(ROOT_FORWARD,
1927 fdWhile = 0;
1928 forward = TRUE;
1929 ,
1930 TR(UCT, ROOT_PORT);
1931 );
1932
1933 ST(ROOT_PORT,
1934 role = RootPort;
1935 rrWhile = FwdDelay;
1936 ,
1937 TR(proposed && !agree && xc, ROOT_PROPOSED);
1938 TR(((allSynced && !agree) || (proposed && agree)) && xc, ROOT_AGREED);
1939 TR(((agreed && !synced) || (sync && synced)) && xc, ROOT_SYNCED);
1940 TR(!forward && !reRoot && xc, REROOT);
1941 TR(reRoot && forward && xc, REROOTED);
1942 TR((fdWhile == 0 ||
1943 ((reRooted && (rbWhile == 0)) && (rstpVersion))) && !learn && xc,
1944 ROOT_LEARN);
1945 TR((fdWhile == 0 || ((reRooted && (rbWhile == 0)) && (rstpVersion)))
1946 && learn && !forward && xc,
1947 ROOT_FORWARD);
1948 TR(rrWhile != FwdDelay && xc, ROOT_PORT);
1949 );
1950
1951
1952 /* Designated port role transitions */
1953
1954 ST(DESIGNATED_PROPOSE,
1955 proposing = TRUE;
1956 edgeDelayWhile = EdgeDelay;
1957 newInfo = TRUE;
1958 ,
1959 TR(UCT, DESIGNATED_PORT);
1960 );
1961
1962 ST(DESIGNATED_SYNCED,
1963 rrWhile = 0;
1964 synced = TRUE;
1965 sync = FALSE;
1966 ,
1967 TR(UCT, DESIGNATED_PORT);
1968 );
1969
1970 ST(DESIGNATED_RETIRED,
1971 reRoot = FALSE;
1972 ,
1973 TR(UCT, DESIGNATED_PORT);
1974 );
1975
1976 ST(DESIGNATED_DISCARD,
1977 learn = forward = disputed = FALSE;
1978 fdWhile = forwardDelay;
1979 ,
1980 TR(UCT, DESIGNATED_PORT);
1981 );
1982
1983 ST(DESIGNATED_LEARN,
1984 learn = TRUE;
1985 fdWhile = forwardDelay;
1986 ,
1987 TR(UCT, DESIGNATED_PORT);
1988 );
1989
1990 ST(DESIGNATED_FORWARD,
1991 forward = TRUE;
1992 fdWhile = 0;
1993 agreed = sendRSTP /* CORR: sendRstp */;
1994 ,
1995 TR(UCT, DESIGNATED_PORT);
1996 );
1997
1998 ST(DESIGNATED_PORT,
1999 role = DesignatedPort;
2000 ,
2001 TR(!forward && !agreed && !proposing && !operEdge && xc,
2002 DESIGNATED_PROPOSE);
2003 TR(((!learning && !forwarding && !synced)
2004 || (agreed && !synced) || (operEdge && !synced)
2005 || (sync && synced)) && xc,
2006 DESIGNATED_SYNCED);
2007 TR(rrWhile == 0 && reRoot && xc, DESIGNATED_RETIRED);
2008 TR(((sync && !synced) || (reRoot && (rrWhile != 0)) || disputed)
2009 && !operEdge && (learn || forward) && xc,
2010 DESIGNATED_DISCARD);
2011 TR(((fdWhile == 0) || agreed || operEdge)
2012 && ((rrWhile ==0) || !reRoot) && !sync && !learn && xc,
2013 DESIGNATED_LEARN);
2014 TR(((fdWhile == 0) || agreed || operEdge)
2015 && ((rrWhile ==0) || !reRoot) && !sync && (learn && !forward) && xc,
2016 DESIGNATED_FORWARD);
2017 );
2018
2019
2020 /* Alternate and Backup Port role transitions */
2021
2022 ST(BLOCK_PORT,
2023 role = selectedRole;
2024 learn = forward = FALSE;
2025 ,
2026 TR(!learning && !forwarding && xc, ALTERNATE_PORT);
2027 );
2028
2029 ST(ALTERNATE_PROPOSED,
2030 setSyncTree(BRIDGE_ARGS);
2031 proposed = FALSE;
2032 ,
2033 TR(UCT, ALTERNATE_PORT);
2034 );
2035
2036 ST(ALTERNATE_AGREED,
2037 proposed = FALSE;
2038 agree = TRUE;
2039 newInfo = TRUE;
2040 ,
2041 TR(UCT, ALTERNATE_PORT);
2042 );
2043
2044 ST(BACKUP_PORT,
2045 rbWhile = 2*HelloTime;
2046 ,
2047 TR(UCT, ALTERNATE_PORT);
2048 );
2049
2050 ST(ALTERNATE_PORT,
2051 // DIFF: std says FwdDelay, but that leads to infinite loop here
2052 // when sendRSTP is set, since FwdDelay != forwardDelay then
2053 // 802.1Q-2005 says forwardDelay here
2054 fdWhile = forwardDelay;
2055 synced = TRUE;
2056 rrWhile = 0;
2057 sync = reRoot = FALSE;
2058 ,
2059 TR(proposed && !agree && xc,
2060 ALTERNATE_PROPOSED);
2061 TR(((allSynced && !agree) || (proposed && agree)) && xc,
2062 ALTERNATE_AGREED);
2063 TR((rbWhile != 2*HelloTime) && (role == BackupPort) && xc,
2064 BACKUP_PORT);
2065 TR(((fdWhile != forwardDelay) || sync || reRoot || !synced) && xc,
2066 ALTERNATE_PORT);
2067 );
2068
2069 );
2070
2071#undef STM_NAME
2072
2073/*------------------------------------------------------------*/
2074
2075// 17.30 - Fig. 17-24
2076
2077#define STM_NAME PortStateTransition
2078
2079STATES(STN(DISCARDING), STN(LEARNING), STN(FORWARDING));
2080
2081STM(PORT_ARGS_PROTO,
2082
2083 GC();
2084
2085 BG(
2086 TR(UCT, DISCARDING);
2087 );
2088
2089 ST(DISCARDING,
2090 disableLearning(PORT_ARGS);
2091 learning = FALSE;
2092 disableForwarding(PORT_ARGS);
2093 forwarding = FALSE;
2094 ,
2095 TR(learn, LEARNING);
2096 );
2097
2098 ST(LEARNING,
2099 enableLearning(PORT_ARGS);
2100 learning = TRUE;
2101 ,
2102 TR(!learn, DISCARDING);
2103 TR(forward, FORWARDING);
2104 );
2105
2106 ST(FORWARDING,
2107 enableForwarding(PORT_ARGS);
2108 forwarding = TRUE;
2109 ,
2110 TR(!forward, DISCARDING);
2111 );
2112
2113 );
2114
2115#undef STM_NAME
2116
2117/*------------------------------------------------------------*/
2118
2119// 17.31 - Fig. 17-25
2120
2121#define STM_NAME TopologyChange
2122
2123STATES(STN(INACTIVE), STN(LEARNING), STN(DETECTED), STN(ACKNOWLEDGED),
2124 STN(PROPAGATING), STN(NOTIFIED_TC), STN(NOTIFIED_TCN), STN(ACTIVE));
2125
2126STM(PORT_ARGS_PROTO,
2127
2128 GC();
2129
2130 BG(
2131 TR(UCT, INACTIVE);
2132 );
2133
2134 ST(INACTIVE,
2135 fdbFlush = TRUE;
2136 do_fdbFlush(PORT_ARGS); /* We add this to trigger the fdb flush */
2137 //tcWhile = 0;
2138 set_tcWhile(0);
2139 tcAck = FALSE;
2140 ,
2141 TR(learn && !fdbFlush, LEARNING);
2142 );
2143
2144 ST(LEARNING,
2145 rcvdTc = rcvdTcn = rcvdTcAck = FALSE;
2146 rcvdTc = tcProp = FALSE;
2147 ,
2148 TR(((role == RootPort) || (role == DesignatedPort))
2149 && forward && !operEdge,
2150 DETECTED);
2151
2152 TR((role != RootPort) &&
2153 (role != DesignatedPort) &&
2154 !(learn || learning) &&
2155 !(rcvdTc || rcvdTcn || rcvdTcAck || tcProp),
2156 INACTIVE);
2157
2158 TR(rcvdTc ||
2159 rcvdTcn ||
2160 rcvdTcAck ||
2161 tcProp,
2162 LEARNING);
2163 );
2164
2165 ST(DETECTED,
2166 newTcWhile(PORT_ARGS);
2167 setTcPropTree(PORT_ARGS);
2168 newInfo = TRUE;
2169 ,
2170 TR(UCT, ACTIVE);
2171 );
2172
2173 ST(ACKNOWLEDGED,
2174 //tcWhile = 0;
2175 set_tcWhile(0);
2176 rcvdTcAck = FALSE;
2177 ,
2178 TR(UCT, ACTIVE);
2179 );
2180
2181 ST(PROPAGATING,
2182 newTcWhile(PORT_ARGS);
2183 fdbFlush = TRUE;
2184 do_fdbFlush(PORT_ARGS); /* We add this to trigger the fdb flush */
2185 tcProp = FALSE;
2186 ,
2187 TR(UCT, ACTIVE);
2188 );
2189
2190 ST(NOTIFIED_TC,
2191 rcvdTcn = rcvdTc = FALSE;
2192 if (role == DesignatedPort) tcAck = TRUE;
2193 /*
2194 Fig 17-25 has setTcPropBridge();
2195 Must mean setTcPropTree().
2196 The 802.1w-2001 standard has setTcPropBridge() defined to be the
2197 same thing as setTcPropTree() in 802.1D-2004, and also has
2198 setTcPropBridge() in both DETECTED and NOTIFIED_TC states.
2199 802.1D-2004 has setTcPropTree() in DETECTED state, but
2200 setTcPropBridge() in NOTIFIED_TC state.
2201 */
2202 setTcPropTree(PORT_ARGS); /* CORR: setTcPropBridge() */
2203 ,
2204 TR(UCT, ACTIVE);
2205 );
2206
2207 ST(NOTIFIED_TCN,
2208 newTcWhile(PORT_ARGS);
2209 ,
2210 TR(UCT, NOTIFIED_TC);
2211 );
2212
2213 ST(ACTIVE,
2214 ,
2215 TR(((role != RootPort) && (role != DesignatedPort) ) || operEdge,
2216 LEARNING);
2217 TR(rcvdTcAck, ACKNOWLEDGED);
2218 TR(tcProp && !operEdge, PROPAGATING);
2219 TR(rcvdTc, NOTIFIED_TC);
2220 TR(rcvdTcn, NOTIFIED_TCN);
2221 );
2222
2223 );
2224
2225#undef STM_NAME
2226
2227
2228/***** Running the state machines *****/
2229
2230static STM_FUNC_DECL((*bridge_stms[NUM_BRIDGE_STATE_MACHINES]),
2231 BRIDGE_ARGS_PROTO)
2232 = {
2233 STM_FUNC(PortRoleSelection),
2234 };
2235
2236static STM_FUNC_DECL((*port_stms[NUM_PORT_STATE_MACHINES]), PORT_ARGS_PROTO)
2237 = {
2238 STM_FUNC(PortTimers),
2239 STM_FUNC(PortReceive),
2240 STM_FUNC(PortProtocolMigration),
2241 STM_FUNC(BridgeDetection),
2242 STM_FUNC(PortTransmit),
2243 STM_FUNC(PortInformation),
2244 STM_FUNC(PortRoleTransitions),
2245 STM_FUNC(PortStateTransition),
2246 STM_FUNC(TopologyChange),
2247 };
2248
2249static void state_machines_run(BRIDGE_ARGS_PROTO)
2250{
2251 if (!BRIDGE->stp_on)
2252 return;
2253
2254 int i;
2255 bool transitioned;
2256 do {
2257 transitioned = FALSE;
2258 for (i = 0; i < NUM_BRIDGE_STATE_MACHINES; i++)
2259 STM_RUN(&BRIDGE->state[i], &transitioned, bridge_stms[i], BRIDGE_ARGS);
2260
2261 ForAllPortDo(
2262 for (i = 0; i < NUM_PORT_STATE_MACHINES; i++)
2263 STM_RUN(&FPORT->state[i], &transitioned,
2264 port_stms[i], FPORT_ARGS);
2265 );
2266 } while (transitioned);
2267}
2268
2269static void state_machines_begin(BRIDGE_ARGS_PROTO)
2270{
2271 if (!BRIDGE->stp_on)
2272 return;
2273
2274 int i;
2275 for (i = 0; i < NUM_BRIDGE_STATE_MACHINES; i++)
2276 STM_BEGIN(&BRIDGE->state[i], bridge_stms[i], BRIDGE_ARGS);
2277
2278 ForAllPortDo(
2279 for (i = 0; i < NUM_PORT_STATE_MACHINES; i++)
2280 STM_BEGIN(&FPORT->state[i], port_stms[i], FPORT_ARGS);
2281 );
2282
2283 /* Initialized. Now run them. */
2284 state_machines_run(BRIDGE_ARGS);
2285}
2286
2287void STP_IN_set_bridge_enable(Bridge *BRIDGE, unsigned int enabled)
2288{
2289 uint on = enabled?1:0;
2290 if (BRIDGE->stp_on == on)
2291 return;
2292
2293 BRIDGE->stp_on = on;
2294
2295 if (on)
2296 state_machines_begin(BRIDGE_ARGS);
2297 else {
2298
2299 }
2300}
2301
2302
2303/*
2304 PortPathCost and operPointToPointMAC can change through configuration
2305 as well as link notifications (when configured to auto).
2306 Common functions to handle these.
2307*/
2308
2309// 17.14 - Table 17-3, also NOTE 3
2310static uint compute_pcost(int speed)
2311{
2312 /* speed is in MB/s*/
2313 if (speed > 0)
2314 return speed < 20000000 ? 20000000/speed : 1;
2315 else
2316 return 200000000;
2317}
2318
2319/* Returns TRUE if PostPathCost changed */
2320static bool check_port_path_cost(PORT_ARGS_PROTO)
2321{
2322 int pcost;
2323 if (AdminPortPathCost == 0) {
2324 pcost = compute_pcost(PORT->speed);
2325 }
2326 else {
2327 pcost = AdminPortPathCost;
2328 }
2329
2330 if (PortPathCost != pcost) {
2331 PortPathCost = pcost;
2332 selected = FALSE; reselect = TRUE; // 17.13
2333 return TRUE;
2334 }
2335
2336 return FALSE;
2337}
2338
2339/* Returns TRUE if operPointToPointMAC changed */
2340static bool check_port_p2p(PORT_ARGS_PROTO)
2341{
2342 bool p2p;
2343 if (adminPointToPointMAC == P2PAuto) {
2344 p2p = !!PORT->duplex;
2345 }
2346 else {
2347 p2p = (adminPointToPointMAC == P2PForceTrue);
2348 }
2349
2350 if (operPointToPointMAC != p2p) {
2351 operPointToPointMAC = p2p;
2352 return TRUE;
2353 }
2354
2355 return FALSE;
2356}
2357
2358/******************** Event notifications ******************************/
2359
2360void STP_IN_one_second(BRIDGE_ARGS_PROTO)
2361{
2362 if (BRIDGE->tcWhile_count == 0)
2363 BRIDGE->time_since_tc++;
2364
2365 ForAllPortDo(
2366 FPORT->_tick = TRUE;
2367 );
2368 state_machines_run(BRIDGE_ARGS);
2369}
2370
2371void STP_IN_set_port_enable(Port *PORT, unsigned int enabled,
2372 unsigned int speed, unsigned int duplex)
2373{
2374 Bridge *BRIDGE = PORT->bridge;
2375
2376 bool changed = FALSE;
2377
2378 if (enabled) {
2379 if (!portEnabled) {
2380 portEnabled = TRUE;
2381 changed = TRUE;
2382 }
2383
2384 if (PORT->speed != speed) {
2385 PORT->speed = speed;
2386 if (check_port_path_cost(PORT_ARGS))
2387 changed = TRUE;
2388 }
2389
2390 if (PORT->duplex != duplex) {
2391 PORT->duplex = duplex;
2392 if (check_port_p2p(PORT_ARGS))
2393 changed = TRUE;
2394 }
2395 }
2396 else {
2397 if (portEnabled) {
2398 portEnabled = FALSE;
2399 changed = TRUE;
2400 }
2401 }
2402 if (changed)
2403 state_machines_run(BRIDGE_ARGS);
2404}
2405
2406/* Rounding up. What should we really be doing with fractional times? */
2407static uint get_bpdu_time(const uchar time[2])
2408{
2409 uint t = (time[0] << 8) + time[1];
2410 return (t + 0xff) >> 8;
2411}
2412
2413
2414void STP_IN_rx_bpdu(Port *PORT, const void *base, unsigned int len)
2415{
2416 Bridge *BRIDGE = PORT->bridge;
2417 const RawBPDU *p = base;
2418
2419 if (!BRIDGE->stp_on)
2420 return; // No action - else we might fail the next test
2421
2422 if (rcvdBpdu) {
2423 ABORT("Port hasn't processed old BPDU\n");
2424 return;
2425 }
2426
2427 // 9.3.4 - Validate
2428 if (len < 4)
2429 return;
2430 if (p->protocol_id != STP_PROTOCOL_ID)
2431 return;
2432
2433 DEBUG("Received BPDU of type %d\n", p->type);
2434
2435 if (// 9.3.4 a)
2436 (p->type == BPDUTypeConfig &&
2437 len >= 35 &&
2438 CMP(p->message_age, <, p->max_age) &&
2439 !(CMP(p->priority.designated_bridge_id.bridge_address, ==,
2440 BridgeIdentifier.bridge_address) &&
2441 (port_id_to_uint(p->priority.designated_port_id) & 0xfff) ==
2442 (port_id_to_uint(portId) & 0xfff)
2443 ))
2444 ||
2445 // 9.3.4 b)
2446 (p->type == BPDUTypeTCN)
2447 ||
2448 // 9.3.4 c)
2449 (p->type == BPDUTypeRST && len >= 36)
2450 ) {
2451 /* BPDU has been validated */
2452 }
2453 else {
2454 DEBUG("BPDU validation failed\n");
2455 return;
2456 }
2457
2458 // 9.3.4 d)
2459 bpduVersion = p->version <= 2 ? p->version : 2;
2460
2461 bpduType = p->type;
2462
2463 bpduTcFlag =
2464 (p->type != BPDUTypeTCN) ? ((p->flags & BPDU_FLAG_TC) != 0) : 0;
2465 bpduProposalFlag =
2466 (p->type == BPDUTypeRST) ? ((p->flags & BPDU_FLAG_PROPOSAL) != 0) : 0;
2467 bpduRole =
2468 (p->type == BPDUTypeRST) ? BPDU_FLAG_ROLE_GET(p->flags) : 0;
2469 bpduLearningFlag =
2470 (p->type == BPDUTypeRST) ? ((p->flags & BPDU_FLAG_LEARNING) != 0) : 0;
2471 bpduForwardingFlag =
2472 (p->type == BPDUTypeRST) ? ((p->flags & BPDU_FLAG_FORWARDING) != 0) : 0;
2473 bpduAgreementFlag =
2474 (p->type == BPDUTypeRST) ? ((p->flags & BPDU_FLAG_AGREEMENT) != 0) : 0;
2475 bpduTcAckFlag =
2476 (p->type != BPDUTypeTCN) ? ((p->flags & BPDU_FLAG_TC_ACK) != 0) : 0;
2477
2478 if (p->type == BPDUTypeTCN) {
2479 ZERO(bpduPriority);
2480 ZERO(bpduTimes);
2481
2482 }
2483 else {
2484 bpduPriority = p->priority;
2485
2486 bpduTimes.message_age = get_bpdu_time(p->message_age);
2487 bpduTimes.max_age = get_bpdu_time(p->max_age);
2488 bpduTimes.forward_delay = get_bpdu_time(p->forward_delay);
2489 bpduTimes.hello_time = get_bpdu_time(p->hello_time);
2490 }
2491
2492 rcvdBpdu = TRUE;
2493
2494 state_machines_run(BRIDGE_ARGS);
2495
2496}
2497
2498/************************** Configuration *************************/
2499
2500// 17.14
2501static int check_times(BRIDGE_ARGS_PROTO, int max_age, int fwd_delay)
2502{
2503 if (2*(fwd_delay - 1) < max_age) {
2504 ERROR("Configured BridgeTimes doesn't meet "
2505 "2 * (Bridge Foward Delay - 1) >= Bridge Max Age\n");
2506 return -1;
2507 }
2508
2509 // This condition never fails, with hello_time == 2 && max_age >= 6.
2510 // So no need to check.
2511 //if (max_age < 2*(BridgeHelloTime + 1)) {
2512 // ERROR("Doesn't meet Bridge Max Age >= 2 * (Bridge Hello Time + 1) ");
2513 // return -1;
2514 //}
2515
2516 return 0;
2517}
2518
2519// 14.8.1.2
2520int STP_IN_set_bridge_config(Bridge *BRIDGE, const STP_BridgeConfig *cfg)
2521{
2522 int r = 0;
2523 bool changed = FALSE, init = FALSE;
2524 /* First, validation */
2525
2526 if (cfg->set_bridge_protocol_version) {
2527 if (cfg->bridge_protocol_version != 0 && cfg->bridge_protocol_version != 2) {
2528 ERROR("Protocol version must be 0 or 2\n");
2529 r = -1;
2530 }
2531 }
2532
2533 /* No validation of bridge_address. It can be any 6 bytes */
2534
2535 if (cfg->set_bridge_priority) {
2536 // 17.14 - Table 17-2
2537 if (cfg->bridge_priority & ~0xf000) {
2538 ERROR("Bridge Priority restricted to 0-61440 in steps of 4096\n");
2539 r = -1;
2540 }
2541 }
2542
2543 Times new_times = BridgeTimes;
2544 bool set_times = FALSE;
2545
2546 if (cfg->set_bridge_hello_time) {
2547 // 17.14 - Table 17-1
2548 if (cfg->bridge_hello_time != BridgeHelloTime) {
2549 ERROR("Hello Time cannot be changed from 2s in RSTP\n");
2550 r = -1;
2551 }
2552 new_times.hello_time = cfg->bridge_hello_time;
2553 set_times = TRUE;
2554 }
2555
2556 if (cfg->set_bridge_max_age) {
2557 // 17.14 - Table 17-1
2558 if (cfg->bridge_max_age < 6 || cfg->bridge_max_age > 40) {
2559 ERROR("Bridge Max Age must be between 6 and 40\n");
2560 r = -1;
2561 }
2562 new_times.max_age = cfg->bridge_max_age;
2563 set_times = TRUE;
2564 }
2565
2566 if (cfg->set_bridge_forward_delay) {
2567 // 17.14 - Table 17-1
2568 if (cfg->bridge_forward_delay < 4 || cfg->bridge_forward_delay > 30) {
2569 ERROR("Bridge Forward Delay must be between 4 and 30\n");
2570 r = -1;
2571 }
2572 new_times.forward_delay = cfg->bridge_forward_delay;
2573 set_times = TRUE;
2574 }
2575
2576 if (check_times(BRIDGE_ARGS, new_times.max_age, new_times.forward_delay))
2577 r = -1;
2578
2579 if (cfg->set_bridge_tx_hold_count) {
2580 // 17.14 - Table 17-1
2581 if (cfg->bridge_tx_hold_count < 1 || cfg->bridge_tx_hold_count > 10) {
2582 ERROR("Transmit Hold Count must be between 1 and 10\n");
2583 r = -1;
2584 }
2585 }
2586
2587 /* If not valid, return error */
2588 if (r)
2589 return r;
2590
2591 /* Set things */
2592 if (cfg->set_bridge_protocol_version &&
2593 ForceProtocolVersion != cfg->bridge_protocol_version) {
2594
2595 ForceProtocolVersion = cfg->bridge_protocol_version;
2596
2597 changed = TRUE;
2598 // 17.13 a)
2599 init = TRUE;
2600 }
2601
2602 if (cfg->set_bridge_address &&
2603 CMP(BridgeIdentifier.bridge_address, !=, cfg->bridge_address)) {
2604
2605 CPY(BridgeIdentifier.bridge_address, cfg->bridge_address);
2606
2607 changed = TRUE;
2608 init = TRUE;
2609 }
2610
2611 if (cfg->set_bridge_priority &&
2612 ((BridgeIdentifierPriority[0] & 0xf0) << 8) != cfg->bridge_priority) {
2613
2614 BridgeIdentifierPriority[0] &= ~0xf0;
2615 BridgeIdentifierPriority[0] |= cfg->bridge_priority >> 8;
2616
2617 changed = TRUE;
2618 /* 802.1Q-2005, 12.8.1.3.4 c) (Management, setting bridge protocol params)
2619 says this should be done for any management change, so we do this below.
2620
2621 // 17.13 b)
2622 ForAllPortDo(
2623 FPORT->_selected = FALSE;
2624 FPORT->_reselect = TRUE;
2625 );
2626 */
2627 }
2628
2629 if (set_times &&
2630 CMP(new_times, !=, BridgeTimes)) {
2631
2632 BridgeTimes = new_times;
2633
2634 changed = TRUE;
2635 }
2636
2637 if (cfg->set_bridge_tx_hold_count &&
2638 TxHoldCount != cfg->bridge_tx_hold_count) {
2639
2640 TxHoldCount = cfg->bridge_tx_hold_count;
2641
2642 changed = TRUE;
2643 // 17.13 introduction
2644 ForAllPortDo(
2645 FPORT->_txCount = 0;
2646 );
2647 }
2648
2649 if (changed && BRIDGE->stp_on) {
2650 // Do this for any change.
2651 // Seems like 802.1Q-2005, 12.8.1.3.4 c) wants this.
2652 /*
2653 Otherwise we fail UNH rstp.op_D test 3.2 since when administratively
2654 setting BridgeForwardDelay, etc, the values don't propagate from
2655 rootTimes to designatedTimes immediately without this change.
2656
2657 */
2658 ForAllPortDo(
2659 FPORT->_selected = FALSE;
2660 FPORT->_reselect = TRUE;
2661 );
2662
2663 if (init)
2664 state_machines_begin(BRIDGE_ARGS);
2665 else
2666 state_machines_run(BRIDGE_ARGS);
2667 }
2668
2669 return 0;
2670}
2671
2672// 14.8.2.3
2673int STP_IN_set_port_config(Port *PORT, const STP_PortConfig *cfg)
2674{
2675 Bridge *BRIDGE = PORT->bridge;
2676
2677 int r = 0;
2678 bool changed = FALSE;
2679 /* First, validation */
2680
2681 if (cfg->set_port_priority) {
2682 // 17.14 - Table 17-2
2683 if (cfg->port_priority & ~0xf0) {
2684 ERROR("Port Priority restricted to 0-240 in steps of 16\n");
2685 r = -1;
2686 }
2687 }
2688
2689 if (cfg->set_port_pathcost) {
2690 // 17.14 - Note 3 and Table 17-3
2691 if (cfg->port_pathcost > 200000000) {
2692 ERROR("Port path cost restricted to 1-200,000,000 or 0 for auto\n");
2693 r = -1;
2694 }
2695 }
2696
2697 if (cfg->set_port_admin_edge) {
2698 if (cfg->port_admin_edge != 0 && cfg->port_admin_edge != 1) {
2699 ERROR("Admin Edge must be 0 or 1\n");
2700 r = -1;
2701 }
2702 }
2703
2704 if (cfg->set_port_auto_edge) {
2705 if (cfg->port_auto_edge != 0 && cfg->port_auto_edge != 1) {
2706 ERROR("Auto Edge must be 0 or 1\n");
2707 r = -1;
2708 }
2709 }
2710
2711 if (cfg->set_port_admin_p2p) {
2712 if (cfg->port_admin_p2p < 0 || cfg->port_admin_p2p > 2) {
2713 ERROR("Admin P2P must be "
2714 "0 (force false), 1 (force true), or 2 (auto)\n");
2715 r = -1;
2716 }
2717 }
2718
2719 /* If not valid, return error */
2720 if (r)
2721 return r;
2722
2723 /* Set things */
2724 if (cfg->set_port_priority &&
2725 (portId.port_id[0] & 0xf0) != cfg->port_priority) {
2726
2727 portId.port_id[0] &= ~0xf0;
2728 portId.port_id[0] |= cfg->port_priority;
2729
2730 changed = TRUE;
2731 // 17.13 c)
2732 selected = FALSE; reselect = TRUE;
2733 }
2734
2735 if (cfg->set_port_pathcost &&
2736 AdminPortPathCost != cfg->port_pathcost) {
2737
2738 AdminPortPathCost = cfg->port_pathcost;
2739
2740 if (check_port_path_cost(PORT_ARGS)) {
2741 /* PortPathCost was changed */
2742 changed = TRUE;
2743 // 17.13 d) - Done by check_port_path_cost()
2744 // selected = FALSE; reselect = TRUE;
2745 }
2746 }
2747
2748 if (cfg->set_port_admin_edge &&
2749 AdminEdge != cfg->port_admin_edge) {
2750
2751 AdminEdge = cfg->port_admin_edge;
2752
2753 changed = TRUE;
2754 }
2755
2756 if (cfg->set_port_auto_edge &&
2757 AutoEdge != cfg->port_auto_edge) {
2758
2759 AutoEdge = cfg->port_auto_edge;
2760
2761 changed = TRUE;
2762 }
2763
2764 if (cfg->set_port_admin_p2p &&
2765 adminPointToPointMAC != cfg->port_admin_p2p) {
2766
2767 adminPointToPointMAC = cfg->port_admin_p2p;
2768
2769 if (check_port_p2p(PORT_ARGS)) {
2770 /* operPointToPointMAC was changed */
2771 changed = TRUE;
2772 }
2773 }
2774
2775 if (changed)
2776 state_machines_run(BRIDGE_ARGS);
2777
2778 return 0;
2779}
2780
2781/* To make this #define work */
2782typedef STP_BridgeConfig STP_bridgeConfig;
2783typedef STP_PortConfig STP_portConfig;
2784
2785#define SET_CFG(_type, _arg, _field, _value) \
2786({ \
2787 STP_ ## _type ## Config cfg; \
2788 ZERO(cfg); \
2789 cfg._type ## _ ## _field = _value; \
2790 cfg. set_ ## _type ## _ ## _field = 1; \
2791 STP_IN_set_ ## _type ## _config(_arg, &cfg); \
2792})
2793
2794/* Single field Bridge config functions */
2795
2796int STP_IN_set_protocol_version(Bridge *BRIDGE, unsigned int version)
2797{
2798 return SET_CFG(bridge, BRIDGE, protocol_version, version);
2799}
2800
2801int STP_IN_set_bridge_address(Bridge *BRIDGE, const STP_MacAddress *addr)
2802{
2803 return SET_CFG(bridge, BRIDGE, address, *addr);
2804}
2805
2806int STP_IN_set_bridge_priority(Bridge *BRIDGE, unsigned int priority)
2807{
2808 return SET_CFG(bridge, BRIDGE, priority, priority);
2809}
2810
2811int STP_IN_set_bridge_hello_time(Bridge *BRIDGE, unsigned int hello_time)
2812{
2813 return SET_CFG(bridge, BRIDGE, hello_time, hello_time);
2814}
2815
2816int STP_IN_set_bridge_max_age(Bridge *BRIDGE, unsigned int max_age)
2817{
2818 return SET_CFG(bridge, BRIDGE, max_age, max_age);
2819}
2820
2821
2822int STP_IN_set_bridge_forward_delay(Bridge *BRIDGE, unsigned int fwd_delay)
2823{
2824 return SET_CFG(bridge, BRIDGE, forward_delay, fwd_delay);
2825}
2826
2827
2828int STP_IN_set_tx_hold_count(Bridge *BRIDGE, unsigned int count)
2829{
2830 return SET_CFG(bridge, BRIDGE, tx_hold_count, count);
2831}
2832
2833
2834/* Single field Port config functions */
2835
2836int STP_IN_set_port_priority(Port *PORT, unsigned int priority)
2837{
2838 return SET_CFG(port, PORT, priority, priority);
2839}
2840
2841int STP_IN_set_port_pathcost(Port *PORT, unsigned int pcost)
2842{
2843 return SET_CFG(port, PORT, pathcost, pcost);
2844}
2845
2846/* edge is 0 or 1 */
2847int STP_IN_set_port_admin_edge(Port *PORT, unsigned int edge)
2848{
2849 return SET_CFG(port, PORT, admin_edge, edge);
2850}
2851
2852int STP_IN_set_port_auto_edge(Port *PORT, unsigned int edge)
2853{
2854 return SET_CFG(port, PORT, auto_edge, edge);
2855}
2856
2857int STP_IN_set_port_admin_p2p(Port *PORT, unsigned int p2p)
2858{
2859 return SET_CFG(port, PORT, admin_p2p, p2p);
2860}
2861
2862/* Force migration check */
2863// 14.8.2.4
2864int STP_IN_port_mcheck(Port *PORT)
2865{
2866 Bridge *BRIDGE = PORT->bridge;
2867
2868 if (!BRIDGE->stp_on) {
2869 ERROR("Bridge is down\n");
2870 return -1;
2871 }
2872
2873 if (rstpVersion) {
2874 mcheck = TRUE;
2875
2876 state_machines_run(BRIDGE_ARGS);
2877 }
2878
2879 return 0;
2880}
2881
2882
2883/********************* Status ************************/
2884
2885// 14.8.1.1
2886void STP_IN_get_bridge_status(Bridge *BRIDGE, STP_BridgeStatus *status)
2887{
2888 CPY(status->bridge_id, BridgeIdentifier);
2889
2890 status->time_since_topology_change = BRIDGE->time_since_tc;
2891 status->topology_change_count = BRIDGE->tc_count;
2892 status->topology_change = BRIDGE->tcWhile_count?1:0;
2893
2894 CPY(status->designated_root, rootPriority.root_bridge_id);
2895
2896 status->root_path_cost = path_cost_to_uint(rootPriority.root_path_cost);
2897
2898 CPY(status->designated_bridge, rootPriority.designated_bridge_id);
2899
2900 status->root_port =
2901 ((rootPortId.port_id[0] & 0x0f) << 8) + rootPortId.port_id[1];
2902
2903 status->max_age = rootTimes.max_age;
2904 status->hello_time = rootTimes.hello_time;
2905 status->forward_delay = rootTimes.forward_delay;
2906
2907 status->bridge_max_age = BridgeTimes.max_age;
2908 status->bridge_hello_time = BridgeTimes.hello_time;
2909 status->bridge_forward_delay = BridgeTimes.forward_delay;
2910
2911 status->tx_hold_count = TxHoldCount;
2912
2913 status->protocol_version = ForceProtocolVersion;
2914
2915 status->enabled = BRIDGE->stp_on;
2916}
2917
2918// 14.8.2.1
2919void STP_IN_get_port_status(Port *PORT, STP_PortStatus *status)
2920{
2921 status->uptime = -1; // XXX: Don't know
2922
2923 status->state = PORT->port_state_flags;
2924 if (status->state & STP_PORT_STATE_FLAG_FORWARDING)
2925 status->state &= ~STP_PORT_STATE_FLAG_LEARNING;
2926
2927 status->id = port_id_to_uint(portId);
2928
2929 // admin_path_cost is not in 14.18.2.1 but it is the one piece of config
2930 // information missing there, so it is useful to have it.
2931 status->admin_path_cost = AdminPortPathCost;
2932 status->path_cost = PortPathCost;
2933
2934 CPY(status->designated_root, designatedPriority.root_bridge_id);
2935 status->designated_cost =
2936 path_cost_to_uint(designatedPriority.root_path_cost);
2937 CPY(status->designated_bridge, designatedPriority.designated_bridge_id);
2938 status->designated_port =
2939 port_id_to_uint(designatedPriority.designated_port_id);
2940
2941 status->tc_ack = tcAck;
2942 status->admin_edge_port = AdminEdge;
2943 status->oper_edge_port = operEdge;
2944 status->auto_edge_port = AutoEdge;
2945
2946 status->enabled = portEnabled;
2947
2948 status->admin_p2p = adminPointToPointMAC;
2949 status->oper_p2p = operPointToPointMAC;
2950}
2951
2952
2953/**************** Creating and deleting ********************/
2954
2955/* user_ref is a pointer that the STP part uses to call the system part
2956 Bridge is created as disabled. You need to enable it.
2957 */
2958Bridge *STP_IN_bridge_create(void *user_ref)
2959{
2960 Bridge *b = STP_OUT_mem_zalloc(sizeof(Bridge));
2961
2962 if (!b) {
2963 ERROR("Couldn't allocate memory for bridge\n");
2964 return NULL;
2965 }
2966
2967 b->user_ref = user_ref;
2968
2969 Bridge *BRIDGE = b;
2970
2971 /* Default configuration values */
2972 BridgeIdentifierPriority[0] = 0x80; // Default bridge priority = 32768
2973 MigrateTime = 3;
2974 BridgeHelloTime = 2;
2975 BridgeMaxAge = 20;
2976 BridgeForwardDelay = 15;
2977 TxHoldCount = 6;
2978 ForceProtocolVersion = 2;
2979
2980 return BRIDGE;
2981}
2982
2983/* All ports will be deleted, don't reference them */
2984void STP_IN_bridge_delete(Bridge *BRIDGE)
2985{
2986 /* Disable ports - for what it is worth */
2987 ForAllPortDo(
2988 FPORT->_portEnabled = FALSE;
2989 );
2990
2991 state_machines_run(BRIDGE_ARGS);
2992
2993 /* Delete all ports */
2994 while (BRIDGE->port_list)
2995 STP_IN_port_delete(BRIDGE->port_list);
2996
2997 STP_OUT_mem_free(BRIDGE);
2998}
2999
3000/* user_ref is a pointer that the STP part uses to call the system part
3001 Port is created as disabled. You need to enable it.
3002*/
3003Port *STP_IN_port_create(Bridge *BRIDGE,
3004 unsigned int port_number, void *user_ref)
3005{
3006 if (port_number == 0 || (port_number & ~0x0fff)) {
3007 ERROR("Port id should be in the range 1 - 1023\n");
3008 return NULL;
3009 }
3010
3011 Port *p = STP_OUT_mem_zalloc(sizeof(Port));
3012
3013 if (!p) {
3014 ERROR("Couldn't allocate memory for port\n");
3015 return NULL;
3016 }
3017
3018 p->user_ref = user_ref;
3019 p->bridge = BRIDGE;
3020
3021 p->port_next = BRIDGE->port_list;
3022 BRIDGE->port_list = p;
3023
3024 Port *PORT = p;
3025
3026 portId.port_id[0] = 0x80 | (port_number >> 8); // Default port priority = 128
3027 portId.port_id[1] = port_number & 0xff;
3028
3029 // Don't need zero assignments since we zalloc'ed
3030
3031 //AdminPathCost = 0;
3032
3033 adminPointToPointMAC = P2PAuto;
3034
3035 if (BRIDGE->stp_on) {
3036 /* Try and initialize some things */
3037 int i;
3038 for (i = 0; i < NUM_PORT_STATE_MACHINES; i++)
3039 STM_BEGIN(&PORT->state[i], port_stms[i], PORT_ARGS);
3040
3041 // selected = FALSE
3042 reselect = TRUE;
3043 }
3044
3045 return PORT;
3046}
3047
3048void STP_IN_port_delete(Port *PORT)
3049{
3050 Bridge *BRIDGE = PORT->bridge;
3051
3052 /* Disable */
3053 if (portEnabled) {
3054 portEnabled = FALSE;
3055 state_machines_run(BRIDGE_ARGS);
3056 }
3057
3058 /* Find position in list */
3059 Port **prev = &BRIDGE->port_list;
3060 while (*prev != PORT && *prev != NULL)
3061 prev = &(*prev)->port_next;
3062
3063 if (*prev == NULL)
3064 ABORT("port not in its bridge's list\n");
3065
3066 /* Remove from list */
3067 *prev = PORT->port_next;
3068
3069 STP_OUT_mem_free(PORT);
3070}
3071