1 /*****************************************************************************
2 Copyright (c) 2007 EMC Corporation.
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)
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
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.
18 The full GNU General Public License is included in this distribution in the
21 Authors: Srinivas Aji <Aji_Srinivas@emc.com>
23 ******************************************************************************/
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 *********************************************************************/
34 /* Macros to be used in the later parts */
37 #define NULL ((void *)0)
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), \
52 #define ZERO(_a) memset(&(_a), 0, sizeof(_a))
54 /* memcmp wrapper, validating size equality of _a and _b */
56 (memcpy(&(_a), &(_b), \
57 __builtin_choose_expr(sizeof(_a) == sizeof(_b), \
62 #define BRIDGE_ARGS_PROTO Bridge *BRIDGE
63 #define PORT_ARGS_PROTO Bridge *BRIDGE, Port *PORT
65 #define BRIDGE_ARGS BRIDGE
66 #define PORT_ARGS BRIDGE, PORT
67 #define FPORT_ARGS BRIDGE, FPORT
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.
74 struct _dummy_user_ref_struct
77 } _user_ref_var
= { .user_ref
= NULL
},
78 *BRIDGE
= &_user_ref_var
, *PORT
= &_user_ref_var
;
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; })
88 /***************** Macros for state machine descriptions ********************/
90 #define _CAT2(x, y) x ## _ ## y
92 #define _CAT(x, y) _CAT2(x, y)
96 #define _STR(x) __STR(x)
98 #define STN(_state) _CAT(STM_NAME, _state)
100 #define STM_LABEL(_state) _CAT(STM_NAME, label_ ## _state)
102 #define STM_FUNC(_name) _CAT(_name, func)
104 /* BEGIN is state 0 in all state machines */
105 #define STATES(_args...) enum _CAT(STM_NAME, states) { STN(BEGIN), _args }
107 /* Transition definition */
108 #define TR(_cond, _new_state) \
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); \
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
120 new_state is the state we last transitioned to, or 0 if no transition
123 #define GC(_transitions...) \
128 /* Transitions from BEGIN state */
129 #define BG(_transitions...) \
132 ABORT("%s: No where to go from BEGIN\n", _STR(STM_NAME)); \
135 /* State definition */
136 #define ST(_state, _actions, _transitions) \
143 goto global_conditions
145 #define STM_FUNC_DECL(_type, _args...) \
146 int _type(int state, int single_step, _args)
148 /* This just barely works. _args expansion includes commas */
149 //int STM_FUNC(STM_NAME)(int state, int single_step, _args)
151 #define STM(_args, _contents...) \
152 static STM_FUNC_DECL(STM_FUNC(STM_NAME), _args) \
158 ABORT("%s: Got unknown state %d\n", __func__, state); \
165 #define STM_RUN(_state, _transitioned, _func, _args...) \
167 int new_state = _func(*_state, 0/*no single step*/, _args); \
168 if (new_state > 0) { \
169 *_state = new_state; \
170 *_transitioned = TRUE; \
174 #define STM_BEGIN(_state, _func, _args...) \
176 *_state = _func(0/*BEGIN*/, 1/*single step*/, _args); \
179 /************ End of Macros for state machine descriptions ***********/
183 typedef unsigned char uchar
;
185 typedef unsigned int uint
;
187 typedef unsigned short uint16
;
191 typedef struct _BridgeId
{
192 uchar bridge_identifier_priority
[2];
193 uchar bridge_address
[6];
194 } __attribute__((packed
)) BridgeId
;
200 static inline uint
path_cost_to_uint(PathCost x
)
203 (x
.cost
[0] << 24) + (x
.cost
[1] << 16) + (x
.cost
[2] << 8) + (x
.cost
[3]);
208 static inline PathCost
path_cost_add(PathCost x
, uint y
)
211 (x
.cost
[0] << 24) + (x
.cost
[1] << 16) + (x
.cost
[2] << 8) + (x
.cost
[3]);
215 (z
>> 24), (z
>> 16) & 0xff, (z
>> 8) & 0xff, z
& 0xff
222 typedef struct _PortId
{
223 uchar port_id
[2]; /* 4 high bits of priority and 12 for Id */
224 } __attribute__((packed
)) PortId
;
226 static inline uint
port_id_to_uint(PortId p
)
228 return (p
.port_id
[0] << 8) + p
.port_id
[1];
231 typedef struct _PriorityVector
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
;
240 /* First 4 components of priority vector */
241 typedef struct _PriorityVector4
243 BridgeId root_bridge_id
;
244 PathCost root_path_cost
;
245 BridgeId designated_bridge_id
;
246 PortId designated_port_id
;
247 } __attribute__((packed
)) PriorityVector4
;
250 typedef struct _Times
258 typedef enum _RcvdInfo
{
259 SuperiorDesignatedInfo
,
260 RepeatedDesignatedInfo
,
261 InferiorDesignatedInfo
,
262 InferiorRootAlternateInfo
,
267 typedef enum _InfoType
{
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
287 typedef enum _BPDUType
{
293 /* Defining fields directly in struct _Port */
295 typedef struct _ParsedBPDU
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
309 PriorityVector4 bpdu_priority
;
315 #define STP_PROTOCOL_ID 0
317 typedef struct _RawBPDU
322 /* TCN has only upto this */
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
335 PriorityVector4 priority
;
337 uchar message_age
[2];
340 uchar forward_delay
[2];
341 /* End of Config BPDU */
345 /* End of RST BPDU */
348 } __attribute__((packed
)) RawBPDU
;
352 /* Values for adminPointToPointMAC */
353 typedef enum _AdminP2P
{
354 P2PForceFalse
= STP_ADMIN_P2P_FORCE_FALSE
,
355 P2PForceTrue
= STP_ADMIN_P2P_FORCE_TRUE
,
356 P2PAuto
= STP_ADMIN_P2P_AUTO
360 /* Number of per bridge and per port state machines. */
361 #define NUM_BRIDGE_STATE_MACHINES 1
362 #define NUM_PORT_STATE_MACHINES 9
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.
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
374 #define portvar PORT->_portvar
376 This way we keep the expressions in the state machines fairly close
377 to the way they appear in the standard's figures.
381 typedef STP_Port Port
;
382 typedef STP_Bridge Bridge
;
389 /* Administratively set variables */
392 // - Not defining - Never used.
393 // This is meant to control FDB, we don't deal with that here.
395 /*#define AgeingTime BRIDGE->_AgeingTime*/
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
405 // - Not defining - this is part of BridgeTimes below
406 /*int BridgeForwardDelay;*/
407 #define BridgeForwardDelay BridgeTimes.forward_delay
410 // - Not defining - this is part of BridgeTimes below
411 /*int BridgeHelloTime;*/
412 #define BridgeHelloTime BridgeTimes.hello_time
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
423 // - Not defining - this is part of BridgeTimes below
424 /*int BridgeMaxAge;*/
425 #define BridgeMaxAge BridgeTimes.max_age
429 #define MigrateTime BRIDGE->_MigrateTime
432 uint _TransmitHoldCount
;
434 #define TransmitHoldCount BRIDGE->_TransmitHoldCount
436 /* Other (not administrative) */
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
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
449 BridgeId _BridgeIdentifier
; /* Includes priority */
450 #define BridgeIdentifier BRIDGE->_BridgeIdentifier
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*/
459 // This holds BridgeForwardDelay, BridgeHelloTime, BridgeMaxAge
460 // in .forward_delay, .hello_time, .max_age
463 #define BridgeTimes BRIDGE->_BridgeTimes
466 // 5th component of root priority vector
468 #define rootPortId BRIDGE->_rootPortId
471 // first 4 components of root priority vector
472 PriorityVector4 _rootPriority
;
473 #define rootPriority BRIDGE->_rootPriority
476 /* operational times got from portTimes for root port, or from BridgeTimes */
478 #define rootTimes BRIDGE->_rootTimes
484 uint tcWhile_count
; /* How many port tcWhile's are nonzero */
486 /* Our stuff - not from standard */
491 int state
[NUM_BRIDGE_STATE_MACHINES
];
500 /* Administrative variables: */
504 #define AdminEdgePort PORT->_AdminEdgePort
508 #define AutoEdgePort PORT->_AutoEdgePort
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*/
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
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
534 AdminP2P _adminPointToPointMAC
;
535 #define adminPointToPointMAC PORT->_adminPointToPointMAC
537 bool _operPointToPointMAC
;
538 #define operPointToPointMAC PORT->_operPointToPointMAC
543 uint _edgeDelayWhile
;
544 #define edgeDelayWhile PORT->_edgeDelayWhile
548 #define fdWhile PORT->_fdWhile
552 #define helloWhen PORT->_helloWhen
556 #define mdelayWhile PORT->_mdelayWhile
560 #define rbWhile PORT->_rbWhile
564 #define rcvdInfoWhile PORT->_rcvdInfoWhile
568 #define rrWhile PORT->_rrWhile
572 #define tcWhile PORT->_tcWhile
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.
579 /*#define ageingTime PORT->_ageingTime*/
583 #define agree PORT->_agree
587 #define agreed PORT->_agreed
590 // First 4 components of designated priority vector
591 // 5th component is portId
592 PriorityVector4 _designatedPriority
;
593 #define designatedPriority PORT->_designatedPriority
596 Times _designatedTimes
;
597 #define designatedTimes PORT->_designatedTimes
601 #define disputed PORT->_disputed
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
611 #define fdbFlush PORT->_fdbFlush
615 #define forward PORT->_forward
618 // Setting this will involve and OUT action
620 #define forwarding PORT->_forwarding
623 // Indicates origin/state of portInfo held for the port
625 #define infoIs PORT->_infoIs
629 #define learn PORT->_learn
633 #define learning PORT->_learning
637 #define mcheck PORT->_mcheck
640 // First 4 components of priority vector from received BPDU
641 PriorityVector4 _msgPriority
;
642 #define msgPriority PORT->_msgPriority
645 // Times from received BPDU
647 #define msgTimes PORT->_msgTimes
650 /* Set if a BPDU is to be transmitted,
651 reset by Port Transmit state machine */
653 #define newInfo PORT->_newInfo
657 #define operEdge PORT->_operEdge
661 #define portEnabled PORT->_portEnabled
665 #define portId PORT->_portId
669 #define PortPathCost PORT->_PortPathCost
672 // First 4 components of port's priority vector
673 PriorityVector4 _portPriority
;
674 #define portPriority PORT->_portPriority
677 /* Ports timer parameter values */
679 #define portTimes PORT->_portTimes
683 #define proposed PORT->_proposed
687 #define proposing PORT->_proposing
690 /* Set by system when Config, TCN, or RST BPDU is received
691 - IN action sets this */
693 But all other references in the doc use rcvdBpdu, so using that. */
695 #define rcvdBpdu PORT->_rcvdBpdu
698 /* Result of rcvInfo() procedure */
700 #define rcvdInfo PORT->_rcvdInfo
704 #define rcvdMsg PORT->_rcvdMsg
708 #define rcvdRSTP PORT->_rcvdRSTP
712 #define rcvdSTP PORT->_rcvdSTP
716 #define rcvdTc PORT->_rcvdTc
720 #define rcvdTcAck PORT->_rcvdTcAck
724 #define rcvdTcn PORT->_rcvdTcn
728 #define reRoot PORT->_reRoot
732 #define reselect PORT->_reselect
736 #define role PORT->_role
740 #define selected PORT->_selected
744 #define selectedRole PORT->_selectedRole
748 #define sendRSTP PORT->_sendRSTP
752 #define sync PORT->_sync
756 #define synced PORT->_synced
760 #define tcAck PORT->_tcAck
764 #define tcProp PORT->_tcProp
768 #define tick PORT->_tick
772 #define txCount PORT->_txCount
776 #define updtInfo PORT->_updtInfo
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
783 #define bpduVersion PORT->_bpduVersion
785 #define bpduType PORT->_bpduType
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
804 #define bpduTimes PORT->_bpduTimes
809 /* Our stuff - not from standard */
815 uint port_state_flags
;
818 int state
[NUM_PORT_STATE_MACHINES
];
822 /* Macros for iterating over all ports */
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) \
832 for (FPORT = BRIDGE->port_list; FPORT != NULL; FPORT = FPORT->port_next) \
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...) \
847 for (FPORT = BRIDGE->port_list; FPORT != NULL; FPORT = FPORT->port_next) \
851 // 17.20 ----- conditions and paramaters
854 #define AdminEdge AdminEdgePort
857 #define AutoEdge AutoEdgePort
860 #define allSynced ForAllPort(FPORT->_synced || FPORT->_role == RootPort)
863 #define EdgeDelay (operPointToPointMAC?MigrateTime:MaxAge)
866 #define forwardDelay (sendRSTP?HelloTime:FwdDelay)
869 #define FwdDelay designatedTimes.forward_delay
872 #define HelloTime designatedTimes.hello_time
875 #define MaxAge designatedTimes.max_age
878 // #define MigrateTime MigrateTime
881 #define reRooted ForAllPort(FPORT == PORT || FPORT->_rrWhile == 0)
884 #define rstpVersion (ForceProtocolVersion >= 2)
887 #define stpVersion (ForceProtocolVersion < 2)
890 #define TxHoldCount TransmitHoldCount
892 // 17.21 ---------- Procedures ------------------------
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
897 static bool betterorsameInfo(PORT_ARGS_PROTO
/*, InfoType newInfoIs*/)
900 (/*newInfoIs == Received &&*/ infoIs
== Received
&&
901 CMP(msgPriority
, <=, portPriority
)) ||
902 (/*newInfoIs == Mine &&*/ infoIs
== Mine
&&
903 CMP(designatedPriority
, <=, portPriority
));
907 static void clearReselectTree(BRIDGE_ARGS_PROTO
)
910 FPORT
->_reselect
= FALSE
;
915 static void disableForwarding(PORT_ARGS_PROTO
)
917 /* Cause forwarding process to stop forwarding frames through this port. */
918 /* Complete before returning */
920 PORT
->port_state_flags
&= ~STP_PORT_STATE_FLAG_FORWARDING
;
921 STP_OUT_port_set_state(PORT
->user_ref
, PORT
->port_state_flags
);
925 static void disableLearning(PORT_ARGS_PROTO
)
927 /* Cause port to stop learning process */
928 /* Complete before returning */
930 PORT
->port_state_flags
&= ~STP_PORT_STATE_FLAG_LEARNING
;
931 STP_OUT_port_set_state(PORT
->user_ref
, PORT
->port_state_flags
);
935 static void enableForwarding(PORT_ARGS_PROTO
)
937 /* Cause port to start forwarding. */
938 /* Complete before returning */
940 PORT
->port_state_flags
|= STP_PORT_STATE_FLAG_FORWARDING
;
941 STP_OUT_port_set_state(PORT
->user_ref
, PORT
->port_state_flags
);
945 static void enableLearning(PORT_ARGS_PROTO
)
947 /* Cause port to start learning. */
948 /* Complete before returning */
950 PORT
->port_state_flags
|= STP_PORT_STATE_FLAG_LEARNING
;
951 STP_OUT_port_set_state(PORT
->user_ref
, PORT
->port_state_flags
);
954 /**** Topology change tracking and reporting ****/
956 // change is +1 if a tcWhile became non-zero, -1 if a tcWhile became 0.
957 static inline void update_tcWhile_count(BRIDGE_ARGS_PROTO
, int change
)
962 if (BRIDGE
->tcWhile_count
== 0) {
963 /* Becoming nonzero from zero */
965 BRIDGE
->time_since_tc
= 0;
968 BRIDGE
->tcWhile_count
+= change
;
971 #define set_tcWhile(_val) \
972 ({ uint _oldval = tcWhile; tcWhile = _val; \
973 update_tcWhile_count(BRIDGE_ARGS, (tcWhile?1:0) - (_oldval?1:0)); })
977 static void newTcWhile(PORT_ARGS_PROTO
)
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);
989 //tcWhile = rootTimes.max_age + rootTimes.forward_delay;
990 set_tcWhile(rootTimes
.max_age
+ rootTimes
.forward_delay
);
996 static RcvdInfo
rcvInfo(PORT_ARGS_PROTO
)
999 if (bpduType
== BPDUTypeTCN
) {
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
1011 /* Note: config BPDU conveys Designated Port Role */
1012 if (bpduType
== BPDUTypeConfig
)
1013 bpduRole
= DesignatedPort
;
1015 msgPriority
= bpduPriority
;
1016 msgTimes
= bpduTimes
;
1018 if (bpduRole
== DesignatedPort
) {
1020 if (CMP(msgPriority
, <, portPriority
) // 1)
1021 || (CMP(msgPriority
, ==, portPriority
) // 2)
1022 && CMP(msgTimes
, !=, portTimes
)))
1023 return SuperiorDesignatedInfo
;
1026 if (CMP(msgPriority
, ==, portPriority
) && CMP(msgTimes
, ==, portTimes
))
1027 return RepeatedDesignatedInfo
;
1030 if (CMP(msgPriority
, >, portPriority
))
1031 return InferiorDesignatedInfo
;
1034 if ((bpduRole
== RootPort
|| bpduRole
== AltBackupPort
) &&
1035 CMP(msgPriority
, >=, portPriority
))
1036 return InferiorRootAlternateInfo
;
1043 static void recordAgreement(PORT_ARGS_PROTO
)
1045 if (rstpVersion
&& operPointToPointMAC
&& bpduAgreementFlag
) {
1054 // No one sets disputed in 802.1D-2004, but see below.
1055 static void recordDispute(PORT_ARGS_PROTO
)
1057 if (// bpduType == BPDUTypeRST && -- Implied since only RST has this flag
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
1066 // and not, as written in 17.21.10,
1068 // proposing = FALSE;
1073 static void recordProposal(PORT_ARGS_PROTO
)
1075 if (bpduRole
== DesignatedPort
&& bpduProposalFlag
)
1080 static void recordPriority(PORT_ARGS_PROTO
)
1082 portPriority
= msgPriority
;
1085 #define MIN_COMPAT_HELLO_TIME 1
1088 static void recordTimes(PORT_ARGS_PROTO
)
1090 portTimes
= msgTimes
;
1091 if (portTimes
.hello_time
< MIN_COMPAT_HELLO_TIME
)
1092 portTimes
.hello_time
= MIN_COMPAT_HELLO_TIME
;
1097 static void setSyncTree(BRIDGE_ARGS_PROTO
)
1100 FPORT
->_sync
= TRUE
;
1105 static void setReRootTree(BRIDGE_ARGS_PROTO
)
1108 FPORT
->_reRoot
= TRUE
;
1114 static void setSelectedTree(BRIDGE_ARGS_PROTO
)
1116 if (ForAllPort(FPORT
->_reselect
== FALSE
))
1118 FPORT
->_selected
= TRUE
;
1123 static void setTcFlags(PORT_ARGS_PROTO
)
1125 if (bpduType
== BPDUTypeTCN
)
1127 else { // Only other types are Config and RST
1128 //if (bpduType == BPDUTypeConfig || bpduType == BPDUTypeRST) {
1137 static void setTcPropTree(PORT_ARGS_PROTO
)
1140 if (FPORT
!= PORT
) FPORT
->_tcProp
= TRUE
;
1144 /***** txConfig(), txRstp() and txTcn() *****/
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)
1150 /* To fill the times values in the BPDU in txConfig() and txRstp() */
1152 static void set_bpdu_time(uchar time
[2], int t
)
1158 static void set_bpdu_times(RawBPDU
*b
, Times
*t
)
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
);
1167 static void txConfig(PORT_ARGS_PROTO
)
1171 b
.protocol_id
= STP_PROTOCOL_ID
;
1173 b
.type
= BPDUTypeConfig
;
1175 b
.priority
= designatedPriority
;
1178 flags
|= (tcWhile
!= 0) ? BPDU_FLAG_TC
: 0;
1179 flags
|= (tcAck
) ? BPDU_FLAG_TC_ACK
: 0;
1182 set_bpdu_times(&b
, &designatedTimes
);
1184 DEBUG("Sending Config BPDU\n");
1185 STP_OUT_tx_bpdu(PORT
->user_ref
, &b
, offsetof(RawBPDU
, CONFIG_END
));
1189 static void txRstp(PORT_ARGS_PROTO
)
1193 b
.protocol_id
= STP_PROTOCOL_ID
;
1195 b
.type
= BPDUTypeRST
;
1197 b
.priority
= designatedPriority
;
1202 if (r
== AlternatePort
|| r
== BackupPort
)
1205 flags
|= BPDU_FLAG_ROLE(r
);
1207 flags
|= agree
? BPDU_FLAG_AGREEMENT
: 0;
1208 flags
|= proposing
? BPDU_FLAG_PROPOSAL
: 0;
1209 flags
|= (tcWhile
!= 0) ? BPDU_FLAG_TC
: 0;
1211 flags
|= learning
? BPDU_FLAG_LEARNING
: 0;
1212 flags
|= forwarding
? BPDU_FLAG_FORWARDING
: 0;
1216 set_bpdu_times(&b
, &designatedTimes
);
1220 DEBUG("Sending RST BPDU\n");
1221 STP_OUT_tx_bpdu(PORT
->user_ref
, &b
, offsetof(RawBPDU
, RST_END
));
1225 static void txTcn(PORT_ARGS_PROTO
)
1229 b
.protocol_id
= STP_PROTOCOL_ID
;
1231 b
.type
= BPDUTypeTCN
;
1233 DEBUG("Sending TCN BPDU\n");
1234 STP_OUT_tx_bpdu(PORT
->user_ref
, &b
, offsetof(RawBPDU
, TCN_END
));
1238 static void updtBPDUVersion(PORT_ARGS_PROTO
)
1240 if ((bpduVersion
== 0 || bpduVersion
== 1)
1241 && (bpduType
== BPDUTypeTCN
|| bpduType
== BPDUTypeConfig
))
1243 if (bpduType
== BPDUTypeRST
)
1248 static void updtRcvdInfoWhile(PORT_ARGS_PROTO
)
1250 if (portTimes
.message_age
+ 1 <= portTimes
.max_age
)
1251 rcvdInfoWhile
= 3*portTimes
.hello_time
;
1257 static void updtRoleDisabledTree(BRIDGE_ARGS_PROTO
)
1260 FPORT
->_selectedRole
= DisabledPort
;
1265 static void updtRolesTree(BRIDGE_ARGS_PROTO
)
1267 // Computes Spanning Tree Priority vectors (17.5, 17.6) and timer values
1271 designatedPriority for each port, designatedTimes for each Port
1272 For each port: selectedRole, possibly updtInfo
1276 PriorityVector4 root_priority
;
1277 PortId root_port_id
;
1278 Port
*root_port
= NULL
;
1280 /* Initialize root_priority to computed BridgePriority = B:0:B:0:0 */
1281 ZERO(root_priority
);
1283 root_priority
.root_bridge_id
= BridgeIdentifier
;
1284 root_priority
.designated_bridge_id
= BridgeIdentifier
;
1286 ForAllPortDo(Port
*PORT
= FPORT
; // So we can use port var names directly
1287 if (infoIs
== Received
&&
1288 CMP(portPriority
.designated_bridge_id
, !=, BridgeIdentifier
)
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
,
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
;
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
1307 rootPriority
= root_priority
;
1308 rootPortId
= root_port_id
;
1311 if (root_port
!= NULL
) {
1312 rootTimes
= root_port
->_portTimes
;
1313 rootTimes
.message_age
+= 1; /* Rounded to whole second */
1316 rootTimes
= BridgeTimes
;
1319 ForAllPortDo(Port
*PORT
= FPORT
;
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
;
1329 designatedTimes
= rootTimes
;
1330 designatedTimes
.hello_time
= BridgeTimes
.hello_time
;
1334 ForAllPortDo(Port
*PORT
= FPORT
;
1336 case Disabled
: // f)
1337 selectedRole
= DisabledPort
;
1341 selectedRole
= DesignatedPort
;
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
))
1354 if (PORT
== root_port
) { // i)
1355 selectedRole
= RootPort
;
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
;
1369 /* portPriority.designated_bridge_id is our bridge */
1370 selectedRole
= BackupPort
;
1372 updtInfo
= FALSE
; // for j) and k)
1375 /* Not root port and designatedPriority < portPriority */
1376 selectedRole
= DesignatedPort
;
1381 ERROR("Unknown value for infoIs: %d\n", infoIs
);
1383 DEBUG("Selected Role Set to: %d\n", selectedRole
);
1389 /* Other procedures we need */
1391 /* Call this whenever we set fdbFlush to TRUE. This will flush the
1392 fdb data on the specfied port. */
1393 static void do_fdbFlush(PORT_ARGS_PROTO
)
1396 STP_OUT_port_fdb_flush(PORT
->user_ref
);
1397 fdbFlush
= FALSE
; /* done */
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
);
1408 /**************************** STATE MACHINES *******************************/
1410 /*--------------- State machine diagrams ------------------------*/
1412 // 17.22 - Fig. 17-13
1414 #define STM_NAME PortTimers
1416 STATES(STN(ONE_SECOND
), STN(TICK
));
1418 #define dec(x) if (x) x = x - 1
1420 STM(PORT_ARGS_PROTO
,
1425 TR(UCT
, ONE_SECOND
);
1431 TR(tick
== TRUE
, TICK
);
1438 set_tcWhile(tcWhile
- 1);
1445 dec(edgeDelayWhile
);
1448 TR(UCT
, ONE_SECOND
);
1455 /*------------------------------------------------------------*/
1457 // 17.23 - Fig. 17-14
1459 #define STM_NAME PortReceive
1461 STATES(STN(DISCARD
), STN(RECEIVE
));
1463 STM(PORT_ARGS_PROTO
,
1466 TR((rcvdBpdu
|| (edgeDelayWhile
!= MigrateTime
)) && !portEnabled
,
1475 rcvdBpdu
= rcvdRSTP
= rcvdSTP
= FALSE
;
1477 edgeDelayWhile
= MigrateTime
;
1479 TR(rcvdBpdu
&& portEnabled
, RECEIVE
);
1483 updtBPDUVersion(PORT_ARGS
);
1484 operEdge
= rcvdBpdu
= FALSE
;
1486 edgeDelayWhile
= MigrateTime
;
1488 TR(rcvdBpdu
&& portEnabled
&& !rcvdMsg
, RECEIVE
);
1495 /*------------------------------------------------------------*/
1497 // 17.24 - Fig. 17-15
1499 #define STM_NAME PortProtocolMigration
1501 STATES(STN(CHECKING_RSTP
), STN(SELECTING_STP
), STN(SENSING
));
1503 STM(PORT_ARGS_PROTO
,
1508 TR(UCT
, CHECKING_RSTP
);
1513 sendRSTP
= (rstpVersion
);
1514 mdelayWhile
= MigrateTime
;
1516 TR(mdelayWhile
== 0, SENSING
);
1517 TR((mdelayWhile
!= MigrateTime
) && !portEnabled
, CHECKING_RSTP
);
1522 mdelayWhile
= MigrateTime
;
1524 TR(mdelayWhile
== 0 || !portEnabled
|| mcheck
, SENSING
);
1528 rcvdRSTP
= rcvdSTP
= FALSE
;
1530 TR(!portEnabled
|| mcheck
|| ((rstpVersion
) && !sendRSTP
&& rcvdRSTP
),
1532 TR(sendRSTP
&& rcvdSTP
, SELECTING_STP
);
1539 /*------------------------------------------------------------*/
1541 // 17.25 - Fig. 17-16
1543 #define STM_NAME BridgeDetection
1545 STATES(STN(EDGE
), STN(NOT_EDGE
));
1547 STM(PORT_ARGS_PROTO
,
1552 TR(AdminEdge
, EDGE
);
1553 TR(!AdminEdge
, NOT_EDGE
);
1559 TR((!portEnabled
&& !AdminEdge
) || !operEdge
, NOT_EDGE
);
1565 TR((!portEnabled
&& AdminEdge
) ||
1566 ((edgeDelayWhile
== 0) && AutoEdge
1567 && sendRSTP
/* CORR: sendRstp */ && proposing
),
1575 /*------------------------------------------------------------*/
1577 // 17.26 - Fig. 17-17
1579 #define xc selected && !updtInfo
1580 #define xc2 newInfo && (txCount < TxHoldCount) && (helloWhen != 0)
1582 #define STM_NAME PortTransmit
1584 STATES(STN(TRANSMIT_INIT
), STN(TRANSMIT_PERIODIC
), STN(TRANSMIT_CONFIG
),
1585 STN(TRANSMIT_TCN
), STN(TRANSMIT_RSTP
), STN(IDLE
));
1587 STM(PORT_ARGS_PROTO
,
1592 TR(UCT
, TRANSMIT_INIT
);
1602 ST(TRANSMIT_PERIODIC
,
1603 newInfo
= newInfo
|| (role
== DesignatedPort
||
1604 (role
== RootPort
&& (tcWhile
!= 0)));
1611 txConfig(PORT_ARGS
);
1636 helloWhen
= HelloTime
;
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
);
1648 /*------------------------------------------------------------*/
1650 // 17.27 - Fig. 17-18
1652 #define STM_NAME PortInformation
1654 STATES(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
)
1660 STM(PORT_ARGS_PROTO
,
1663 TR(!portEnabled
&& (infoIs
!= Disabled
), DISABLED
);
1672 proposing
= proposed
= agree
= agreed
= FALSE
;
1674 infoIs
= Disabled
; reselect
= TRUE
; selected
= FALSE
;
1676 TR(portEnabled
, AGED
);
1677 TR(rcvdMsg
, DISABLED
);
1682 reselect
= TRUE
; selected
= FALSE
;
1684 TR(selected
&& updtInfo
, 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
;
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
;
1712 ST(REPEATED_DESIGNATED
,
1713 recordProposal(PORT_ARGS
);
1714 setTcFlags(PORT_ARGS
);
1715 updtRcvdInfoWhile(PORT_ARGS
);
1721 ST(INFERIOR_DESIGNATED
,
1722 recordDispute(PORT_ARGS
);
1729 recordAgreement(PORT_ARGS
);
1730 setTcFlags(PORT_ARGS
);
1744 TR(selected
&& updtInfo
, UPDATE
);
1745 TR((infoIs
== Received
) && (rcvdInfoWhile
== 0) &&
1746 !updtInfo
&& !rcvdMsg
,
1748 TR(rcvdMsg
&& !updtInfo
, RECEIVE
);
1752 rcvdInfo
= rcvInfo(PORT_ARGS
);
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
);
1765 /*------------------------------------------------------------*/
1767 // 17.28 - Fig. 17-19
1769 #define STM_NAME PortRoleSelection
1771 STATES(STN(INIT_BRIDGE
), STN(ROLE_SELECTION
));
1773 STM(BRIDGE_ARGS_PROTO
,
1778 TR(UCT
, INIT_BRIDGE
);
1782 updtRoleDisabledTree(BRIDGE_ARGS
);
1784 TR(UCT
, ROLE_SELECTION
);
1788 clearReselectTree(BRIDGE_ARGS
);
1789 updtRolesTree(BRIDGE_ARGS
);
1790 setSelectedTree(BRIDGE_ARGS
);
1792 TR(!ForAllPort(!FPORT
->_reselect
), ROLE_SELECTION
);
1799 /*------------------------------------------------------------*/
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
1807 #define STM_NAME PortRoleTransitions
1809 STATES(/* 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
)
1824 STM(PORT_ARGS_PROTO
,
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.
1831 /* Disabled Port role transitions */
1832 TR((role
!= selectedRole
) && (selectedRole
== DisabledPort
) && xc
,
1834 /* Root Port role transitions */
1835 TR((role
!= selectedRole
) && (selectedRole
== RootPort
) && xc
,
1837 /* Designated port role transitions */
1838 TR((role
!= selectedRole
) && (selectedRole
== DesignatedPort
) && xc
,
1840 /* Alternate and Backup Port role transitions */
1841 TR((role
!=selectedRole
) && ((selectedRole
== AlternatePort
)
1842 || (selectedRole
== BackupPort
)) && xc
,
1847 /* Disabled Port role transitions */
1854 role
= DisabledPort
;
1855 learn
= forward
= FALSE
;
1857 sync
= reRoot
= TRUE
;
1862 TR(UCT
, DISABLE_PORT
);
1866 role
= selectedRole
;
1867 learn
= forward
= FALSE
;
1869 TR(!learning
&& !forwarding
&& xc
, DISABLED_PORT
);
1876 sync
= reRoot
= FALSE
;
1878 TR((fdWhile
!= MaxAge
|| sync
|| reRoot
|| !synced
) && xc
,
1882 /* Root Port role transitions */
1885 setSyncTree(BRIDGE_ARGS
);
1892 proposed
= sync
= FALSE
;
1899 // This state is there in the 802.1Q-2005 state machine
1908 setReRootTree(BRIDGE_ARGS
);
1920 fdWhile
= forwardDelay
;
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
);
1943 ((reRooted
&& (rbWhile
== 0)) && (rstpVersion
))) && !learn
&& xc
,
1945 TR((fdWhile
== 0 || ((reRooted
&& (rbWhile
== 0)) && (rstpVersion
)))
1946 && learn
&& !forward
&& xc
,
1948 TR(rrWhile
!= FwdDelay
&& xc
, ROOT_PORT
);
1952 /* Designated port role transitions */
1954 ST(DESIGNATED_PROPOSE
,
1956 edgeDelayWhile
= EdgeDelay
;
1959 TR(UCT
, DESIGNATED_PORT
);
1962 ST(DESIGNATED_SYNCED
,
1967 TR(UCT
, DESIGNATED_PORT
);
1970 ST(DESIGNATED_RETIRED
,
1973 TR(UCT
, DESIGNATED_PORT
);
1976 ST(DESIGNATED_DISCARD
,
1977 learn
= forward
= disputed
= FALSE
;
1978 fdWhile
= forwardDelay
;
1980 TR(UCT
, DESIGNATED_PORT
);
1983 ST(DESIGNATED_LEARN
,
1985 fdWhile
= forwardDelay
;
1987 TR(UCT
, DESIGNATED_PORT
);
1990 ST(DESIGNATED_FORWARD
,
1993 agreed
= sendRSTP
/* CORR: sendRstp */;
1995 TR(UCT
, DESIGNATED_PORT
);
1999 role
= DesignatedPort
;
2001 TR(!forward
&& !agreed
&& !proposing
&& !operEdge
&& xc
,
2002 DESIGNATED_PROPOSE
);
2003 TR(((!learning
&& !forwarding
&& !synced
)
2004 || (agreed
&& !synced
) || (operEdge
&& !synced
)
2005 || (sync
&& synced
)) && xc
,
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
,
2014 TR(((fdWhile
== 0) || agreed
|| operEdge
)
2015 && ((rrWhile
==0) || !reRoot
) && !sync
&& (learn
&& !forward
) && xc
,
2016 DESIGNATED_FORWARD
);
2020 /* Alternate and Backup Port role transitions */
2023 role
= selectedRole
;
2024 learn
= forward
= FALSE
;
2026 TR(!learning
&& !forwarding
&& xc
, ALTERNATE_PORT
);
2029 ST(ALTERNATE_PROPOSED
,
2030 setSyncTree(BRIDGE_ARGS
);
2033 TR(UCT
, ALTERNATE_PORT
);
2036 ST(ALTERNATE_AGREED
,
2041 TR(UCT
, ALTERNATE_PORT
);
2045 rbWhile
= 2*HelloTime
;
2047 TR(UCT
, 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
;
2057 sync
= reRoot
= FALSE
;
2059 TR(proposed
&& !agree
&& xc
,
2060 ALTERNATE_PROPOSED
);
2061 TR(((allSynced
&& !agree
) || (proposed
&& agree
)) && xc
,
2063 TR((rbWhile
!= 2*HelloTime
) && (role
== BackupPort
) && xc
,
2065 TR(((fdWhile
!= forwardDelay
) || sync
|| reRoot
|| !synced
) && xc
,
2073 /*------------------------------------------------------------*/
2075 // 17.30 - Fig. 17-24
2077 #define STM_NAME PortStateTransition
2079 STATES(STN(DISCARDING
), STN(LEARNING
), STN(FORWARDING
));
2081 STM(PORT_ARGS_PROTO
,
2086 TR(UCT
, DISCARDING
);
2090 disableLearning(PORT_ARGS
);
2092 disableForwarding(PORT_ARGS
);
2095 TR(learn
, LEARNING
);
2099 enableLearning(PORT_ARGS
);
2102 TR(!learn
, DISCARDING
);
2103 TR(forward
, FORWARDING
);
2107 enableForwarding(PORT_ARGS
);
2110 TR(!forward
, DISCARDING
);
2117 /*------------------------------------------------------------*/
2119 // 17.31 - Fig. 17-25
2121 #define STM_NAME TopologyChange
2123 STATES(STN(INACTIVE
), STN(LEARNING
), STN(DETECTED
), STN(ACKNOWLEDGED
),
2124 STN(PROPAGATING
), STN(NOTIFIED_TC
), STN(NOTIFIED_TCN
), STN(ACTIVE
));
2126 STM(PORT_ARGS_PROTO
,
2136 do_fdbFlush(PORT_ARGS
); /* We add this to trigger the fdb flush */
2141 TR(learn
&& !fdbFlush
, LEARNING
);
2145 rcvdTc
= rcvdTcn
= rcvdTcAck
= FALSE
;
2146 rcvdTc
= tcProp
= FALSE
;
2148 TR(((role
== RootPort
) || (role
== DesignatedPort
))
2149 && forward
&& !operEdge
,
2152 TR((role
!= RootPort
) &&
2153 (role
!= DesignatedPort
) &&
2154 !(learn
|| learning
) &&
2155 !(rcvdTc
|| rcvdTcn
|| rcvdTcAck
|| tcProp
),
2166 newTcWhile(PORT_ARGS
);
2167 setTcPropTree(PORT_ARGS
);
2182 newTcWhile(PORT_ARGS
);
2184 do_fdbFlush(PORT_ARGS
); /* We add this to trigger the fdb flush */
2191 rcvdTcn
= rcvdTc
= FALSE
;
2192 if (role
== DesignatedPort
) tcAck
= TRUE
;
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.
2202 setTcPropTree(PORT_ARGS
); /* CORR: setTcPropBridge() */
2208 newTcWhile(PORT_ARGS
);
2210 TR(UCT
, NOTIFIED_TC
);
2215 TR(((role
!= RootPort
) && (role
!= DesignatedPort
) ) || operEdge
,
2217 TR(rcvdTcAck
, ACKNOWLEDGED
);
2218 TR(tcProp
&& !operEdge
, PROPAGATING
);
2219 TR(rcvdTc
, NOTIFIED_TC
);
2220 TR(rcvdTcn
, NOTIFIED_TCN
);
2228 /***** Running the state machines *****/
2230 static STM_FUNC_DECL((*bridge_stms
[NUM_BRIDGE_STATE_MACHINES
]),
2233 STM_FUNC(PortRoleSelection
),
2236 static STM_FUNC_DECL((*port_stms
[NUM_PORT_STATE_MACHINES
]), PORT_ARGS_PROTO
)
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
),
2249 static void state_machines_run(BRIDGE_ARGS_PROTO
)
2251 if (!BRIDGE
->stp_on
)
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
);
2262 for (i
= 0; i
< NUM_PORT_STATE_MACHINES
; i
++)
2263 STM_RUN(&FPORT
->state
[i
], &transitioned
,
2264 port_stms
[i
], FPORT_ARGS
);
2266 } while (transitioned
);
2269 static void state_machines_begin(BRIDGE_ARGS_PROTO
)
2271 if (!BRIDGE
->stp_on
)
2275 for (i
= 0; i
< NUM_BRIDGE_STATE_MACHINES
; i
++)
2276 STM_BEGIN(&BRIDGE
->state
[i
], bridge_stms
[i
], BRIDGE_ARGS
);
2279 for (i
= 0; i
< NUM_PORT_STATE_MACHINES
; i
++)
2280 STM_BEGIN(&FPORT
->state
[i
], port_stms
[i
], FPORT_ARGS
);
2283 /* Initialized. Now run them. */
2284 state_machines_run(BRIDGE_ARGS
);
2287 void STP_IN_set_bridge_enable(Bridge
*BRIDGE
, unsigned int enabled
)
2289 uint on
= enabled
?1:0;
2290 if (BRIDGE
->stp_on
== on
)
2293 BRIDGE
->stp_on
= on
;
2296 state_machines_begin(BRIDGE_ARGS
);
2304 PortPathCost and operPointToPointMAC can change through configuration
2305 as well as link notifications (when configured to auto).
2306 Common functions to handle these.
2309 // 17.14 - Table 17-3, also NOTE 3
2310 static uint
compute_pcost(int speed
)
2312 /* speed is in MB/s*/
2314 return speed
< 20000000 ? 20000000/speed
: 1;
2319 /* Returns TRUE if PostPathCost changed */
2320 static bool check_port_path_cost(PORT_ARGS_PROTO
)
2323 if (AdminPortPathCost
== 0) {
2324 pcost
= compute_pcost(PORT
->speed
);
2327 pcost
= AdminPortPathCost
;
2330 if (PortPathCost
!= pcost
) {
2331 PortPathCost
= pcost
;
2332 selected
= FALSE
; reselect
= TRUE
; // 17.13
2339 /* Returns TRUE if operPointToPointMAC changed */
2340 static bool check_port_p2p(PORT_ARGS_PROTO
)
2343 if (adminPointToPointMAC
== P2PAuto
) {
2344 p2p
= !!PORT
->duplex
;
2347 p2p
= (adminPointToPointMAC
== P2PForceTrue
);
2350 if (operPointToPointMAC
!= p2p
) {
2351 operPointToPointMAC
= p2p
;
2358 /******************** Event notifications ******************************/
2360 void STP_IN_one_second(BRIDGE_ARGS_PROTO
)
2362 if (BRIDGE
->tcWhile_count
== 0)
2363 BRIDGE
->time_since_tc
++;
2366 FPORT
->_tick
= TRUE
;
2368 state_machines_run(BRIDGE_ARGS
);
2371 void STP_IN_set_port_enable(Port
*PORT
, unsigned int enabled
,
2372 unsigned int speed
, unsigned int duplex
)
2374 Bridge
*BRIDGE
= PORT
->bridge
;
2376 bool changed
= FALSE
;
2384 if (PORT
->speed
!= speed
) {
2385 PORT
->speed
= speed
;
2386 if (check_port_path_cost(PORT_ARGS
))
2390 if (PORT
->duplex
!= duplex
) {
2391 PORT
->duplex
= duplex
;
2392 if (check_port_p2p(PORT_ARGS
))
2398 portEnabled
= FALSE
;
2403 state_machines_run(BRIDGE_ARGS
);
2406 /* Rounding up. What should we really be doing with fractional times? */
2407 static uint
get_bpdu_time(const uchar time
[2])
2409 uint t
= (time
[0] << 8) + time
[1];
2410 return (t
+ 0xff) >> 8;
2414 void STP_IN_rx_bpdu(Port
*PORT
, const void *base
, unsigned int len
)
2416 Bridge
*BRIDGE
= PORT
->bridge
;
2417 const RawBPDU
*p
= base
;
2419 if (!BRIDGE
->stp_on
)
2420 return; // No action - else we might fail the next test
2423 ABORT("Port hasn't processed old BPDU\n");
2430 if (p
->protocol_id
!= STP_PROTOCOL_ID
)
2433 DEBUG("Received BPDU of type %d\n", p
->type
);
2436 (p
->type
== BPDUTypeConfig
&&
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)
2446 (p
->type
== BPDUTypeTCN
)
2449 (p
->type
== BPDUTypeRST
&& len
>= 36)
2451 /* BPDU has been validated */
2454 DEBUG("BPDU validation failed\n");
2459 bpduVersion
= p
->version
<= 2 ? p
->version
: 2;
2464 (p
->type
!= BPDUTypeTCN
) ? ((p
->flags
& BPDU_FLAG_TC
) != 0) : 0;
2466 (p
->type
== BPDUTypeRST
) ? ((p
->flags
& BPDU_FLAG_PROPOSAL
) != 0) : 0;
2468 (p
->type
== BPDUTypeRST
) ? BPDU_FLAG_ROLE_GET(p
->flags
) : 0;
2470 (p
->type
== BPDUTypeRST
) ? ((p
->flags
& BPDU_FLAG_LEARNING
) != 0) : 0;
2471 bpduForwardingFlag
=
2472 (p
->type
== BPDUTypeRST
) ? ((p
->flags
& BPDU_FLAG_FORWARDING
) != 0) : 0;
2474 (p
->type
== BPDUTypeRST
) ? ((p
->flags
& BPDU_FLAG_AGREEMENT
) != 0) : 0;
2476 (p
->type
!= BPDUTypeTCN
) ? ((p
->flags
& BPDU_FLAG_TC_ACK
) != 0) : 0;
2478 if (p
->type
== BPDUTypeTCN
) {
2484 bpduPriority
= p
->priority
;
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
);
2494 state_machines_run(BRIDGE_ARGS
);
2498 /************************** Configuration *************************/
2501 static int check_times(BRIDGE_ARGS_PROTO
, int max_age
, int fwd_delay
)
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");
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) ");
2520 int STP_IN_set_bridge_config(Bridge
*BRIDGE
, const STP_BridgeConfig
*cfg
)
2523 bool changed
= FALSE
, init
= FALSE
;
2524 /* First, validation */
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");
2533 /* No validation of bridge_address. It can be any 6 bytes */
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");
2543 Times new_times
= BridgeTimes
;
2544 bool set_times
= FALSE
;
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");
2552 new_times
.hello_time
= cfg
->bridge_hello_time
;
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");
2562 new_times
.max_age
= cfg
->bridge_max_age
;
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");
2572 new_times
.forward_delay
= cfg
->bridge_forward_delay
;
2576 if (check_times(BRIDGE_ARGS
, new_times
.max_age
, new_times
.forward_delay
))
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");
2587 /* If not valid, return error */
2592 if (cfg
->set_bridge_protocol_version
&&
2593 ForceProtocolVersion
!= cfg
->bridge_protocol_version
) {
2595 ForceProtocolVersion
= cfg
->bridge_protocol_version
;
2602 if (cfg
->set_bridge_address
&&
2603 CMP(BridgeIdentifier
.bridge_address
, !=, cfg
->bridge_address
)) {
2605 CPY(BridgeIdentifier
.bridge_address
, cfg
->bridge_address
);
2611 if (cfg
->set_bridge_priority
&&
2612 ((BridgeIdentifierPriority
[0] & 0xf0) << 8) != cfg
->bridge_priority
) {
2614 BridgeIdentifierPriority
[0] &= ~0xf0;
2615 BridgeIdentifierPriority
[0] |= cfg
->bridge_priority
>> 8;
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.
2623 FPORT->_selected = FALSE;
2624 FPORT->_reselect = TRUE;
2630 CMP(new_times
, !=, BridgeTimes
)) {
2632 BridgeTimes
= new_times
;
2637 if (cfg
->set_bridge_tx_hold_count
&&
2638 TxHoldCount
!= cfg
->bridge_tx_hold_count
) {
2640 TxHoldCount
= cfg
->bridge_tx_hold_count
;
2643 // 17.13 introduction
2645 FPORT
->_txCount
= 0;
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.
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.
2659 FPORT
->_selected
= FALSE
;
2660 FPORT
->_reselect
= TRUE
;
2664 state_machines_begin(BRIDGE_ARGS
);
2666 state_machines_run(BRIDGE_ARGS
);
2673 int STP_IN_set_port_config(Port
*PORT
, const STP_PortConfig
*cfg
)
2675 Bridge
*BRIDGE
= PORT
->bridge
;
2678 bool changed
= FALSE
;
2679 /* First, validation */
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");
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");
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");
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");
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");
2719 /* If not valid, return error */
2724 if (cfg
->set_port_priority
&&
2725 (portId
.port_id
[0] & 0xf0) != cfg
->port_priority
) {
2727 portId
.port_id
[0] &= ~0xf0;
2728 portId
.port_id
[0] |= cfg
->port_priority
;
2732 selected
= FALSE
; reselect
= TRUE
;
2735 if (cfg
->set_port_pathcost
&&
2736 AdminPortPathCost
!= cfg
->port_pathcost
) {
2738 AdminPortPathCost
= cfg
->port_pathcost
;
2740 if (check_port_path_cost(PORT_ARGS
)) {
2741 /* PortPathCost was changed */
2743 // 17.13 d) - Done by check_port_path_cost()
2744 // selected = FALSE; reselect = TRUE;
2748 if (cfg
->set_port_admin_edge
&&
2749 AdminEdge
!= cfg
->port_admin_edge
) {
2751 AdminEdge
= cfg
->port_admin_edge
;
2756 if (cfg
->set_port_auto_edge
&&
2757 AutoEdge
!= cfg
->port_auto_edge
) {
2759 AutoEdge
= cfg
->port_auto_edge
;
2764 if (cfg
->set_port_admin_p2p
&&
2765 adminPointToPointMAC
!= cfg
->port_admin_p2p
) {
2767 adminPointToPointMAC
= cfg
->port_admin_p2p
;
2769 if (check_port_p2p(PORT_ARGS
)) {
2770 /* operPointToPointMAC was changed */
2776 state_machines_run(BRIDGE_ARGS
);
2781 /* To make this #define work */
2782 typedef STP_BridgeConfig STP_bridgeConfig
;
2783 typedef STP_PortConfig STP_portConfig
;
2785 #define SET_CFG(_type, _arg, _field, _value) \
2787 STP_ ## _type ## Config cfg; \
2789 cfg._type ## _ ## _field = _value; \
2790 cfg. set_ ## _type ## _ ## _field = 1; \
2791 STP_IN_set_ ## _type ## _config(_arg, &cfg); \
2794 /* Single field Bridge config functions */
2796 int STP_IN_set_protocol_version(Bridge
*BRIDGE
, unsigned int version
)
2798 return SET_CFG(bridge
, BRIDGE
, protocol_version
, version
);
2801 int STP_IN_set_bridge_address(Bridge
*BRIDGE
, const STP_MacAddress
*addr
)
2803 return SET_CFG(bridge
, BRIDGE
, address
, *addr
);
2806 int STP_IN_set_bridge_priority(Bridge
*BRIDGE
, unsigned int priority
)
2808 return SET_CFG(bridge
, BRIDGE
, priority
, priority
);
2811 int STP_IN_set_bridge_hello_time(Bridge
*BRIDGE
, unsigned int hello_time
)
2813 return SET_CFG(bridge
, BRIDGE
, hello_time
, hello_time
);
2816 int STP_IN_set_bridge_max_age(Bridge
*BRIDGE
, unsigned int max_age
)
2818 return SET_CFG(bridge
, BRIDGE
, max_age
, max_age
);
2822 int STP_IN_set_bridge_forward_delay(Bridge
*BRIDGE
, unsigned int fwd_delay
)
2824 return SET_CFG(bridge
, BRIDGE
, forward_delay
, fwd_delay
);
2828 int STP_IN_set_tx_hold_count(Bridge
*BRIDGE
, unsigned int count
)
2830 return SET_CFG(bridge
, BRIDGE
, tx_hold_count
, count
);
2834 /* Single field Port config functions */
2836 int STP_IN_set_port_priority(Port
*PORT
, unsigned int priority
)
2838 return SET_CFG(port
, PORT
, priority
, priority
);
2841 int STP_IN_set_port_pathcost(Port
*PORT
, unsigned int pcost
)
2843 return SET_CFG(port
, PORT
, pathcost
, pcost
);
2846 /* edge is 0 or 1 */
2847 int STP_IN_set_port_admin_edge(Port
*PORT
, unsigned int edge
)
2849 return SET_CFG(port
, PORT
, admin_edge
, edge
);
2852 int STP_IN_set_port_auto_edge(Port
*PORT
, unsigned int edge
)
2854 return SET_CFG(port
, PORT
, auto_edge
, edge
);
2857 int STP_IN_set_port_admin_p2p(Port
*PORT
, unsigned int p2p
)
2859 return SET_CFG(port
, PORT
, admin_p2p
, p2p
);
2862 /* Force migration check */
2864 int STP_IN_port_mcheck(Port
*PORT
)
2866 Bridge
*BRIDGE
= PORT
->bridge
;
2868 if (!BRIDGE
->stp_on
) {
2869 ERROR("Bridge is down\n");
2876 state_machines_run(BRIDGE_ARGS
);
2883 /********************* Status ************************/
2886 void STP_IN_get_bridge_status(Bridge
*BRIDGE
, STP_BridgeStatus
*status
)
2888 CPY(status
->bridge_id
, BridgeIdentifier
);
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;
2894 CPY(status
->designated_root
, rootPriority
.root_bridge_id
);
2896 status
->root_path_cost
= path_cost_to_uint(rootPriority
.root_path_cost
);
2898 CPY(status
->designated_bridge
, rootPriority
.designated_bridge_id
);
2901 ((rootPortId
.port_id
[0] & 0x0f) << 8) + rootPortId
.port_id
[1];
2903 status
->max_age
= rootTimes
.max_age
;
2904 status
->hello_time
= rootTimes
.hello_time
;
2905 status
->forward_delay
= rootTimes
.forward_delay
;
2907 status
->bridge_max_age
= BridgeTimes
.max_age
;
2908 status
->bridge_hello_time
= BridgeTimes
.hello_time
;
2909 status
->bridge_forward_delay
= BridgeTimes
.forward_delay
;
2911 status
->tx_hold_count
= TxHoldCount
;
2913 status
->protocol_version
= ForceProtocolVersion
;
2915 status
->enabled
= BRIDGE
->stp_on
;
2919 void STP_IN_get_port_status(Port
*PORT
, STP_PortStatus
*status
)
2921 status
->uptime
= -1; // XXX: Don't know
2923 status
->state
= PORT
->port_state_flags
;
2924 if (status
->state
& STP_PORT_STATE_FLAG_FORWARDING
)
2925 status
->state
&= ~STP_PORT_STATE_FLAG_LEARNING
;
2927 status
->id
= port_id_to_uint(portId
);
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
;
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
);
2941 status
->tc_ack
= tcAck
;
2942 status
->admin_edge_port
= AdminEdge
;
2943 status
->oper_edge_port
= operEdge
;
2944 status
->auto_edge_port
= AutoEdge
;
2946 status
->enabled
= portEnabled
;
2948 status
->admin_p2p
= adminPointToPointMAC
;
2949 status
->oper_p2p
= operPointToPointMAC
;
2953 /**************** Creating and deleting ********************/
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.
2958 Bridge
*STP_IN_bridge_create(void *user_ref
)
2960 Bridge
*b
= STP_OUT_mem_zalloc(sizeof(Bridge
));
2963 ERROR("Couldn't allocate memory for bridge\n");
2967 b
->user_ref
= user_ref
;
2971 /* Default configuration values */
2972 BridgeIdentifierPriority
[0] = 0x80; // Default bridge priority = 32768
2974 BridgeHelloTime
= 2;
2976 BridgeForwardDelay
= 15;
2978 ForceProtocolVersion
= 2;
2983 /* All ports will be deleted, don't reference them */
2984 void STP_IN_bridge_delete(Bridge
*BRIDGE
)
2986 /* Disable ports - for what it is worth */
2988 FPORT
->_portEnabled
= FALSE
;
2991 state_machines_run(BRIDGE_ARGS
);
2993 /* Delete all ports */
2994 while (BRIDGE
->port_list
)
2995 STP_IN_port_delete(BRIDGE
->port_list
);
2997 STP_OUT_mem_free(BRIDGE
);
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.
3003 Port
*STP_IN_port_create(Bridge
*BRIDGE
,
3004 unsigned int port_number
, void *user_ref
)
3006 if (port_number
== 0 || (port_number
& ~0x0fff)) {
3007 ERROR("Port id should be in the range 1 - 1023\n");
3011 Port
*p
= STP_OUT_mem_zalloc(sizeof(Port
));
3014 ERROR("Couldn't allocate memory for port\n");
3018 p
->user_ref
= user_ref
;
3021 p
->port_next
= BRIDGE
->port_list
;
3022 BRIDGE
->port_list
= p
;
3026 portId
.port_id
[0] = 0x80 | (port_number
>> 8); // Default port priority = 128
3027 portId
.port_id
[1] = port_number
& 0xff;
3029 // Don't need zero assignments since we zalloc'ed
3031 //AdminPathCost = 0;
3033 adminPointToPointMAC
= P2PAuto
;
3035 if (BRIDGE
->stp_on
) {
3036 /* Try and initialize some things */
3038 for (i
= 0; i
< NUM_PORT_STATE_MACHINES
; i
++)
3039 STM_BEGIN(&PORT
->state
[i
], port_stms
[i
], PORT_ARGS
);
3048 void STP_IN_port_delete(Port
*PORT
)
3050 Bridge
*BRIDGE
= PORT
->bridge
;
3054 portEnabled
= FALSE
;
3055 state_machines_run(BRIDGE_ARGS
);
3058 /* Find position in list */
3059 Port
**prev
= &BRIDGE
->port_list
;
3060 while (*prev
!= PORT
&& *prev
!= NULL
)
3061 prev
= &(*prev
)->port_next
;
3064 ABORT("port not in its bridge's list\n");
3066 /* Remove from list */
3067 *prev
= PORT
->port_next
;
3069 STP_OUT_mem_free(PORT
);