[ovs-dev] [PATCH v3] Rapid Spanning Protocol Implementation (IEEE 802.1D) + functional tests
Daniele Venturino
daniele.venturino at m3s.it
Mon Mar 31 13:58:49 UTC 2014
This is an updated version of the RSTP patch.
It contains some modifications to our implementation.
Among them:
- enable RSTP to send BPDU frames in learning and discarding states
- manage STP and RSTP coexistence in handling BPDU frames
- add missing SLOW_RSTP in compose_slow_path()
It also involves some minor fixes to match modifications done to OVS during the
last month.
Signed-off by: Daniele Venturino <daniele.venturino at m3s.it>
Signed-off by: Martino Fornasa <mf at fornasa.it>
---
AUTHORS | 1 +
NOTICE | 3 +
lib/automake.mk | 5 +
lib/odp-util.h | 1 +
lib/packets.h | 3 +
lib/rstp-common.h | 824 ++++++++++++++++++++
lib/rstp-state-machines.c | 1683 +++++++++++++++++++++++++++++++++++++++++
lib/rstp-state-machines.h | 107 +++
lib/rstp.c | 1160 ++++++++++++++++++++++++++++
lib/rstp.h | 184 +++++
ofproto/ofproto-dpif-upcall.c | 6 +-
ofproto/ofproto-dpif-xlate.c | 117 ++-
ofproto/ofproto-dpif-xlate.h | 4 +-
ofproto/ofproto-dpif.c | 282 ++++++-
ofproto/ofproto-provider.h | 47 ++
ofproto/ofproto.c | 80 +-
ofproto/ofproto.h | 50 ++
tests/automake.mk | 6 +
tests/ovs-vsctl.at | 4 +
tests/rstp.at | 149 ++++
tests/test-rstp.c | 660 ++++++++++++++++
tests/testsuite.at | 1 +
utilities/ovs-vsctl.8.in | 79 ++
vswitchd/bridge.c | 324 ++++++++
vswitchd/vswitch.ovsschema | 13 +-
vswitchd/vswitch.xml | 101 +++
26 files changed, 5864 insertions(+), 30 deletions(-)
create mode 100644 lib/rstp-common.h
create mode 100644 lib/rstp-state-machines.c
create mode 100644 lib/rstp-state-machines.h
create mode 100644 lib/rstp.c
create mode 100644 lib/rstp.h
create mode 100644 tests/rstp.at
create mode 100644 tests/test-rstp.c
diff --git a/AUTHORS b/AUTHORS
index b189957..9feb1e9 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -79,6 +79,7 @@ Luca Giraudo lgiraudo at nicira.com
Luigi Rizzo rizzo at iet.unipi.it
Mark Hamilton mhamilton at nicira.com
Martin Casado casado at nicira.com
+Martino Fornasa mf at fornasa.it
Mehak Mahajan mmahajan at nicira.com
Murphy McCauley murphy.mccauley at gmail.com
Natasha Gude natasha at nicira.com
diff --git a/NOTICE b/NOTICE
index 7a3d660..a213190 100644
--- a/NOTICE
+++ b/NOTICE
@@ -22,3 +22,6 @@ and Werner Lemberg.
m4/include_next.m4 and m4/absolute-header.m4
Copyright (C) 2006-2013 Free Software Foundation, Inc.
+
+Rapid Spanning Tree Protocol (RSTP) implementation
+Copyright (c) 2011-2014 M3S, Srl - Italy
diff --git a/lib/automake.mk b/lib/automake.mk
index fcd2c5b..09e01c5 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -175,6 +175,11 @@ lib_libopenvswitch_la_SOURCES = \
lib/rconn.h \
lib/reconnect.c \
lib/reconnect.h \
+ lib/rstp.c \
+ lib/rstp.h \
+ lib/rstp-common.h \
+ lib/rstp-state-machines.c \
+ lib/rstp-state-machines.h \
lib/sat-math.h \
lib/seq.c \
lib/seq.h \
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 7bc64c7..53243a5 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -42,6 +42,7 @@ struct pkt_metadata;
SPR(SLOW_BFD, "bfd", "Consists of BFD packets") \
SPR(SLOW_LACP, "lacp", "Consists of LACP packets") \
SPR(SLOW_STP, "stp", "Consists of STP packets") \
+ SPR(SLOW_RSTP, "rstp", "Consists of RSTP packets") \
SPR(SLOW_CONTROLLER, "controller", \
"Sends \"packet-in\" messages to the OpenFlow controller") \
SPR(SLOW_ACTION, "action", \
diff --git a/lib/packets.h b/lib/packets.h
index 22817af..e9fc8a9 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -73,6 +73,9 @@ static const uint8_t eth_addr_broadcast[ETH_ADDR_LEN] OVS_UNUSED
static const uint8_t eth_addr_stp[ETH_ADDR_LEN] OVS_UNUSED
= { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 };
+static const uint8_t eth_addr_rstp[ETH_ADDR_LEN] OVS_UNUSED
+ = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 };
+
static const uint8_t eth_addr_lacp[ETH_ADDR_LEN] OVS_UNUSED
= { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x02 };
diff --git a/lib/rstp-common.h b/lib/rstp-common.h
new file mode 100644
index 0000000..61ffc65
--- /dev/null
+++ b/lib/rstp-common.h
@@ -0,0 +1,824 @@
+/*
+ * Copyright (c) 2011-2014 M3S, Srl - Italy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Rapid Spanning Tree Protocol (IEEE 802.1D-2004) common header file.
+ *
+ * Authors:
+ * Martino Fornasa <mf at fornasa.it>
+ * Daniele Venturino <daniele.venturino at m3s.it>
+ *
+ * References to IEEE 802.1D-2004 standard are enclosed in square brackets.
+ * E.g. [17.3], [Table 17-1], etc.
+ *
+ */
+
+#ifndef RSTP_COMMON_H
+#define RSTP_COMMON_H 1
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "packets.h"
+#include "ovs-atomic.h"
+#include "list.h"
+#include "rstp.h"
+
+enum rstp_force_protocol_version;
+
+#define RSTP_LLC_DSAP 0x42
+#define RSTP_LLC_SSAP 0x42
+#define RSTP_LLC_CNTL 0x03
+
+/* Ethernet 802/803 header. */
+#define LLC_SIZE 3
+#define ETH_SIZE 14
+struct ethernet_hdr {
+ unsigned char dhost[6];
+ unsigned char shost[6];
+ unsigned short length;
+ unsigned char dsap;
+ unsigned char ssap;
+ unsigned char cntrl;
+};
+
+enum admin_port_state {
+ RSTP_ADMIN_BRIDGE_PORT_STATE_DISABLED = 0,
+ RSTP_ADMIN_BRIDGE_PORT_STATE_ENABLED = 1
+};
+
+enum oper_p2p_mac_state {
+ RSTP_OPER_P2P_MAC_STATE_DISABLED = 0,
+ RSTP_OPER_P2P_MAC_STATE_ENABLED = 1
+};
+
+/* State enumerations for state machines defined in rstp-state-machines.c */
+enum port_receive_state_machine {
+ PORT_RECEIVE_SM_INIT,
+ PORT_RECEIVE_SM_DISCARD_EXEC,
+ PORT_RECEIVE_SM_DISCARD,
+ PORT_RECEIVE_SM_RECEIVE_EXEC,
+ PORT_RECEIVE_SM_RECEIVE
+};
+enum port_transmit_state_machine {
+ PORT_TRANSMIT_SM_INIT,
+ PORT_TRANSMIT_SM_TRANSMIT_INIT_EXEC,
+ PORT_TRANSMIT_SM_TRANSMIT_INIT,
+ PORT_TRANSMIT_SM_TRANSMIT_PERIODIC_EXEC,
+ PORT_TRANSMIT_SM_TRANSMIT_PERIODIC,
+ PORT_TRANSMIT_SM_IDLE_EXEC,
+ PORT_TRANSMIT_SM_IDLE,
+ PORT_TRANSMIT_SM_TRANSMIT_CONFIG_EXEC,
+ PORT_TRANSMIT_SM_TRANSMIT_CONFIG,
+ PORT_TRANSMIT_SM_TRANSMIT_TCN_EXEC,
+ PORT_TRANSMIT_SM_TRANSMIT_TCN,
+ PORT_TRANSMIT_SM_TRANSMIT_RSTP_EXEC,
+ PORT_TRANSMIT_SM_TRANSMIT_RSTP
+};
+enum bridge_detection_state_machine {
+ BRIDGE_DETECTION_SM_INIT,
+ BRIDGE_DETECTION_SM_EDGE_EXEC,
+ BRIDGE_DETECTION_SM_EDGE,
+ BRIDGE_DETECTION_SM_NOT_EDGE_EXEC,
+ BRIDGE_DETECTION_SM_NOT_EDGE
+};
+enum port_protocol_migration_state_machine {
+ PORT_PROTOCOL_MIGRATION_SM_INIT,
+ PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC,
+ PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP,
+ PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP_EXEC,
+ PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP,
+ PORT_PROTOCOL_MIGRATION_SM_SENSING_EXEC,
+ PORT_PROTOCOL_MIGRATION_SM_SENSING
+};
+enum port_information_state_machine {
+ PORT_INFORMATION_SM_INIT,
+ PORT_INFORMATION_SM_DISABLED_EXEC,
+ PORT_INFORMATION_SM_DISABLED,
+ PORT_INFORMATION_SM_AGED_EXEC,
+ PORT_INFORMATION_SM_AGED,
+ PORT_INFORMATION_SM_UPDATE_EXEC,
+ PORT_INFORMATION_SM_UPDATE,
+ PORT_INFORMATION_SM_CURRENT_EXEC,
+ PORT_INFORMATION_SM_CURRENT,
+ PORT_INFORMATION_SM_RECEIVE_EXEC,
+ PORT_INFORMATION_SM_RECEIVE,
+ PORT_INFORMATION_SM_OTHER_EXEC,
+ PORT_INFORMATION_SM_OTHER,
+ PORT_INFORMATION_SM_NOT_DESIGNATED_EXEC,
+ PORT_INFORMATION_SM_NOT_DESIGNATED,
+ PORT_INFORMATION_SM_INFERIOR_DESIGNATED_EXEC,
+ PORT_INFORMATION_SM_INFERIOR_DESIGNATED,
+ PORT_INFORMATION_SM_REPEATED_DESIGNATED_EXEC,
+ PORT_INFORMATION_SM_REPEATED_DESIGNATED,
+ PORT_INFORMATION_SM_SUPERIOR_DESIGNATED_EXEC,
+ PORT_INFORMATION_SM_SUPERIOR_DESIGNATED
+};
+enum port_role_selection_state_machine {
+ PORT_ROLE_SELECTION_SM_INIT,
+ PORT_ROLE_SELECTION_SM_INIT_BRIDGE_EXEC,
+ PORT_ROLE_SELECTION_SM_INIT_BRIDGE,
+ PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC,
+ PORT_ROLE_SELECTION_SM_ROLE_SELECTION
+};
+enum port_role_transition_state_machine {
+ PORT_ROLE_TRANSITION_SM_INIT,
+ PORT_ROLE_TRANSITION_SM_INIT_PORT_EXEC,
+ PORT_ROLE_TRANSITION_SM_DISABLE_PORT_EXEC,
+ PORT_ROLE_TRANSITION_SM_DISABLE_PORT,
+ PORT_ROLE_TRANSITION_SM_DISABLED_PORT_EXEC,
+ PORT_ROLE_TRANSITION_SM_DISABLED_PORT,
+ PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC,
+ PORT_ROLE_TRANSITION_SM_ROOT_PORT,
+ PORT_ROLE_TRANSITION_SM_REROOT_EXEC,
+ PORT_ROLE_TRANSITION_SM_ROOT_AGREED_EXEC,
+ PORT_ROLE_TRANSITION_SM_ROOT_PROPOSED_EXEC,
+ PORT_ROLE_TRANSITION_SM_ROOT_FORWARD_EXEC,
+ PORT_ROLE_TRANSITION_SM_ROOT_LEARN_EXEC,
+ PORT_ROLE_TRANSITION_SM_REROOTED_EXEC,
+ PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC,
+ PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT,
+ PORT_ROLE_TRANSITION_SM_DESIGNATED_RETIRED_EXEC,
+ PORT_ROLE_TRANSITION_SM_DESIGNATED_SYNCED_EXEC,
+ PORT_ROLE_TRANSITION_SM_DESIGNATED_PROPOSE_EXEC,
+ PORT_ROLE_TRANSITION_SM_DESIGNATED_FORWARD_EXEC,
+ PORT_ROLE_TRANSITION_SM_DESIGNATED_LEARN_EXEC,
+ PORT_ROLE_TRANSITION_SM_DESIGNATED_DISCARD_EXEC,
+ PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC,
+ PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT,
+ PORT_ROLE_TRANSITION_SM_ALTERNATE_AGREED,
+ PORT_ROLE_TRANSITION_SM_ALTERNATE_PROPOSED_EXEC,
+ PORT_ROLE_TRANSITION_SM_BLOCK_PORT_EXEC,
+ PORT_ROLE_TRANSITION_SM_BLOCK_PORT,
+ PORT_ROLE_TRANSITION_SM_BACKUP_PORT_EXEC
+};
+enum port_state_transition_state_machine {
+ PORT_STATE_TRANSITION_SM_INIT,
+ PORT_STATE_TRANSITION_SM_DISCARDING_EXEC,
+ PORT_STATE_TRANSITION_SM_DISCARDING,
+ PORT_STATE_TRANSITION_SM_LEARNING_EXEC,
+ PORT_STATE_TRANSITION_SM_LEARNING,
+ PORT_STATE_TRANSITION_SM_FORWARDING_EXEC,
+ PORT_STATE_TRANSITION_SM_FORWARDING
+};
+enum topology_change_state_machine {
+ TOPOLOGY_CHANGE_SM_INIT,
+ TOPOLOGY_CHANGE_SM_INACTIVE_EXEC,
+ TOPOLOGY_CHANGE_SM_INACTIVE,
+ TOPOLOGY_CHANGE_SM_LEARNING_EXEC,
+ TOPOLOGY_CHANGE_SM_LEARNING,
+ TOPOLOGY_CHANGE_SM_DETECTED_EXEC,
+ TOPOLOGY_CHANGE_SM_ACTIVE_EXEC,
+ TOPOLOGY_CHANGE_SM_ACTIVE,
+ TOPOLOGY_CHANGE_SM_ACKNOWLEDGED_EXEC,
+ TOPOLOGY_CHANGE_SM_PROPAGATING_EXEC,
+ TOPOLOGY_CHANGE_SM_NOTIFIED_TC_EXEC,
+ TOPOLOGY_CHANGE_SM_NOTIFIED_TCN_EXEC,
+};
+
+
+/* [17.18.4, 17.13, Table 17-1]. */
+struct rstp_times {
+ /* [17.13.5 - Bridge Forward Delay] The delay used by STP Bridges (17.4)
+ to transition Root and Designated Ports to Forwarding (Table 17-1).
+ Default = 15.0 s. Values in range 4.0 - 30.0 */
+ unsigned int forward_delay;
+
+ /* [17.13.6 - Bridge Hello Time]
+ The interval between periodic transmissions of Configuration Messages by
+ Designated Ports (Table 17-1).
+ Default = 2.0 s. Fixed value */
+ unsigned int hello_time;
+
+ /* [17.13.8 - Bridge Max Age]
+ The maximum age of the information transmitted by the Bridge when it is
+ the Root Bridge (Table 17-1).
+ Default = 20.0 s. Values in range 6.0 - 40.0 */
+ unsigned int max_age;
+
+ unsigned int message_age;
+};
+
+/* Priority vector [17.6] */
+struct rstp_priority_vector {
+ uint8_t root_bridge_id[8];
+ uint8_t root_path_cost[4];
+ uint8_t designated_bridge_id[8];
+ uint8_t designated_port_id[2];
+ uint8_t bridge_port_id[2];
+};
+
+struct rstp_priority_vector4 {
+ unsigned char root_bridge_id[8];
+ unsigned char root_path_cost[4];
+ unsigned char designated_bridge_id[8];
+ unsigned char designated_port_id[2];
+};
+
+enum rstp_bpdu_type {
+ CONFIGURATION_BPDU = 0x0,
+ TOPOLOGY_CHANGE_NOTIFICATION = 0x80,
+ RAPID_SPANNING_TREE_BPDU = 0x2
+} bpdu_type_t;
+
+enum rstp_bpdu_flag {
+ BPDU_FLAG_TOPCHANGE = 0x01,
+ BPDU_FLAG_PROPOSAL = 0x02,
+ BPDU_FLAG_LEARNING = 0x10,
+ BPDU_FLAG_FORWARDING = 0x20,
+ BPDU_FLAG_AGREEMENT = 0x40,
+ BPDU_FLAG_TOPCHANGEACK = 0x80
+} bpdu_flag;
+
+/* Rapid Spanning Tree BPDU [9.3.3] */
+OVS_PACKED(
+struct rstp_bpdu {
+ uint16_t protocol_identifier;
+ uint8_t protocol_version_identifier;
+ uint8_t bpdu_type;
+ uint8_t flags;
+ struct rstp_priority_vector4 priority_vector;
+ uint8_t message_age[2];
+ uint8_t max_age[2];
+ uint8_t hello_time[2];
+ uint8_t forward_delay[2];
+ uint8_t version1_length;
+ uint8_t padding[7];
+});
+
+enum rstp_admin_point_to_point_mac_state {
+ RSTP_ADMIN_P2P_MAC_FORCE_TRUE,
+ RSTP_ADMIN_P2P_MAC_FORCE_FALSE,
+ RSTP_ADMIN_P2P_MAC_FORCE_AUTO
+};
+
+enum rstp_info_is {
+ INFO_IS_DISABLED,
+ INFO_IS_RECEIVED,
+ INFO_IS_AGED,
+ INFO_IS_MINE
+};
+
+enum rstp_rcvd_info {
+ SUPERIOR_DESIGNATED_INFO,
+ REPEATED_DESIGNATED_INFO,
+ INFERIOR_DESIGNATED_INFO,
+ INFERIOR_ROOT_ALTERNATE_INFO,
+ OTHER_INFO
+};
+
+struct rstp_port {
+ struct rstp *rstp;
+ void *aux;
+ struct rstp_bpdu received_bpdu_buffer;
+ /*************************************************************************
+ * MAC status parameters
+ ************************************************************************/
+ /* [6.4.2 - MAC_Operational]
+ The value of this parameter is TRUE if [...] the MAC entity can be used
+ to transmit and/or receive frames, and its use is permitted by management */
+ bool mac_operational;
+
+ /* [14.8.2.2] Administrative Bridge Port State */
+ bool is_administrative_bridge_port;
+
+ /* [6.4.3 - operPointToPointMAC]
+ a) True. The MAC is connected to a point-to-point LAN; i.e., there is
+ at most one other system attached to the LAN.
+ b) False. The MAC is connected to a non-point-to-point LAN; i.e., there
+ can be more than one other system attached to the LAN.
+
+ If adminPointToPointMAC is set to ForceTrue, then operPointToPointMAC
+ shall be set True. If adminPointToPointMAC is set to ForceFalse, then
+ operPointToPointMAC shall be set False. */
+ bool oper_point_to_point_mac;
+
+ /* [6.4.3 - adminPointToPointMAC]
+ a) ForceTrue. The administrator requires the MAC to be treated as if it
+ is connected to a point-to-point LAN, regardless of any indications
+ to the contrary that are generated by the MAC entity.
+ b) ForceFalse. The administrator requires the MAC to be treated as
+ connected to a non-point-to-point LAN, regardless of any indications
+ to the contrary that are generated by the MAC entity.
+ c) Auto. The administrator requires the point-to-point status of the
+ MAC to be determined in accordance with the specific MAC procedures
+ defined in 6.5. */
+ enum rstp_admin_point_to_point_mac_state admin_point_to_point_mac;
+
+
+ /*************************************************************************
+ * [17.3 - RSTP performance parameters] Set by management actions on the bridge
+ *************************************************************************/
+
+ /* [17.13.1 - Admin Edge Port]
+ The AdminEdgePort parameter for the Port (14.8.2). */
+ bool admin_edge;
+
+ /* [17.13.3 - AutoEdge]
+ The AutoEdgePort parameter for the Port (14.8.2). */
+ bool auto_edge;
+
+
+ /*************************************************************************
+ * The following variable are set by management actions on the bridge
+ ************************************************************************/
+
+ /* Port number and priority */
+ uint16_t port_number; // >=1 (max 12 bits [9.2.7])
+ #define RSTP_MAX_PORT_NUMBER 0xFFF
+
+ /* Port priority
+ Range: 0-240 in steps of 16 (table 17-2) */
+ uint8_t priority;
+
+ /* [17.13.11 - PortPathCost]
+ The Port's contribution, when it is the Root Port, to the Root Path Cost
+ (17.3.1, 17.5, 17.6) for the Bridge. */
+ uint32_t port_path_cost;
+
+ /*************************************************************************
+ * The following variable are defined in [17.17 - State machine timers]
+ ************************************************************************/
+ /* [17.17.1 - edgeDelayWhile]
+ The Edge Delay timer. The time remaining, in the absence of a received
+ BPDU, before this port is identified as an operEdgePort. */
+ unsigned int edge_delay_while;
+
+ /* [17.17.2 - fdWhile]
+ The Forward Delay timer. Used to delay Port State transitions until
+ other Bridges have received spanning tree information. */
+ unsigned int fd_while;
+
+ /* [17.17.3 - helloWhen]
+ The Hello timer. Used to ensure that at least one BPDU is transmitted by
+ a Designated Port in each HelloTime period. */
+ unsigned int hello_when;
+
+ /* [17.17.4 - mdelayWhile]
+ The Migration Delay timer. Used by the Port Protocol Migration state
+ machine to allow time for another RSTP Bridge on the same LAN to
+ synchronize its migration state with this Port before the receipt of a
+ BPDU can cause this Port to change the BPDU types it transmits.
+ Initialized to MigrateTime (17.13.9). */
+ unsigned int mdelay_while;
+
+ /* [17.17.5 - rbWhile]
+ The Recent Backup timer. Maintained at its initial value, twice HelloTime,
+ while the Port is a Backup Port. */
+ unsigned int rb_while;
+
+ /* [17.17.6 - rcvdInfoWhile]
+ The Received Info timer. The time remaining before the spanning tree
+ information received by this Port
+ [portPriority (17.19.21) and portTimes (17.19.22)] is aged out if not
+ refreshed by the receipt of a further Configuration Message. */
+ unsigned int rcvd_info_while;
+
+ /* [17.17.7 - rrWhile]
+ The Recent Root timer. */
+ unsigned int rr_while;
+
+ /* [17.17.8 - tcWhile]
+ The Topology Change timer. TCN Messages are sent while this timer is
+ running. */
+ unsigned int tc_while;
+
+
+ /*************************************************************************
+ * The following variable are defined in [17.19 - Per-Port variables]
+ ************************************************************************/
+
+ /* [17.19.1 - ageingTime]
+ Filtering database entries for this Port are aged out after ageingTime
+ has elapsed since they were first created or refreshed by the Learning
+ Process.
+ The value of this parameter is normally Ageing Time (7.9.2, Table 7-5),
+ and is changed to FwdDelay (17.20.6) for a period of FwdDelay after
+ fdbFlush (17.19.7) is set by the topology change state machine if
+ stpVersion (17.19.7) is TRUE. */
+ unsigned int ageing_time;
+
+ /* [17.19.2 - agree]
+ Set if synced is set for all other Ports. An RST BPDU with the Agreement
+ flag set is transmitted and proposed is reset when agree is first set,
+ and when proposed is set.
+ Initialized by Port Information state machine. */
+ bool agree;
+
+ /* [17.19.3 - agreed]
+ Set when an RST BPDU is received with a Port Role of Root, Alternate, or
+ Backup Port, the Agreement flag set, and a message priority the same or
+ worse than the port priority. When agreed is set, the Designated Port
+ knows that its neighbouring Bridge has confirmed that it can proceed to
+ the Forwarding state without further delay.
+ Initialized by Port Information state machine. */
+ bool agreed;
+
+ /* [17.19.4 - designatedPriority]
+ The first four components of the Port’s designated priority vector value,
+ as defined in 17.6. The fifth component of the designated priority vector
+ value is portId (17.19.19).
+ (Fifth component of the structure must not be used) */
+ struct rstp_priority_vector designated_priority_vector;
+
+ /* [17.19.5 - designatedTimes]
+ The designatedTimes variable comprises the set of timer parameter values
+ (Message Age, Max Age, Forward Delay, and Hello Time) that used to update
+ Port Times when updtInfo is set. Updated by the updtRolesTree()
+ procedure (17.21.25). */
+ struct rstp_times designated_times;
+
+ /* [17.19.6 - disputed] */
+ bool disputed;
+
+ /* [17.19.7 - fdbFlush]
+ A boolean. Set by the topology change state machine to instruct the
+ filtering database to remove all entries for this Port, immediately if
+ rstpVersion (17.20.11) is TRUE, or by rapid ageing (17.19.1) if stpVersion
+ (17.20.12) is TRUE. Reset by the filtering database once the entries are
+ removed if rstpVersion is TRUE, and immediately if stpVersion is TRUE. */
+ uint8_t fdb_flush;
+
+ /* [17.19.8 - forward]
+ Initialized by Port State Transition state machine */
+ bool forward;
+
+ /* [17.19.9 - forwarding]
+ Initialized by Port State Transition state machine */
+ bool forwarding;
+
+ /* [17.19.10 - infoIs]
+ A variable that takes the values Mine, Aged, Received, or Disabled, to
+ indicate the origin/state of the Port's Spanning Tree information (portInfo)
+ held for the Port, as follows:
+ a) If infoIs is Received, the port has received current (not aged out)
+ information from the Designated Bridge for the attached LAN (a
+ point-to-point bridge link being a special case of a LAN).
+ b) If infoIs is Mine, information for the port has been derived from
+ the Root Port for the Bridge (with the addition of root port cost
+ information). This includes the possibility that the Root Port is
+ "Port 0," i.e., the bridge is the Root Bridge for the Bridged Local
+ Area Network.
+ c) If infoIs is Aged, information from the Root Bridge has been aged
+ out. Just as for "reselect" (see 17.19.34), the state machine does
+ not formally allow the "Aged" state to persist. However, if there is
+ a delay in recomputing the new root port, correct processing of a
+ received BPDU is specified.
+ d) Finally if the port is disabled, infoIs is Disabled.*/
+
+ enum rstp_info_is info_is;
+
+ /* [17.19.11 - learn]
+ Initialized by Port State Transition state machine */
+ bool learn;
+
+ /* [17.19.12 - learning]
+ Initialized by Port State Transition state machine */
+ bool learning;
+
+ /* [17.19.13 - mcheck]
+ A boolean. May be set by management to force the Port Protocol Migration
+ state machine to transmit RST BPDUs for a MigrateTime (17.13.9) period,
+ to test whether all STP Bridges (17.4) on the attached LAN have been
+ removed and the Port can continue to transmit RSTP BPDUs. Setting mcheck
+ has no effect if stpVersion (17.20.12) is TRUE, i.e., the Bridge is
+ operating in STP Compatibility mode. */
+ bool mcheck;
+
+ /* [17.19.14 - msgPriority]
+ The first four components of the message priority vector conveyed in a
+ received BPDU, as defined in 17.6.
+ (Fifth component of the structure must not be used) */
+ struct rstp_priority_vector msg_priority;
+
+ /* [17.19.15 - msgTimes]
+ The msgTimes variable comprises the timer parameter values (Message Age,
+ Max Age, Forward Delay, and Hello Time) conveyed in a received BPDU. */
+ struct rstp_times msg_times;
+
+ /* [17.19.16 - newInfo]
+ A boolean. Set if a BPDU is to be transmitted. Reset by the Port Transmit
+ state machine. */
+ bool new_info;
+
+ /* [17.19.17 - operEdge]
+ A boolean. The value of the operEdgePort parameter, as determined by the
+ operation of the Bridge Detection state machine (17.25). */
+ bool oper_edge;
+
+ /* [17.19.18 - portEnabled]
+ A boolean. Set if the Bridge's MAC Relay Entity and Spanning Tree
+ Protocol Entity can use the MAC Service provided by the Port's MAC
+ entity to transmit and receive frames to and from the attached LAN,
+ i.e., portEnabled is TRUE if and only if:
+ a) MAC_Operational (6.4.2) is TRUE; and
+ b) Administrative Bridge Port State (14.8.2.2) for the Port is
+ Enabled; and
+ c) AuthControlledPortStatus is Authorized [if the port is a network
+ access port (IEEE Std 802.1X)]. */
+ bool port_enabled;
+
+ /* [17.19.19 - portId]
+ The Port Identifier. This variable forms the fifth component of the port
+ priority and designated priority vectors defined in 17.6. */
+ uint8_t port_id[2];
+
+ /* [17.19.21 - portPriority]
+ The first four components of the Port’s port priority vector value, as
+ defined in 17.6.
+ (Fifth component of the structure must not be used) */
+ struct rstp_priority_vector port_priority;
+
+ /* [17.19.22 - portTimes]
+ The portTimes variable comprises the Port’s timer parameter values
+ (Message Age, Max Age, Forward Delay, and Hello Time). These timer
+ values are used in BPDUs transmitted from the Port. */
+ struct rstp_times port_times;
+
+ /* [17.19.23 - proposed]
+ Set when an RST BPDU with a Designated Port role and the Proposal flag
+ set is received. If agree is not set, proposed causes sync to be set for
+ all other Ports.of the Bridge. */
+ bool proposed;
+
+ /* [17.19.24 - proposing]
+ Set by a Designated Port that is not Forwarding, and conveyed to the
+ Root Port or Alternate Port of a neighboring Bridge in the Proposal flag
+ of an RST BPDU (9.3.3). */
+ bool proposing;
+
+ /* [17.19.25 - rcvdBPDU]
+ A boolean. Set by system dependent processes, this variable notifies the
+ Port Receive state machine (17.23) when a valid (9.3.4) Configuration,
+ TCN, or RST BPDU (9.3.1, 9.3.2, 9.3.3) is received on the Port. Reset
+ by the Port Receive state machine. */
+ bool rcvd_bpdu;
+
+ /* [17.19.26 - rcvdInfo]
+ Set to the result of the rcvInfo() procedure (17.21.8). */
+ enum rstp_rcvd_info rcvd_info;
+
+ /* [17.19.27 - rcvdMsg] */
+ bool rcvd_msg;
+
+ /* [17.19.28 - rcvdRSTP] */
+ bool rcvd_rstp;
+
+ /* [17.19.29 - rcvdSTP] */
+ bool rcvd_stp;
+
+ /* [17.19.30 - rcvdTc] */
+ bool rcvd_tc;
+
+ /* [17.19.31 - rcvdTcAck] */
+ bool rcvd_tc_ack;
+
+ /* [17.19.32 - rcvdTcn] */
+ bool rcvd_tcn;
+
+ /* [17.19.33 - reRoot] */
+ bool re_root;
+
+ /* [17.19.34 - reselect] */
+ bool reselect;
+
+ /* [17.19.35 - role]
+ The assigned Port Role (17.7).*/
+ enum rstp_port_role role;
+
+ /* [17.19.36 - selected]
+ A boolean. See 17.28, 17.21.16. */
+ bool selected;
+
+ /* [17.19.37 - selectedRole]
+ The newly computed role for the Port (17.7, 17.28, 17.21.25, 17.19.35). */
+ enum rstp_port_role selected_role;
+
+ /* [17.19.38 - sendRSTP]
+ A boolean. See 17.24, 17.26. */
+ bool send_rstp;
+
+ /* [17.19.39 - sync]
+ A boolean. See 17.10. */
+ bool sync;
+
+ /* [17.19.40 - synced]
+ A boolean. See 17.10. */
+ bool synced;
+
+ /* [17.19.41 - tcAck]
+ A boolean. Set if a Configuration Message with a topology change
+ acknowledge flag set is to be transmitted. */
+ bool tc_ack;
+
+ /* [17.19.42 - tcProp]
+ A boolean. Set by the Topology Change state machine of any other Port,
+ to indicate that a topology change should be propagated through this Port. */
+ bool tc_prop;
+
+ /* [17.19.43 - tick]
+ A boolean. See 17.22. */
+ bool tick;
+
+ /* [17.19.44 - txCount]
+ A counter. Incremented by the Port Transmission (17.26) state machine on
+ every BPDU transmission, and decremented used by the Port Timers state
+ machine (17.22) once a second. Transmissions are delayed if txCount
+ reaches TxHoldCount (17.13.12). */
+ unsigned int tx_count;
+
+ /* [17.19.45 - updtInfo]
+ A boolean. Set by the Port Role Selection state machine (17.28, 17.21.25)
+ to tell the Port Information state machine that it should copy
+ designatedPriority to portPriority and designatedTimes to portTimes. */
+ bool updt_info;
+
+ /* Counter for RSTP received frames - for rstpd */
+ unsigned int rx_rstp_bpdu_cnt;
+
+ /* Counter for bad RSTP received frames */
+ unsigned int error_count;
+
+ /*14.8.2.1.3 Outputs
+ a) Uptime count in seconds of the time elapsed since the Port was last
+ reset or initialized. */
+ unsigned int uptime;
+
+ enum rstp_state rstp_state;
+ bool state_changed;
+
+ /* Per-port state machines state */
+ enum port_receive_state_machine port_receive_sm_state;
+ enum port_protocol_migration_state_machine port_protocol_migration_sm_state;
+ enum bridge_detection_state_machine bridge_detection_sm_state;
+ enum port_transmit_state_machine port_transmit_sm_state;
+ enum port_information_state_machine port_information_sm_state;
+ enum port_role_transition_state_machine port_role_transition_sm_state;
+ enum port_state_transition_state_machine port_state_transition_sm_state;
+ enum topology_change_state_machine topology_change_sm_state;
+};
+
+struct rstp {
+ struct list node; /* Node in rstp instances list */
+ char *name; /* Bridge name. */
+
+ /* Changes in last SM execution. */
+ bool changes;
+
+ /* Per-bridge state machines state */
+ enum port_role_selection_state_machine port_role_selection_sm_state;
+
+ /* Bridge MAC address */
+ uint8_t address[ETH_ADDR_LEN]; /* [7.12.5] */
+
+ /* Bridge priority */
+ uint16_t priority; /* Valid values: 0-61440 in steps of 4096 */
+
+ /*************************************************************************
+ * [17.3 - RSTP performance parameters]
+ ************************************************************************/
+
+ /* [17.13]
+ The Spanning Tree Protocol Entity shall be reinitialized, as specified
+ by the assertion of BEGIN (17.18.1) in the state machine specification,
+ if the following parameters are modified:
+ a) Force Protocol Version (17.13.4)
+
+ The spanning tree priority vectors and Port Role assignments for a Bridge
+ shall be recomputed, as specified by the operation of the Port Role
+ Selection state machine (17.28) by clearing selected (17.19.36) and
+ setting reselect (17.19.34) for any Port or Ports for which the following
+ parameters are modified:
+ b) Bridge Identifier Priority (17.13.7)
+ c) Port Identifier Priority (17.13.10)
+ d) Port Path Cost (17.13.11)
+
+ If the Transmit Hold Count is modified the value of txCount (17.19.44)
+ for all Ports shall be set to zero.
+
+ The RSTP specification permits changes in other performance parameters
+ without exceptional actions.
+ */
+
+
+ /* [17.13.2 - Ageing Time]
+ The Ageing Time parameter for the Bridge (7.9.2, Table 7-5). */
+ unsigned int ageing_time;
+
+ /* [17.13.4 - Force Protocol Version]
+ The Force Protocol Version parameter for the Bridge (17.4, 14.8.1).
+ This can take the value 0 (STP Compatibility mode) or 2 (the default,
+ normal operation). */
+ enum rstp_force_protocol_version force_protocol_version;
+
+ /* [17.13.5 - Bridge Forward Delay]
+ The delay used by STP Bridges (17.4) to transition Root and Designated
+ Ports to Forwarding (Table 17-1). */
+ unsigned int bridge_forward_delay;
+
+ /* [17.13.6 - Bridge Hello Time]
+ The interval between periodic transmissions of Configuration Messages by
+ Designated Ports (Table 17-1). */
+ unsigned int bridge_hello_time;
+
+ /* [17.13.8 - Bridge Max Age]
+ The maximum age of the information transmitted by the Bridge when it is
+ the Root Bridge (Table 17-1). */
+ unsigned int bridge_max_age;
+
+ /* [17.13.9 - Migrate Time]
+ The initial value of the mdelayWhile and edgeDelayWhile timers (17.17.4,
+ 17.17.1), fixed for all RSTP implementations conforming to this
+ specification (Table 17-1). */
+ unsigned int migrate_time;
+
+ /* [17.13.12 - Transmit Hold Count]
+ The Transmit Hold Count (Table 17-1) used by the Port Transmit state
+ machine to limit transmission rate. */
+ unsigned int transmit_hold_count;
+
+
+
+ /*************************************************************************
+ * The following variable are defined in [17.18 - Per-Bridge variables]
+ ************************************************************************/
+
+ /* [17.18.1 - BEGIN]
+ A Boolean controlled by the system initialization (17.16). If TRUE causes
+ all state machines, including per Port state machines, to continuously
+ execute their initial state. */
+ bool begin;
+
+ /* [17.18.2 BridgeIdentifier]
+ The unique Bridge Identifier assigned to this Bridge, comprising two
+ components: the Bridge Identifier Priority, which may be modified by
+ management (see 9.2.5 and 14.8.1.2) and is the more significant when
+ Bridge Identifiers are compared, and a component derived from the Bridge
+ Address (7.12.5), which guarantees uniqueness of the Bridge Identifiers
+ of different Bridges. */
+ uint8_t bridge_identifier[8];
+
+ /* [17.8.3 BridgePriority]
+ The bridge priority vector, as defined in 17.6. The first (RootBridgeID)
+ and third (DesignatedBridgeID) components are both equal to the value
+ of the Bridge Identifier (17.18.2). The other components are zero. */
+ struct rstp_priority_vector bridge_priority;
+
+ /* [17.18.4 - BridgeTimes]
+ BridgeTimes comprises four components: the current values of Bridge
+ Forward Delay, Bridge Hello Time, Bridge Max Age (17.13, Table 17-1),
+ and a Message Age of zero. */
+ struct rstp_times bridge_times;
+
+ /* [17.18.6 - rootPriority]
+ The first four components of the Bridge’s root priority vector, as
+ defined in 17.6. */
+ struct rstp_priority_vector root_priority;
+
+ /* [17.18.5 - rootPortId]
+ The Port Identifier of the Root Port. This is the fifth component of
+ the root priority vector, as defined in 17.6. */
+ unsigned char root_port_id[2];
+
+ /* [17.18.7 - rootTimes]
+ The rootTimes variable comprises the Bridge's operational timer parameter
+ values (Message Age, Max Age, Forward Delay, and Hello Time), derived
+ from the values stored in portTimes (17.19.22) for the Root Port or
+ from BridgeTimes (17.18.4). */
+ struct rstp_times root_times;
+
+ /* 17.20 State machine conditions and parameters */
+
+ /* [17.20.11] rstpVersion
+ * TRUE if Force Protocol Version (17.13.4) is greater than or equal to 2. */
+ bool rstp_version;
+ /* [17.20.12] stpVersion
+ * TRUE if Force Protocol Version (17.13.4) is less than 2.
+ */
+ bool stp_version;
+
+ /* Ports */
+ struct rstp_port ports[RSTP_MAX_PORTS];
+ uint16_t ports_count;
+
+ struct ovs_refcount ref_cnt;
+
+ /* Interface to client. */
+ struct rstp_port *first_changed_port;
+ void (*send_bpdu)(struct ofpbuf *bpdu, int port_no, void *aux);
+ void *aux;
+};
+
+#endif /* rstp-common.h */
diff --git a/lib/rstp-state-machines.c b/lib/rstp-state-machines.c
new file mode 100644
index 0000000..9f96f79
--- /dev/null
+++ b/lib/rstp-state-machines.c
@@ -0,0 +1,1683 @@
+/*
+ * Copyright (c) 2011-2014 M3S, Srl - Italy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Rapid Spanning Tree Protocol (IEEE 802.1D-2004) state machines
+ * implementation.
+ *
+ * Authors:
+ * Martino Fornasa <mf at fornasa.it>
+ * Daniele Venturino <daniele.venturino at m3s.it>
+ *
+ * References to IEEE 802.1D-2004 standard are enclosed in square brackets.
+ * E.g. [17.3], [Table 17-1], etc.
+ *
+ */
+
+#include <config.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include "byte-order.h"
+#include "ofpbuf.h"
+#include "packets.h"
+#include "unixctl.h"
+#include "util.h"
+#include "vlog.h"
+#include "rstp.h"
+#include "rstp-state-machines.h"
+#include "seq.h"
+#include "connectivity.h"
+
+VLOG_DEFINE_THIS_MODULE(rstp_sm);
+
+void decrement_timer(unsigned int *);
+static void rstp_send_bpdu(struct rstp_port *, const void *, size_t)
+ OVS_REQUIRES(mutex);
+
+void
+process_received_bpdu(struct rstp_port *p, const void *bpdu, size_t bpdu_size)
+{
+ struct rstp *rstp = p->rstp;
+
+ if (!p->port_enabled)
+ return;
+ if (p->rcvd_bpdu)
+ return;
+
+ if (validate_received_bpdu(p, bpdu, bpdu_size) == 0) {
+ p->rcvd_bpdu = true;
+ p->rx_rstp_bpdu_cnt++;
+
+ memcpy(&p->received_bpdu_buffer, bpdu, sizeof(struct rstp_bpdu));
+
+ rstp->changes = true;
+ move_rstp(rstp);
+ } else {
+ VLOG_DBG("Bad BPDU received");
+ p->error_count++;
+ }
+}
+
+int
+validate_received_bpdu(struct rstp_port *p, const void *bpdu, size_t bpdu_size)
+{
+ /* Validation of received BPDU, see [9.3.4]. */
+ struct rstp_bpdu * temp;
+ temp = CONST_CAST(struct rstp_bpdu *, bpdu);
+ if (bpdu_size < 4 || temp->protocol_identifier != 0) {
+ return -1;
+ } else {
+ if (temp->bpdu_type == CONFIGURATION_BPDU && bpdu_size >= 35 && memcmp(temp->message_age, temp->max_age, sizeof(uint8_t[2])) < 0) {
+ if (memcmp(temp->priority_vector.designated_bridge_id, p->rstp->bridge_identifier, sizeof(uint8_t[8])) != 0 ||
+ (memcmp(temp->priority_vector.designated_bridge_id, p->rstp->bridge_identifier, sizeof(uint8_t[8])) == 0 &&
+ (memcmp(temp->priority_vector.designated_port_id, p->port_id, sizeof(uint8_t[2])) != 0))) {
+ return 0;
+ }
+ else {
+ return -1;
+ }
+ } else if (temp->bpdu_type == TOPOLOGY_CHANGE_NOTIFICATION) {
+ return 0;
+ } else if (temp->bpdu_type == RAPID_SPANNING_TREE_BPDU && bpdu_size >= 36) {
+ return 0;
+ }
+ else {
+ return -1;
+ }
+ }
+}
+
+/*
+* move_rstp()
+* This method is invoked to move the State Machines. The SMs move only if the
+* boolean 'changes' is true, meaning that something changed and the SMs need to
+* work to process this change.
+* The boolean 'changes' is set every time a SM modifies its state, a BPDU is
+* received, a timer expires or port down event is detected. If a parameter is set by
+* management, then 'changes' is set.
+*/
+#define MAX_RSTP_ITERATIONS 1000 /* safeguard */
+int
+move_rstp(struct rstp * rstp )
+{
+ int port_no, num_iterations;
+ num_iterations = 0;
+ while (rstp->changes == true && num_iterations < MAX_RSTP_ITERATIONS) {
+ VLOG_DBG("%s: move_rstp()", rstp->name);
+ rstp->changes = false;
+ port_role_selection_sm(rstp);
+ for (port_no = 0; port_no < RSTP_MAX_PORTS; port_no++) {
+ struct rstp_port *p = rstp_get_port(rstp, port_no);
+ if (p->rstp_state != RSTP_DISABLED) {
+ port_receive_sm(p);
+ bridge_detection_sm(p);
+ port_information_sm(p);
+ port_role_transition_sm(p);
+ port_state_transition_sm(p);
+ topology_change_sm(p);
+ port_transmit_sm(p);
+ port_protocol_migration_sm(p);
+ }
+ }
+ num_iterations++;
+ seq_change(connectivity_seq_get());
+ }
+ if (num_iterations >= MAX_RSTP_ITERATIONS) {
+ VLOG_ERR("%s: move_rstp() reached the iteration safeguard limit!", rstp->name);
+ }
+ return 0;
+}
+
+void decrease_rstp_port_timers(struct rstp * r)
+{
+ int port_no;
+ for (port_no = 0; port_no < RSTP_MAX_PORTS; port_no++) {
+ struct rstp_port *p = rstp_get_port(r, port_no);
+ decrement_timer(&p->hello_when);
+ decrement_timer(&p->tc_while);
+ decrement_timer(&p->fd_while);
+ decrement_timer(&p->rcvd_info_while);
+ decrement_timer(&p->rr_while);
+ decrement_timer(&p->rb_while);
+ decrement_timer(&p->mdelay_while);
+ decrement_timer(&p->edge_delay_while);
+ decrement_timer(&p->tx_count);
+ p->uptime+=1;
+ }
+ r->changes = true;
+ move_rstp(r);
+}
+
+void
+decrement_timer(unsigned int * timer)
+{
+ if (*timer!=0) {
+ *timer = *timer - 1;
+ }
+}
+
+/* Bridge State Machine. */
+/* [17.28] Port Role Selection state machine. */
+
+void
+updt_role_disabled_tree(struct rstp * r)
+{
+ int port_no;
+ for (port_no = 0; port_no < RSTP_MAX_PORTS; port_no++) {
+ struct rstp_port *p = rstp_get_port(r, port_no);
+ p->selected_role = ROLE_DISABLED;
+ }
+}
+
+void
+clear_reselect_tree(struct rstp * r)
+{
+ int port_no;
+ for (port_no = 0; port_no < RSTP_MAX_PORTS; port_no++) {
+ struct rstp_port *p = rstp_get_port(r, port_no);
+ p->reselect = false;
+ }
+}
+
+void
+updt_roles_tree(struct rstp * r)
+{
+ int port_no;
+ int vsel = -1;
+ struct rstp_priority_vector best_vector, candidate_vector;
+ memcpy(&best_vector, &r->bridge_priority, sizeof(struct rstp_priority_vector));
+ /* Letter c1) */
+ memcpy(&r->root_times, &r->bridge_times, sizeof(struct rstp_times));
+ /* Letters a) b) c) */
+ for (port_no = 0; port_no < RSTP_MAX_PORTS; port_no++) {
+ uint32_t old_root_path_cost;
+ unsigned int root_path_cost;
+ unsigned int n;
+ struct rstp_port *p = rstp_get_port(r, port_no);
+ if (p->info_is !=INFO_IS_RECEIVED) {
+ continue;
+ }
+ /* [17.6] */
+ memcpy(&candidate_vector, &p->port_priority, sizeof(struct rstp_priority_vector4));
+ memcpy(&candidate_vector.bridge_port_id[0], &p->port_id[0], 2);
+ memcpy(&old_root_path_cost, candidate_vector.root_path_cost, sizeof(uint32_t));
+ root_path_cost = ntohl(old_root_path_cost);
+ root_path_cost += p->port_path_cost;
+ n = htonl(root_path_cost);
+ memcpy(&candidate_vector.root_path_cost, &n, 4);
+
+ if (memcmp(&candidate_vector.designated_bridge_id[2], &r->bridge_priority.designated_bridge_id[2], 6) == 0) {
+ break;
+ }
+ if (rstp_priority_vector_is_superior(&candidate_vector, &best_vector) == SUPERIOR_ABSOLUTE ||
+ rstp_priority_vector_is_superior(&candidate_vector, &best_vector) == SUPERIOR_SAME_DES) {
+ memcpy(&best_vector, &candidate_vector, sizeof(struct rstp_priority_vector));
+ memcpy(&r->root_times, &p->port_times, sizeof(struct rstp_times));
+ r->root_times.message_age++;
+ vsel = p->port_number;
+ }
+ }
+ memcpy(&r->root_priority, &best_vector, sizeof(struct rstp_priority_vector));
+ memcpy(&r->root_port_id, &((unsigned char *)&best_vector)[sizeof(struct rstp_priority_vector4)], 2);
+ VLOG_DBG("%s: new Root is %s", r->name, get_id_string_from_uint8_t(r->root_priority.root_bridge_id, 8));
+ /* Letters d) e) */
+ for (port_no = 0; port_no < RSTP_MAX_PORTS; port_no++) {
+ struct rstp_port *p = rstp_get_port(r, port_no);
+ memcpy(&p->designated_priority_vector.root_bridge_id, &r->root_priority.root_bridge_id, 8);
+ memcpy(&p->designated_priority_vector.root_path_cost, &r->root_priority.root_path_cost, 4);
+ memcpy(&p->designated_priority_vector.designated_bridge_id, &r->bridge_identifier, 8);
+ memcpy(&p->designated_priority_vector.designated_port_id, &p->port_id, 2);
+ memcpy(&p->designated_times, &r->root_times, sizeof(struct rstp_times));
+ p->designated_times.hello_time = r->bridge_times.hello_time;
+ }
+ for (port_no = 0; port_no < RSTP_MAX_PORTS; port_no++) {
+ struct rstp_port *p = rstp_get_port(r, port_no);
+ switch (p->info_is) {
+ case INFO_IS_DISABLED:
+ p->selected_role = ROLE_DISABLED;
+ break;
+ case INFO_IS_AGED:
+ p->updt_info = true;
+ p->selected_role = ROLE_DESIGNATED;
+ break;
+ case INFO_IS_MINE:
+ p->selected_role = ROLE_DESIGNATED;
+ if ((rstp_priority_vector_is_superior(&p->port_priority, &p->designated_priority_vector) != SAME) ||
+ (memcmp(&p->designated_times, &r->root_times, sizeof(struct rstp_times)) != 0)) {
+ p->updt_info = true;
+ }
+ break;
+ case INFO_IS_RECEIVED:
+ if (vsel == p->port_number) { /* Letter i) */
+ p->selected_role = ROLE_ROOT;
+ p->updt_info = false;
+ } else if (rstp_priority_vector_is_superior(&p->designated_priority_vector, &p->port_priority) == NOT_SUPERIOR) {
+ if (memcmp(p->port_priority.designated_bridge_id, r->bridge_identifier, 8)!=0) {
+ p->selected_role = ROLE_ALTERNATE;
+ p->updt_info = false;
+ } else {
+ p->selected_role = ROLE_BACKUP;
+ p->updt_info = false;
+ }
+ } else {
+ p->selected_role = ROLE_DESIGNATED;
+ p->updt_info = true;
+ }
+ break;
+ default:
+ ovs_assert(0);
+ /* no break */
+ }
+ }
+ seq_change(connectivity_seq_get());
+}
+
+void
+set_selected_tree(struct rstp * r)
+{
+ int port_no;
+ for (port_no = 0; port_no < RSTP_MAX_PORTS; port_no++) {
+ struct rstp_port *p = rstp_get_port(r, port_no);
+ if (p->reselect) {
+ return;
+ }
+ }
+ for (port_no = 0; port_no < RSTP_MAX_PORTS; port_no++) {
+ struct rstp_port *p = rstp_get_port(r, port_no);
+ p->selected = true;
+ }
+}
+
+int
+port_role_selection_sm (struct rstp * r)
+{
+ enum port_role_selection_state_machine old_state = r->port_role_selection_sm_state;
+ int port_no;
+ struct rstp_port *p;
+ switch (r->port_role_selection_sm_state) {
+ case PORT_ROLE_SELECTION_SM_INIT:
+ if (r->begin)
+ r->port_role_selection_sm_state = PORT_ROLE_SELECTION_SM_INIT_BRIDGE_EXEC;
+ break;
+ case PORT_ROLE_SELECTION_SM_INIT_BRIDGE_EXEC:
+ updt_role_disabled_tree(r);
+ r->port_role_selection_sm_state = PORT_ROLE_SELECTION_SM_INIT_BRIDGE;
+ /* no break */
+ case PORT_ROLE_SELECTION_SM_INIT_BRIDGE:
+ r->port_role_selection_sm_state = PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC;
+ break;
+ case PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC:
+ clear_reselect_tree(r);
+ updt_roles_tree(r);
+ set_selected_tree(r);
+ r->port_role_selection_sm_state = PORT_ROLE_SELECTION_SM_ROLE_SELECTION;
+ /* no break */
+ case PORT_ROLE_SELECTION_SM_ROLE_SELECTION:
+ for (port_no = 0; port_no < RSTP_MAX_PORTS; port_no++) {
+ p = rstp_get_port(r, port_no);
+ if (p->reselect) {
+ r->port_role_selection_sm_state = PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC;
+ break;
+ }
+ }
+ break;
+ default:
+ ovs_assert(0);
+ /* no break */
+ }
+ if (old_state != r->port_role_selection_sm_state) {
+ r->changes = true;
+ VLOG_DBG("Port_role_selection_sm %d -> %d", old_state, r->port_role_selection_sm_state);
+ }
+ return 0;
+}
+
+/* Port State Machines */
+
+/* [17.23 - Port receive state machine] */
+
+void
+updt_bpdu_version(struct rstp_port * p) /* [17.21.22] */
+{
+ switch (p->received_bpdu_buffer.bpdu_type) {
+ case CONFIGURATION_BPDU:
+ case TOPOLOGY_CHANGE_NOTIFICATION:
+ p->rcvd_rstp = false;
+ p->rcvd_stp = true;
+ break;
+ case RAPID_SPANNING_TREE_BPDU:
+ p->rcvd_rstp = true;
+ p->rcvd_stp = false;
+ break;
+ default:
+ ovs_assert(0);
+ /* no break */
+ }
+}
+
+int
+port_receive_sm(struct rstp_port * p)
+{
+ enum port_receive_state_machine old_state = p->port_receive_sm_state;
+ struct rstp * r = p->rstp;
+ switch (p->port_receive_sm_state) {
+ case PORT_RECEIVE_SM_INIT:
+ if (r->begin || ((p->rcvd_bpdu || (p->edge_delay_while != r->migrate_time)) && !p->port_enabled)) {
+ p->port_receive_sm_state = PORT_RECEIVE_SM_DISCARD_EXEC;
+ }
+ break;
+ case PORT_RECEIVE_SM_DISCARD_EXEC:
+ p->rcvd_bpdu = p->rcvd_rstp = p->rcvd_stp = false;
+ p->rcvd_msg = false;
+ p->edge_delay_while = r->migrate_time;
+ p->port_receive_sm_state = PORT_RECEIVE_SM_DISCARD;
+ /* no break */
+ case PORT_RECEIVE_SM_DISCARD:
+ if (p->rcvd_bpdu && p->port_enabled) {
+ p->port_receive_sm_state = PORT_RECEIVE_SM_RECEIVE_EXEC;
+ }
+ break;
+ case PORT_RECEIVE_SM_RECEIVE_EXEC:
+ updt_bpdu_version(p);
+ p->oper_edge = p->rcvd_bpdu = false;
+ p->rcvd_msg = true;
+ p->edge_delay_while = r->migrate_time;
+ p->port_receive_sm_state = PORT_RECEIVE_SM_RECEIVE;
+ /* no break */
+ case PORT_RECEIVE_SM_RECEIVE:
+ if (p->rcvd_bpdu && p->port_enabled && !p->rcvd_msg) {
+ p->port_receive_sm_state = PORT_RECEIVE_SM_RECEIVE_EXEC;
+ }
+ break;
+ default:
+ ovs_assert(0);
+ /* no break */
+ }
+ if (old_state != p->port_receive_sm_state) {
+ r->changes = true;
+ VLOG_DBG("%s, port %u: Port_receive_sm %d -> %d", p->rstp->name, p->port_number, old_state, p->port_receive_sm_state);
+ }
+ return 0;
+}
+
+/* [17.24 - Port Protocol Migration state machine] */
+int
+port_protocol_migration_sm (struct rstp_port * p)
+{
+ enum port_protocol_migration_state_machine old_state = p->port_protocol_migration_sm_state;
+ struct rstp * r = p->rstp;
+ switch (p->port_protocol_migration_sm_state) {
+ case PORT_PROTOCOL_MIGRATION_SM_INIT:
+ p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC;
+ /* no break */
+ case PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC:
+ p->mcheck = false;
+ p->send_rstp = r->rstp_version;
+ p->mdelay_while = r->migrate_time;
+ p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP;
+ /* no break */
+ case PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP:
+ if (p->mdelay_while == 0) {
+ p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_SENSING_EXEC;
+ }
+ if ((p->mdelay_while != r->migrate_time) && !p->port_enabled) {
+ p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC;
+ }
+ break;
+ case PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP_EXEC:
+ p->send_rstp = false;
+ p->mdelay_while = r->migrate_time;
+ p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP;
+ /* no break */
+ case PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP:
+ if ((p->mdelay_while == 0) || (!p->port_enabled) || p->mcheck) {
+ p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_SENSING_EXEC;
+ }
+ break;
+ case PORT_PROTOCOL_MIGRATION_SM_SENSING_EXEC:
+ p->rcvd_rstp = false;
+ p->rcvd_stp = false;
+ p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_SENSING;
+ /* no break */
+ case PORT_PROTOCOL_MIGRATION_SM_SENSING:
+ if (!p->port_enabled || p->mcheck || ((r->rstp_version) && !p->send_rstp && p->rcvd_rstp)) {
+ p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC;
+ }
+ if (p->send_rstp && p->rcvd_stp) {
+ p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP_EXEC;
+ }
+ break;
+ default:
+ ovs_assert(0);
+ /* no break */
+ }
+ if (old_state != p->port_protocol_migration_sm_state) {
+ r->changes = true;
+ VLOG_DBG("%s, port %u: port_protocol_migration_sm %d -> %d", p->rstp->name, p->port_number, old_state, p->port_protocol_migration_sm_state);
+ }
+
+ return 0;
+}
+
+/* [17.25 - Bridge Detection state machine] */
+int
+bridge_detection_sm (struct rstp_port * p)
+{
+ enum bridge_detection_state_machine old_state = p->bridge_detection_sm_state;
+ struct rstp * r = p->rstp;
+ switch (p->bridge_detection_sm_state) {
+ case BRIDGE_DETECTION_SM_INIT:
+ if (r->begin && p->admin_edge) {
+ p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_EDGE_EXEC;
+ } else if (r->begin && !p->admin_edge) {
+ p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_NOT_EDGE_EXEC;
+ }
+ break;
+ case BRIDGE_DETECTION_SM_EDGE_EXEC:
+ p->oper_edge = true;
+ p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_EDGE;
+ /* no break */
+ case BRIDGE_DETECTION_SM_EDGE:
+ if ((!p->port_enabled && !p->admin_edge)||!p->oper_edge) {
+ p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_NOT_EDGE_EXEC;
+ }
+ break;
+ case BRIDGE_DETECTION_SM_NOT_EDGE_EXEC:
+ p->oper_edge = false;
+ p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_NOT_EDGE;
+ /* no break */
+ case BRIDGE_DETECTION_SM_NOT_EDGE:
+ if ((!p->port_enabled && p->admin_edge)|| ((p->edge_delay_while == 0)&& p->auto_edge && p->send_rstp && p->proposing)) {
+ p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_EDGE_EXEC;
+ }
+ break;
+ default:
+ ovs_assert(0);
+ /* no break */
+ }
+ if (old_state != p->bridge_detection_sm_state) {
+ r->changes = true;
+ VLOG_DBG("%s, port %u: bridge_detection_sm %d -> %d", p->rstp->name, p->port_number, old_state, p->bridge_detection_sm_state);
+ }
+ return 0;
+}
+
+/* [17.26 - Port Transmit state machine] */
+static void
+rstp_send_bpdu(struct rstp_port *p, const void *bpdu, size_t bpdu_size)
+OVS_REQUIRES(mutex)
+{
+ struct eth_header *eth;
+ struct llc_header *llc;
+ struct ofpbuf *pkt;
+
+ /* Skeleton. */
+ pkt = ofpbuf_new(ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size);
+ pkt->l2 = eth = ofpbuf_put_zeros(pkt, sizeof *eth);
+ llc = ofpbuf_put_zeros(pkt, sizeof *llc);
+ ofpbuf_set_l3(pkt, ofpbuf_put(pkt, bpdu, bpdu_size));
+
+ /* 802.2 header. */
+ memcpy(eth->eth_dst, eth_addr_rstp, ETH_ADDR_LEN);
+ /* p->rstp->send_bpdu() must fill in source address. */
+ eth->eth_type = htons(pkt->size - ETH_HEADER_LEN);
+
+ /* LLC header. */
+ llc->llc_dsap = RSTP_LLC_DSAP;
+ llc->llc_ssap = RSTP_LLC_SSAP;
+ llc->llc_cntl = RSTP_LLC_CNTL;
+ p->rstp->send_bpdu(pkt, rstp_port_no(p), p->rstp->aux);
+ p->tx_count++;
+}
+
+void
+record_agreement(struct rstp_port * p)
+{
+ struct rstp * r = p->rstp;
+ if (r->rstp_version && p->oper_point_to_point_mac && ((p->received_bpdu_buffer.flags & BPDU_FLAG_AGREEMENT) != 0)) {
+ p->agreed = true;
+ p->proposing = false;
+ } else {
+ p->agreed = false;
+ }
+}
+
+void
+set_tc_flags(struct rstp_port * p)
+{
+ /* Sets rcvd_tc and/or rcvd_tc_ack if the Topology Change and/or Topology
+ Change Acknowledgment flags, respectively, are set in a ConfigBPDU or
+ RST BPDU. */
+ if (p->received_bpdu_buffer.bpdu_type == CONFIGURATION_BPDU ||
+ p->received_bpdu_buffer.bpdu_type == RAPID_SPANNING_TREE_BPDU) {
+ if ((p->received_bpdu_buffer.flags & BPDU_FLAG_TOPCHANGE) != 0) {
+ p->rcvd_tc = true;
+ }
+ if ((p->received_bpdu_buffer.flags & BPDU_FLAG_TOPCHANGEACK) != 0) {
+ p->rcvd_tc_ack = true;
+ }
+ }
+ /* Sets rcvd_tcn true if the BPDU is a TCN BPDU. */
+ if (p->received_bpdu_buffer.bpdu_type == TOPOLOGY_CHANGE_NOTIFICATION) {
+ p->rcvd_tcn = true;
+ }
+}
+
+void
+record_dispute(struct rstp_port * p)
+{
+ if ((p->received_bpdu_buffer.flags & BPDU_FLAG_LEARNING) != 0) {
+ p->agreed = true;
+ p->proposing = false;
+ }
+}
+
+void
+record_proposal(struct rstp_port * p)
+{
+ unsigned int role = ((p->received_bpdu_buffer.flags)&0xC)>>2;
+ if ((role == PORT_DES) && ((p->received_bpdu_buffer.flags & BPDU_FLAG_PROPOSAL) != 0)) {
+ p->proposed = true;
+ }
+}
+
+void
+record_priority(struct rstp_port * p)
+{
+ memcpy(&p->port_priority, &p->msg_priority, sizeof(struct rstp_priority_vector4));
+}
+
+void
+record_times(struct rstp_port * p)
+{
+ p->port_times.message_age = p->msg_times.message_age;
+ p->port_times.max_age = p->msg_times.max_age;
+ p->port_times.forward_delay = p->msg_times.forward_delay;
+ if (p->msg_times.hello_time > 1) {
+ p->port_times.hello_time = p->msg_times.hello_time;
+ } else {
+ p->port_times.hello_time = 1;
+ }
+}
+
+void
+updt_rcvd_info_while(struct rstp_port * p)
+{
+ if (p->port_times.message_age + 1 <= p->port_times.max_age) {
+ p->rcvd_info_while = p->port_times.hello_time * 3;
+ } else {
+ p->rcvd_info_while = 0;
+ }
+
+}
+
+void
+time_encode(unsigned int value, uint8_t * encoded)
+{
+ uint16_t val = htons(value*256);
+ memcpy(encoded, &val, sizeof(uint16_t));
+}
+
+unsigned int
+time_decode(uint8_t * encoded)
+{
+ uint16_t val;
+ memcpy(&val, encoded, sizeof(uint16_t));
+ return (ntohs(val)/256);
+}
+
+/* [17.21.19] */
+void
+tx_config(struct rstp_port * p)
+{
+ struct rstp_bpdu * bpdu = (struct rstp_bpdu *) malloc(sizeof(struct rstp_bpdu));
+ bzero(bpdu, sizeof(struct rstp_bpdu));
+
+ bpdu->protocol_identifier = htons(0);
+ bpdu->protocol_version_identifier = 0;
+ bpdu->bpdu_type = CONFIGURATION_BPDU;
+ memcpy(&bpdu->priority_vector, &p->designated_priority_vector, sizeof(struct rstp_priority_vector4));
+ time_encode(p->designated_times.message_age, bpdu->message_age);
+ time_encode(p->designated_times.max_age, bpdu->max_age);
+ time_encode(p->designated_times.hello_time, bpdu->hello_time);
+ time_encode(p->designated_times.forward_delay, bpdu->forward_delay);
+ if (p->tc_while !=0) {
+ bpdu->flags |= BPDU_FLAG_TOPCHANGE;
+ }
+ if (p->tc_ack !=0) {
+ bpdu->flags |= BPDU_FLAG_TOPCHANGEACK;
+ }
+ rstp_send_bpdu(p, bpdu, sizeof(struct rstp_bpdu));
+}
+
+/* [17.21.20] */
+void
+tx_rstp(struct rstp_port * p)
+{
+ struct rstp_bpdu * bpdu = (struct rstp_bpdu *) malloc(sizeof(struct rstp_bpdu));
+ bzero(bpdu, sizeof(struct rstp_bpdu));
+
+ bpdu->protocol_identifier = htons(0);
+ bpdu->protocol_version_identifier = 2;
+ bpdu->bpdu_type = RAPID_SPANNING_TREE_BPDU;
+ memcpy(&bpdu->priority_vector, &p->designated_priority_vector, sizeof(struct rstp_priority_vector4));
+ time_encode(p->designated_times.message_age, bpdu->message_age);
+ time_encode(p->designated_times.max_age, bpdu->max_age);
+ time_encode(p->designated_times.hello_time, bpdu->hello_time);
+ time_encode(p->designated_times.forward_delay, bpdu->forward_delay);
+ switch (p->role) {
+ case ROLE_ROOT:
+ bpdu->flags = PORT_ROOT<<2;
+ break;
+ case ROLE_DESIGNATED:
+ bpdu->flags = PORT_DES<<2;
+ break;
+ case ROLE_ALTERNATE:
+ case ROLE_BACKUP:
+ bpdu->flags = PORT_ALT_BACK<<2;
+ break;
+ case ROLE_DISABLED:
+ /* should not happen! */
+ ovs_assert(0);
+ break;
+ }
+ if (p->agree) {
+ bpdu->flags |= BPDU_FLAG_AGREEMENT;
+ }
+ if (p->proposing) {
+ bpdu->flags |= BPDU_FLAG_PROPOSAL;
+ }
+ if (p->tc_while !=0) {
+ bpdu->flags |= BPDU_FLAG_TOPCHANGE;
+ }
+ if (p->learning) {
+ bpdu->flags |= BPDU_FLAG_LEARNING;
+ }
+ if (p->forwarding) {
+ bpdu->flags |= BPDU_FLAG_FORWARDING;
+ }
+ rstp_send_bpdu(p, bpdu, sizeof(struct rstp_bpdu));
+}
+
+/* [17.21.21] */
+void
+tx_tcn(struct rstp_port * p)
+{
+ struct rstp_bpdu * bpdu = (struct rstp_bpdu *) malloc(sizeof(struct rstp_bpdu));
+ bzero(bpdu, sizeof(struct rstp_bpdu));
+
+ bpdu->protocol_identifier = htons(0);
+ bpdu->protocol_version_identifier = 0;
+ bpdu->bpdu_type = TOPOLOGY_CHANGE_NOTIFICATION;
+ rstp_send_bpdu(p, bpdu, sizeof(struct rstp_bpdu));
+}
+
+int
+port_transmit_sm (struct rstp_port * p)
+{
+ enum port_transmit_state_machine old_state = p->port_transmit_sm_state;
+ struct rstp * r = p->rstp;
+ switch (p->port_transmit_sm_state) {
+ case PORT_TRANSMIT_SM_INIT:
+ if (r->begin) {
+ p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_INIT_EXEC;
+ }
+ break;
+ case PORT_TRANSMIT_SM_TRANSMIT_INIT_EXEC:
+ p->new_info = true;
+ p->tx_count = 0;
+ p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_INIT;
+ /* no break */
+ case PORT_TRANSMIT_SM_TRANSMIT_INIT:
+ p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC;
+ break;
+ case PORT_TRANSMIT_SM_TRANSMIT_PERIODIC_EXEC:
+ p->new_info = p->new_info || (p->role==ROLE_DESIGNATED || (p->role==ROLE_ROOT && p->tc_while!=0));
+ p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_PERIODIC;
+ /* no break */
+ case PORT_TRANSMIT_SM_TRANSMIT_PERIODIC:
+ p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC;
+ break;
+ case PORT_TRANSMIT_SM_IDLE_EXEC:
+ p->hello_when = r->bridge_hello_time;
+ p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE;
+ /* no break */
+ case PORT_TRANSMIT_SM_IDLE:
+ if (p->role == ROLE_DISABLED) {
+ break;
+ }
+ if (p->send_rstp && p->new_info && (p->tx_count < r->transmit_hold_count) && (p->hello_when != 0) && p->selected && !p->updt_info) {
+ p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_RSTP_EXEC;
+ }
+ if (p->hello_when == 0 && p->selected && !p->updt_info) {
+ p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_PERIODIC_EXEC;
+ }
+ if (!p->send_rstp && p->new_info && (p->role == ROLE_ROOT) && (p->tx_count < r->transmit_hold_count) && (p->hello_when != 0) && p->selected && !p->updt_info) {
+ p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_TCN_EXEC;
+ }
+ if (!p->send_rstp && p->new_info && (p->role == ROLE_DESIGNATED) && (p->tx_count < r->transmit_hold_count) && (p->hello_when != 0) && p->selected && !p->updt_info) {
+ p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_CONFIG_EXEC;
+ }
+ break;
+ case PORT_TRANSMIT_SM_TRANSMIT_CONFIG_EXEC:
+ p->new_info = false;
+ tx_config(p);
+ p->tx_count += 1;
+ p->tc_ack = false;
+ p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_CONFIG;
+ /* no break */
+ case PORT_TRANSMIT_SM_TRANSMIT_CONFIG:
+ p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC;
+ break;
+ case PORT_TRANSMIT_SM_TRANSMIT_TCN_EXEC:
+ p->new_info = false;
+ tx_tcn(p);
+ p->tx_count +=1;
+ p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_TCN;
+ /* no break */
+ case PORT_TRANSMIT_SM_TRANSMIT_TCN:
+ p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC;
+ break;
+ case PORT_TRANSMIT_SM_TRANSMIT_RSTP_EXEC:
+ p->new_info = false;
+ tx_rstp(p);
+ p->tx_count++;
+ p->tc_ack = false;
+ p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_RSTP;
+ /* no break */
+ case PORT_TRANSMIT_SM_TRANSMIT_RSTP:
+ p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC;
+ break;
+ default:
+ ovs_assert(0);
+ /* no break */
+ }
+ if (old_state != p->port_transmit_sm_state) {
+ r->changes = true;
+ VLOG_DBG("%s, port %u: port_transmit_sm %d -> %d", p->rstp->name, p->port_number, old_state, p->port_transmit_sm_state);
+ }
+ return 0;
+}
+
+/* [17.27 Port Information state machine] */
+#define RECEIVED 0
+#define MINE 1
+
+int
+rcv_info(struct rstp_port * p)
+{
+ int cp, ct;
+ unsigned int role;
+
+ memcpy(&p->msg_priority, &p->received_bpdu_buffer.priority_vector, sizeof(struct rstp_priority_vector4));
+ p->msg_times.forward_delay = time_decode(p->received_bpdu_buffer.forward_delay);
+ p->msg_times.hello_time = time_decode(p->received_bpdu_buffer.hello_time);
+ p->msg_times.max_age = time_decode(p->received_bpdu_buffer.max_age);
+ p->msg_times.message_age = time_decode(p->received_bpdu_buffer.message_age);
+
+ cp = rstp_priority_vector_is_superior(&p->msg_priority, &p->port_priority);
+ ct = memcmp(&p->port_times, &p->msg_times, sizeof(struct rstp_times));
+ role = ((p->received_bpdu_buffer.flags)&0xC)>>2;
+
+ /*Returns SuperiorDesignatedInfo if:
+ a) The received message conveys a Designated Port Role, and
+ 1) The message priority is superior (17.6) to the Port.s port priority
+ vector, or
+ 2) The message priority vector is the same as the Port.s port priority
+ vector, and any of the received timer parameter values (msg_times.
+ 17.19.15) differ from those already held for the Port (port_times
+ 17.19.22).
+ NOTE: Configuration BPDU explicitly conveys a Designated Port Role.*/
+ if ((role == PORT_DES || p->received_bpdu_buffer.bpdu_type == CONFIGURATION_BPDU) && ((cp == SUPERIOR_ABSOLUTE) || (cp == SUPERIOR_SAME_DES) || ((cp == SAME) && ct != 0))) {
+ return SUPERIOR_DESIGNATED_INFO;
+ }
+
+ /*Returns RepeatedDesignatedInfo if:
+ b) The received message conveys Designated Port Role, and a message
+ priority vector and timer parameters that are the same as the Port's
+ port priority vector or timer values.*/
+ else if ((role == PORT_DES) && (cp == SAME) && (ct == 0)) {
+ return REPEATED_DESIGNATED_INFO;
+ }
+
+ /*Returns InferiorDesignatedInfo if:
+ c) The received message conveys a Designated Port Role, and a message
+ priority vector that is worse than the Port.s port priority vector.*/
+ else if ((role == PORT_DES) && (cp == NOT_SUPERIOR)) {
+ return INFERIOR_DESIGNATED_INFO;
+ }
+
+ /*Returns InferiorRootAlternateInfo if:
+ d) The received message conveys a Root Port, Alternate Port, or Backup
+ Port Role and a message priority that is the same as or worse than the
+ port priority vector.*/
+ else if ((role == PORT_ROOT || role == PORT_ALT_BACK) && (cp == NOT_SUPERIOR || cp == SAME)) {
+ return INFERIOR_ROOT_ALTERNATE_INFO;
+ }
+
+ //Otherwise, returns OtherInfo.
+ else {
+ return OTHER_INFO;
+ }
+}
+
+int
+better_or_same_info(struct rstp_port * p, int new_info_is)
+{
+ /* >= SUPERIOR_ABSOLUTE means that the vector is better or the same. */
+ if ((new_info_is == RECEIVED && p->info_is == INFO_IS_RECEIVED && rstp_priority_vector_is_superior(&p->msg_priority, &p->port_priority) >= SUPERIOR_ABSOLUTE)
+ || (new_info_is == MINE && p->info_is == INFO_IS_MINE && rstp_priority_vector_is_superior(&p->designated_priority_vector, &p->port_priority) >= SUPERIOR_ABSOLUTE)) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+int
+port_information_sm (struct rstp_port * p)
+{
+ enum port_information_state_machine old_state = p->port_information_sm_state;
+ struct rstp * r = p->rstp;
+ if (!p->port_enabled && (p->info_is!=INFO_IS_DISABLED)) {
+ p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
+ }
+ switch (p->port_information_sm_state) {
+ case PORT_INFORMATION_SM_INIT:
+ if (r->begin) {
+ p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
+ }
+ break;
+ case PORT_INFORMATION_SM_DISABLED_EXEC:
+ p->rcvd_msg = false;
+ p->proposing = p->proposed = p->agree = p->agreed = false;
+ p->rcvd_info_while = 0;
+ p->info_is = INFO_IS_DISABLED;
+ p->reselect = true;
+ p->selected = false;
+ p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED;
+ /* no break */
+ case PORT_INFORMATION_SM_DISABLED:
+ if (p->port_enabled) {
+ p->port_information_sm_state = PORT_INFORMATION_SM_AGED_EXEC;
+ }
+ if (p->rcvd_msg) {
+ p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
+ }
+ break;
+ case PORT_INFORMATION_SM_AGED_EXEC:
+ p->info_is = INFO_IS_AGED;
+ p->reselect = true;
+ p->selected = false;
+ p->port_information_sm_state = PORT_INFORMATION_SM_AGED;
+ /* no break */
+ case PORT_INFORMATION_SM_AGED:
+ if (p->selected && p->updt_info) {
+ p->port_information_sm_state = PORT_INFORMATION_SM_UPDATE_EXEC;
+ }
+ break;
+ case PORT_INFORMATION_SM_UPDATE_EXEC:
+ p->proposing = p->proposed = false;
+ p->agreed = p->agreed && better_or_same_info(p, MINE); /* MINE is not specified in Standard 802.1D-2004. */
+ p->synced = p->synced && p->agreed;
+ memcpy(&p->port_priority, &p->designated_priority_vector, sizeof(struct rstp_priority_vector4));
+ memcpy(&p->port_times, &p->designated_times, sizeof(struct rstp_times));
+ p->updt_info = false;
+ p->info_is = INFO_IS_MINE;
+ p->new_info = true;
+ p->port_information_sm_state = PORT_INFORMATION_SM_UPDATE;
+ /* no break */
+ case PORT_INFORMATION_SM_UPDATE:
+ p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
+ break;
+ case PORT_INFORMATION_SM_CURRENT_EXEC:
+ p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT;
+ /* no break */
+ case PORT_INFORMATION_SM_CURRENT:
+ if (p->rcvd_msg && !p->updt_info) {
+ p->port_information_sm_state = PORT_INFORMATION_SM_RECEIVE_EXEC;
+ } else if (p->selected && p->updt_info) {
+ p->port_information_sm_state = PORT_INFORMATION_SM_UPDATE_EXEC;
+ } else if ( (p->info_is == INFO_IS_RECEIVED) && (p->rcvd_info_while == 0) && !p->updt_info && !p->rcvd_msg) {
+ p->port_information_sm_state = PORT_INFORMATION_SM_AGED_EXEC;
+ }
+ break;
+ case PORT_INFORMATION_SM_RECEIVE_EXEC:
+ p->rcvd_info = rcv_info(p);
+ p->port_information_sm_state = PORT_INFORMATION_SM_RECEIVE;
+ /* no break */
+ case PORT_INFORMATION_SM_RECEIVE:
+ switch (p->rcvd_info) {
+ case SUPERIOR_DESIGNATED_INFO:
+ p->port_information_sm_state = PORT_INFORMATION_SM_SUPERIOR_DESIGNATED_EXEC;
+ break;
+ case REPEATED_DESIGNATED_INFO:
+ p->port_information_sm_state = PORT_INFORMATION_SM_REPEATED_DESIGNATED_EXEC;
+ break;
+ case INFERIOR_DESIGNATED_INFO:
+ p->port_information_sm_state = PORT_INFORMATION_SM_INFERIOR_DESIGNATED_EXEC;
+ break;
+ case INFERIOR_ROOT_ALTERNATE_INFO:
+ p->port_information_sm_state = PORT_INFORMATION_SM_NOT_DESIGNATED_EXEC;
+ break;
+ case OTHER_INFO:
+ p->port_information_sm_state = PORT_INFORMATION_SM_OTHER_EXEC;
+ break;
+ default:
+ ovs_assert(0);
+ /* no break */
+ }
+ break;
+ case PORT_INFORMATION_SM_OTHER_EXEC:
+ p->rcvd_msg = false;
+ p->port_information_sm_state = PORT_INFORMATION_SM_OTHER;
+ /* no break */
+ case PORT_INFORMATION_SM_OTHER:
+ p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
+ break;
+ case PORT_INFORMATION_SM_NOT_DESIGNATED_EXEC:
+ record_agreement(p);
+ set_tc_flags(p);
+ p->rcvd_msg = false;
+ p->port_information_sm_state = PORT_INFORMATION_SM_NOT_DESIGNATED;
+ /* no break */
+ case PORT_INFORMATION_SM_NOT_DESIGNATED:
+ p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
+ break;
+ case PORT_INFORMATION_SM_INFERIOR_DESIGNATED_EXEC:
+ record_dispute(p);
+ p->rcvd_msg = false;
+ p->port_information_sm_state = PORT_INFORMATION_SM_INFERIOR_DESIGNATED;
+ /* no break */
+ case PORT_INFORMATION_SM_INFERIOR_DESIGNATED:
+ p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
+ break;
+ case PORT_INFORMATION_SM_REPEATED_DESIGNATED_EXEC:
+ record_proposal(p);
+ set_tc_flags(p);
+ updt_rcvd_info_while(p);
+ p->rcvd_msg = false;
+ p->port_information_sm_state = PORT_INFORMATION_SM_REPEATED_DESIGNATED;
+ /* no break */
+ case PORT_INFORMATION_SM_REPEATED_DESIGNATED:
+ p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
+ break;
+ case PORT_INFORMATION_SM_SUPERIOR_DESIGNATED_EXEC:
+ p->agreed = p->proposing = false;
+ record_proposal(p);
+ set_tc_flags(p);
+ p->agree = p->agree && better_or_same_info(p, RECEIVED); /* RECEIVED is not specified in Standard 802.1D-2004. */
+ record_priority(p);
+ record_times(p);
+ updt_rcvd_info_while(p);
+ p->info_is = INFO_IS_RECEIVED;
+ p->reselect = true;
+ p->selected = false;
+ p->rcvd_msg = false;
+ p->port_information_sm_state = PORT_INFORMATION_SM_SUPERIOR_DESIGNATED;
+ /* no break */
+ case PORT_INFORMATION_SM_SUPERIOR_DESIGNATED:
+ p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
+ break;
+ default:
+ ovs_assert(0);
+ /* no break */
+ }
+ if (old_state != p->port_information_sm_state) {
+ r->changes = true;
+ VLOG_DBG("Port_information_sm %d -> %d", old_state, p->port_information_sm_state);
+ }
+ return 0;
+}
+
+/* [17.29 Port Role Transitions state machine] */
+
+void
+set_re_root_tree(struct rstp_port * p)
+{
+ struct rstp * r = p->rstp;
+ int port_no;
+ for (port_no = 0; port_no < RSTP_MAX_PORTS; port_no++) {
+ struct rstp_port *p1 = rstp_get_port(r, port_no);
+ p1->re_root = true;
+ }
+}
+
+void
+set_sync_tree(struct rstp_port * p)
+{
+ struct rstp * r = p->rstp;
+ int port_no;
+ for (port_no = 0; port_no < RSTP_MAX_PORTS; port_no++) {
+ struct rstp_port *p1 = rstp_get_port(r, port_no);
+ p1->sync = true;
+ }
+}
+
+int
+hello_time(struct rstp_port * p)
+{
+ return p->designated_times.hello_time;
+}
+
+int
+fwd_delay(struct rstp_port * p)
+{
+ return p->designated_times.forward_delay;
+}
+
+
+int
+forward_delay (struct rstp_port * p)
+{
+ if (p->send_rstp) {
+ return hello_time(p);
+ } else {
+ return fwd_delay(p);
+ }
+}
+
+int
+edge_delay (struct rstp_port * p)
+{
+ struct rstp * r = p->rstp;
+ if (p->oper_point_to_point_mac == 1) {
+ return r->migrate_time;
+ } else {
+ return p->designated_times.max_age;
+ }
+}
+
+int
+check_selected_role_change(struct rstp_port * p, int current_role_state)
+{
+ if (p->selected && !p->updt_info && (p->role != p->selected_role) && (p->selected_role != current_role_state)) {
+ switch (p->selected_role) {
+ VLOG_DBG("%s, port %u: Entering case. current: %s role: %s selected: %d",
+ p->rstp->name, p->port_number, rstp_port_role_name(current_role_state),
+ rstp_port_role_name(p->role), p->selected_role);
+ case ROLE_ROOT:
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
+ return true;
+ case ROLE_DESIGNATED:
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
+ return true;
+ case ROLE_ALTERNATE:
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_BLOCK_PORT_EXEC;
+ return true;
+ case ROLE_BACKUP:
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_BLOCK_PORT_EXEC;
+ return true;
+ case ROLE_DISABLED:
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DISABLE_PORT_EXEC;
+ return true;
+ }
+ }
+ return false;
+}
+
+int
+re_rooted(struct rstp_port * p)
+{
+ struct rstp * r = p->rstp;
+ int port_no;
+ for (port_no = 0; port_no < RSTP_MAX_PORTS; port_no++) {
+ struct rstp_port *p1 = rstp_get_port(r, port_no);
+ if ((p1 != p) && (p1->rr_while != 0)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+int
+all_synced(struct rstp * r)
+{
+ int port_no;
+ for (port_no = 0; port_no < RSTP_MAX_PORTS; port_no++) {
+ struct rstp_port *p = rstp_get_port(r, port_no);
+ if (!(p->selected && p->role == p->selected_role && (p->role == ROLE_ROOT || p->synced == true))) {
+ return false;
+ }
+ }
+ return true;
+}
+
+int
+port_role_transition_sm (struct rstp_port * p)
+{
+ enum port_role_transition_state_machine old_state = p->port_role_transition_sm_state;
+ struct rstp * r = p->rstp;
+ enum rstp_port_role last_role = p->role;
+ switch (p->port_role_transition_sm_state) {
+ case PORT_ROLE_TRANSITION_SM_INIT:
+ if (r->begin) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_INIT_PORT_EXEC;
+ }
+ break;
+ case PORT_ROLE_TRANSITION_SM_INIT_PORT_EXEC:
+ p->role = ROLE_DISABLED;
+ p->learn = p->forward = false;
+ p->synced = false;
+ p->sync = p->re_root = true;
+ p->rr_while = p->designated_times.forward_delay;
+ p->fd_while = p->designated_times.max_age;
+ p->rb_while = 0;
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DISABLE_PORT_EXEC;
+ break;
+ case PORT_ROLE_TRANSITION_SM_DISABLE_PORT_EXEC:
+ p->role = p->selected_role;
+ p->learn = p->forward = false;
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DISABLE_PORT;
+ /* no break */
+ case PORT_ROLE_TRANSITION_SM_DISABLE_PORT:
+ if (check_selected_role_change(p, ROLE_DISABLED)) {
+ break;
+ }
+ if (p->selected && !p->updt_info && !p->learning && !p->forwarding) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DISABLED_PORT_EXEC;
+ }
+ break;
+ case PORT_ROLE_TRANSITION_SM_DISABLED_PORT_EXEC:
+ p->fd_while = p->designated_times.max_age;
+ p->synced = true;
+ p->rr_while = 0;
+ p->sync = p->re_root = false;
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DISABLED_PORT;
+ /* no break */
+ case PORT_ROLE_TRANSITION_SM_DISABLED_PORT:
+ if (check_selected_role_change(p, ROLE_DISABLED)) {
+ break;
+ }
+ if (p->selected && !p->updt_info &&
+ ((p->fd_while != p->designated_times.max_age)||p->sync||p->re_root||!p->synced)) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DISABLED_PORT_EXEC;
+ }
+ break;
+ case PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC:
+ p->role = ROLE_ROOT;
+ p->rr_while = p->designated_times.forward_delay;
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT;
+ /* no break */
+ case PORT_ROLE_TRANSITION_SM_ROOT_PORT:
+ if (check_selected_role_change(p, ROLE_ROOT)) {
+ break;
+ }
+ if (p->selected && !p->updt_info) {
+ if (p->rr_while != p->designated_times.forward_delay) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
+ break;
+ }
+ if (p->re_root && p->forward) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_REROOTED_EXEC;
+ break;
+ }
+ if (((p->fd_while == 0) || ((re_rooted(p) && (p->rb_while == 0)) && (r->rstp_version))) && !p->learn) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_LEARN_EXEC;
+ break;
+ }
+ if (((p->fd_while==0) || ((re_rooted(p) && (p->rb_while == 0)) && (r->rstp_version))) && p->learn && !p->forward) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_FORWARD_EXEC;
+ break;
+ }
+ if (p->proposed && !p->agree) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PROPOSED_EXEC;
+ break;
+ }
+ if ((all_synced(r) && !p->agree) || (p->proposed && p->agree)) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_AGREED_EXEC;
+ break;
+ }
+ if (!p->forward && !p->re_root) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_REROOT_EXEC;
+ break;
+ }
+ }
+ break;
+ case PORT_ROLE_TRANSITION_SM_REROOT_EXEC:
+ set_re_root_tree(p);
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
+ break;
+ case PORT_ROLE_TRANSITION_SM_ROOT_AGREED_EXEC:
+ p->proposed = p->sync = false;
+ p->agree = p->new_info = true;
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
+ break;
+ case PORT_ROLE_TRANSITION_SM_ROOT_PROPOSED_EXEC:
+ set_sync_tree(p);
+ p->proposed = false;
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
+ break;
+ case PORT_ROLE_TRANSITION_SM_ROOT_FORWARD_EXEC:
+ p->fd_while = 0;
+ p->forward = true;
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
+ break;
+ case PORT_ROLE_TRANSITION_SM_ROOT_LEARN_EXEC:
+ p->fd_while = forward_delay(p);
+ p->learn = true;
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
+ break;
+ case PORT_ROLE_TRANSITION_SM_REROOTED_EXEC:
+ p->re_root = false;
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
+ break;
+ case PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC:
+ p->role = ROLE_DESIGNATED;
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT;
+ /* no break */
+ case PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT:
+ if (check_selected_role_change(p, ROLE_DESIGNATED)) {
+ break;
+ }
+ if (p->selected && !p->updt_info) {
+ if (((p->sync && !p->synced) || (p->re_root && (p->rr_while !=0)) || p->disputed) &&
+ !p->oper_edge && (p->learn || p->forward)) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_DISCARD_EXEC;
+ }
+ if (((p->fd_while==0)|| p->agreed || p->oper_edge) && ((p->rr_while==0) || !p->re_root) &&
+ !p->sync && !p->learn) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_LEARN_EXEC;
+ }
+ if (((p->fd_while == 0) || p->agreed || p->oper_edge) && ((p->rr_while == 0) || !p->re_root) &&
+ !p->sync && (p->learn && !p->forward)) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_FORWARD_EXEC;
+ }
+ if (!p->forward && !p->agreed && !p->proposing && !p->oper_edge) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_PROPOSE_EXEC;
+ }
+ if ((!p->learning && !p->forwarding && !p->synced) || (p->agreed && !p->synced) ||
+ (p->oper_edge && !p->synced) || (p->sync && p->synced)) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_SYNCED_EXEC;
+ }
+ if ((p->rr_while == 0) && p->re_root) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_RETIRED_EXEC;
+ }
+ }
+ break;
+ case PORT_ROLE_TRANSITION_SM_DESIGNATED_RETIRED_EXEC:
+ p->re_root = false;
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
+ break;
+ case PORT_ROLE_TRANSITION_SM_DESIGNATED_SYNCED_EXEC:
+ p->rr_while = 0;
+ p->synced = true;
+ p->sync = false;
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
+ break;
+ case PORT_ROLE_TRANSITION_SM_DESIGNATED_PROPOSE_EXEC:
+ p->proposing = true;
+ p->edge_delay_while = edge_delay(p);
+ p->new_info = true;
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
+ break;
+ case PORT_ROLE_TRANSITION_SM_DESIGNATED_FORWARD_EXEC:
+ p->forward = true;
+ p->fd_while = 0;
+ p->agreed = p->send_rstp;
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
+ break;
+ case PORT_ROLE_TRANSITION_SM_DESIGNATED_LEARN_EXEC:
+ p->learn = true;
+ p->fd_while = forward_delay(p);
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
+ break;
+ case PORT_ROLE_TRANSITION_SM_DESIGNATED_DISCARD_EXEC:
+ p->learn = p->forward = p->disputed = false;
+ p->fd_while = forward_delay(p);
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
+ break;
+ case PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC:
+ p->fd_while = p->designated_times.forward_delay;
+ p->synced = true;
+ p->rr_while = 0;
+ p->sync = p->re_root = false;
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT;
+ /* no break */
+ case PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT:
+ if (check_selected_role_change(p, ROLE_ALTERNATE)) {
+ break;
+ }
+ if (p->selected && !p->updt_info) {
+ if ((p->rb_while != (2*p->designated_times.hello_time)) && (p->role == ROLE_BACKUP)) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_BACKUP_PORT_EXEC;
+ }
+ if ((p->fd_while != forward_delay(p)) || p->sync || p->re_root || !p->synced) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
+ }
+ if (p->proposed && !p->agree) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ALTERNATE_PROPOSED_EXEC;
+ }
+ if (( all_synced(r) && !p->agree) || (p->proposed && p->agree)) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ALTERNATE_AGREED;
+ }
+ }
+ break;
+ case PORT_ROLE_TRANSITION_SM_ALTERNATE_AGREED:
+ p->proposed = false;
+ p->agree = true;
+ p->new_info = true;
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
+ break;
+ case PORT_ROLE_TRANSITION_SM_ALTERNATE_PROPOSED_EXEC:
+ set_sync_tree(p);
+ p->proposed = false;
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
+ break;
+ case PORT_ROLE_TRANSITION_SM_BLOCK_PORT_EXEC:
+ p->role = p->selected_role;
+ p->learn = p->forward = false;
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_BLOCK_PORT;
+ /* no break */
+ case PORT_ROLE_TRANSITION_SM_BLOCK_PORT:
+ if (check_selected_role_change(p, ROLE_ALTERNATE)) {
+ break;
+ }
+ if (p->selected && !p->updt_info && !p->learning && !p->forwarding) {
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
+ }
+ break;
+ case PORT_ROLE_TRANSITION_SM_BACKUP_PORT_EXEC:
+ p->rb_while = 2 * p->designated_times.hello_time;
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
+ break;
+ default:
+ ovs_assert(0);
+ /* no break */
+ }
+ if (old_state != p->port_role_transition_sm_state) {
+ r->changes = true;
+ VLOG_DBG("%s, port %u: Port_role_transition_sm %d -> %d", p->rstp->name, p->port_number, old_state, p->port_role_transition_sm_state);
+ }
+ if (last_role != p->role) {
+ last_role = p->role;
+ VLOG_DBG("%s, port %u, port role [%s] = %s", p->rstp->name, p->port_number, get_id_string_from_uint8_t(p->port_id, 2), rstp_port_role_name(p->role));
+ }
+ return 0;
+}
+
+/* [17.30 - Port state transition state machine] */
+
+void
+enable_learning(struct rstp_port * p)
+{
+ /* [17.21.6 enableLearning()] An implementation dependent procedure that
+ causes the Learning Process (7.8) to start learning from frames received
+ on the Port. The procedure does not complete until learning has been
+ enabled. */
+ rstp_port_set_state(p, RSTP_LEARNING);
+ /* setLearning(p->index); done in update_rstp_port_state() in ofproto-dpif.c */
+}
+
+void
+enable_forwarding(struct rstp_port * p)
+{
+ /* [17.21.5 enableForwarding()] An implementation dependent procedure that
+ causes the Forwarding Process (7.7) to start forwarding frames through the
+ Port. The procedure does not complete until forwarding has been enabled. */
+ rstp_port_set_state(p, RSTP_FORWARDING);
+ /* setForwarding(p->index); done in update_rstp_port_state() in ofproto-dpif.c */
+}
+
+void
+disable_learning(struct rstp_port * p)
+{
+ /* [17.21.4 - disableLearning()] An implementation dependent procedure that
+ causes the Learning Process (7.8) to stop learning from the source address
+ of frames received on the Port. The procedure does not complete until
+ learning has stopped. */
+ rstp_port_set_state(p, RSTP_DISCARDING);
+ /* setDiscarding(p->index); done in update_rstp_port_state() in ofproto-dpif.c */
+}
+
+void
+disable_forwarding(struct rstp_port * p)
+{
+ /* [17.21.3 - disableForwarding()] An implementation dependent procedure
+ that causes the Forwarding Process (7.7) to stop forwarding frames through
+ the Port. The procedure does not complete until forwarding has stopped. */
+ rstp_port_set_state(p, RSTP_DISCARDING);
+ /* setDiscarding(p->index); done in update_rstp_port_state() in ofproto-dpif.c */
+}
+
+int
+port_state_transition_sm (struct rstp_port * p)
+{
+ enum port_state_transition_state_machine old_state = p->port_state_transition_sm_state;
+ struct rstp * r = p->rstp;
+ switch (p->port_state_transition_sm_state) {
+ case PORT_STATE_TRANSITION_SM_INIT:
+ if (r->begin) {
+ p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_DISCARDING_EXEC;
+ }
+ break;
+ case PORT_STATE_TRANSITION_SM_DISCARDING_EXEC:
+ disable_learning(p);
+ p->learning = false;
+ disable_forwarding(p);
+ p->forwarding = false;
+ p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_DISCARDING;
+ /* no break */
+ case PORT_STATE_TRANSITION_SM_DISCARDING:
+ if (p->learn) {
+ p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_LEARNING_EXEC;
+ }
+ break;
+ case PORT_STATE_TRANSITION_SM_LEARNING_EXEC:
+ enable_learning(p);
+ p->learning = true;
+ p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_LEARNING;
+ /* no break */
+ case PORT_STATE_TRANSITION_SM_LEARNING:
+ if (!p->learn) {
+ p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_DISCARDING_EXEC;
+ }
+ if (p->forward) {
+ p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_FORWARDING_EXEC;
+ }
+ break;
+ case PORT_STATE_TRANSITION_SM_FORWARDING_EXEC:
+ enable_forwarding(p);
+ p->forwarding = true;
+ p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_FORWARDING;
+ /* no break */
+ case PORT_STATE_TRANSITION_SM_FORWARDING:
+ if (!p->forward) {
+ p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_DISCARDING_EXEC;
+ }
+ break;
+ default:
+ ovs_assert(0);
+ /* no break */
+ }
+ if (old_state != p->port_state_transition_sm_state) {
+ r->changes = true;
+ VLOG_DBG("%s, port %u: Port_state_transition_sm %d -> %d", p->rstp->name, p->port_number, old_state, p->port_state_transition_sm_state);
+ }
+ return 0;
+}
+
+/* [17.31 - Topology Change state machine] */
+
+void
+new_tc_while(struct rstp_port * p)
+{
+ struct rstp * r = p->rstp;
+ if (p->tc_while == 0 && p->send_rstp == true) {
+ p->tc_while = r->bridge_hello_time + 1;
+ p->new_info = true;
+ }
+ if (p->tc_while == 0 && p->send_rstp == false) {
+ p->tc_while = r->bridge_max_age + r->bridge_forward_delay;
+ }
+}
+
+/* [17.21.18 setTcPropTree()]
+ Sets tcprop for all Ports except the Port that called the procedure.
+*/
+void
+set_tc_prop_tree(struct rstp_port * p)
+{
+ struct rstp * r = p->rstp;
+ int port_no;
+ for (port_no = 0; port_no < RSTP_MAX_PORTS; port_no++) {
+ struct rstp_port *p1 = rstp_get_port(r, port_no);
+ /* Set tc_prop on every port, except the one calling this function. */
+ if (p1->port_number != p->port_number) {
+ p1->tc_prop = true;
+ }
+ }
+}
+
+void
+set_tc_prop_bridge(struct rstp_port * p) /* not specified in 802.1D-2004. */
+{
+ set_tc_prop_tree(p); /* see 802.1w-2001. */
+}
+
+int
+topology_change_sm (struct rstp_port * p)
+{
+ enum topology_change_state_machine old_state = p->topology_change_sm_state;
+ struct rstp * r = p->rstp;
+ switch (p->topology_change_sm_state) {
+ case TOPOLOGY_CHANGE_SM_INIT:
+ if (r->begin) {
+ p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_INACTIVE_EXEC;
+ }
+ break;
+ case TOPOLOGY_CHANGE_SM_INACTIVE_EXEC:
+ p->fdb_flush = true;
+ p->tc_while = 0;
+ p->tc_ack = false;
+ p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_INACTIVE;
+ /* no break */
+ case TOPOLOGY_CHANGE_SM_INACTIVE:
+ if (p->learn && !p->fdb_flush) {
+ p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING_EXEC;
+ }
+ break;
+ case TOPOLOGY_CHANGE_SM_LEARNING_EXEC:
+ p->rcvd_tc = p->rcvd_tcn = p->rcvd_tc_ack = false;
+ p->tc_prop = p->rcvd_tc_ack = false;
+ p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING;
+ /* no break */
+ case TOPOLOGY_CHANGE_SM_LEARNING:
+ if ((p->role != ROLE_ROOT) && (p->role != ROLE_DESIGNATED) && !(p->learn || p->learning) && !(p->rcvd_tc || p->rcvd_tcn || p->rcvd_tc_ack || p->tc_prop)) {
+ p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_INACTIVE_EXEC;
+ }
+ if (p->rcvd_tc || p->rcvd_tcn || p->rcvd_tc_ack || p->tc_prop) {
+ p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING_EXEC;
+ }
+ if (((p->role == ROLE_ROOT) || (p->role == ROLE_DESIGNATED)) && p->forward && !p->oper_edge) {
+ p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_DETECTED_EXEC;
+ }
+ break;
+ case TOPOLOGY_CHANGE_SM_DETECTED_EXEC:
+ new_tc_while(p);
+ set_tc_prop_tree(p);
+ p->new_info = true;
+ p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE_EXEC;
+ /* no break */
+ case TOPOLOGY_CHANGE_SM_ACTIVE_EXEC:
+ p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE;
+ /* no break */
+ case TOPOLOGY_CHANGE_SM_ACTIVE:
+ if (((p->role!=ROLE_ROOT) && (p->role!=ROLE_DESIGNATED)) || p->oper_edge) {
+ p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING_EXEC;
+ }
+ if (p->rcvd_tcn) {
+ p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_NOTIFIED_TCN_EXEC;
+ }
+ if (p->rcvd_tc) {
+ p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_NOTIFIED_TC_EXEC;
+ }
+ if (p->tc_prop && !p->oper_edge) {
+ p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_PROPAGATING_EXEC;
+ }
+ if (p->rcvd_tc_ack) {
+ p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACKNOWLEDGED_EXEC;
+ }
+ break;
+ case TOPOLOGY_CHANGE_SM_ACKNOWLEDGED_EXEC:
+ p->tc_while = 0;
+ p->rcvd_tc_ack = false;
+ p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE;
+ break;
+ case TOPOLOGY_CHANGE_SM_PROPAGATING_EXEC:
+ new_tc_while(p);
+ p->fdb_flush = true;
+ p->tc_prop = false;
+ p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE;
+ break;
+ case TOPOLOGY_CHANGE_SM_NOTIFIED_TC_EXEC:
+ p->rcvd_tcn = p->rcvd_tc = false;
+ if (p->role == ROLE_DESIGNATED) {
+ p->tc_ack = true;
+ }
+ set_tc_prop_bridge(p);
+ p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE;
+ break;
+ case TOPOLOGY_CHANGE_SM_NOTIFIED_TCN_EXEC:
+ new_tc_while(p);
+ p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE;
+ break;
+ default:
+ ovs_assert(0);
+ /* no break */
+ }
+ if (old_state != p->topology_change_sm_state) {
+ r->changes = true;
+ VLOG_DBG("Topology_change_sm %d -> %d", old_state, p->topology_change_sm_state);
+ }
+ return 0;
+}
+
+/****************************************************************************
+ * [17.6] Priority vector calculation helper functions
+ ****************************************************************************/
+
+/* [17.6]
+ This message priority vector is superior to the port priority vector and
+ will replace it if, and only if, the message priority vector is better than
+ the port priority vector, or the message has been transmitted from the same
+ Designated Bridge and Designated Port as the port priority vector, i.e.,
+ if the following is true:
+ ((RD < RootBridgeID)) ||
+ ((RD == RootBridgeID) && (RPCD < RootPathCost)) ||
+ ((RD == RootBridgeID) && (RPCD == RootPathCost) && (D < designated_bridge_id)) ||
+ ((RD == RootBridgeID) && (RPCD == RootPathCost) && (D == designated_bridge_id) && (PD < designated_port_id)) ||
+ ((D == designated_bridge_id.BridgeAddress) && (PD == designated_port_id.PortNumber))
+*/
+int rstp_priority_vector_is_superior(struct rstp_priority_vector * v1, struct rstp_priority_vector * v2)
+{
+ if (memcmp(v1, v2, sizeof(struct rstp_priority_vector4)) < 0) {
+ return SUPERIOR_ABSOLUTE;
+ }
+ else if ((memcmp(v1, v2, sizeof(struct rstp_priority_vector4)) > 0) &&
+ (memcmp(v1->designated_bridge_id, v2->designated_bridge_id, sizeof(unsigned char[8])) == 0) &&
+ (memcmp(v1->designated_port_id, v2->designated_port_id, sizeof (unsigned char[2])) == 0)) {
+ return SUPERIOR_SAME_DES;
+ }
+ else if (memcmp(v1, v2, sizeof(struct rstp_priority_vector)) == 0) {
+ return SAME;
+ }
+ else {
+ return NOT_SUPERIOR;
+ }
+}
diff --git a/lib/rstp-state-machines.h b/lib/rstp-state-machines.h
new file mode 100644
index 0000000..9bf996b
--- /dev/null
+++ b/lib/rstp-state-machines.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2011-2014 M3S, Srl - Italy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Rapid Spanning Tree Protocol (IEEE 802.1D-2004) state machines
+ * implementation (header file).
+ *
+ * Authors:
+ * Martino Fornasa <mf at fornasa.it>
+ * Daniele Venturino <daniele.venturino at m3s.it>
+ *
+ * References to IEEE 802.1D-2004 standard are enclosed in square brackets.
+ * E.g. [17.3], [Table 17-1], etc.
+ *
+ */
+
+#ifndef RSTP_STATE_MACHINES_H
+#define RSTP_STATE_MACHINES_H 1
+
+#include "rstp-common.h"
+
+enum portFlag {
+ PORT_UNKN = 0,
+ PORT_ALT_BACK = 1,
+ PORT_ROOT = 2,
+ PORT_DES = 3
+} portFlag_t;
+
+/* Per-Bridge State Machine */
+int port_role_selection_sm (struct rstp *);
+
+/* Per-Port State Machines */
+int port_receive_sm(struct rstp_port *);
+int port_protocol_migration_sm(struct rstp_port *);
+int bridge_detection_sm (struct rstp_port *);
+int port_transmit_sm (struct rstp_port *);
+int port_information_sm (struct rstp_port *);
+int port_role_transition_sm (struct rstp_port *);
+int port_state_transition_sm (struct rstp_port *);
+int topology_change_sm (struct rstp_port *);
+/* port_timers_sm() not defined as a state machine */
+
+/* Methods called by the Forwarding Layer, through functions of rstp.h. */
+int move_rstp(struct rstp * );
+void decrease_rstp_port_timers(struct rstp *);
+int validate_received_bpdu(struct rstp_port *, const void *bpdu, size_t);
+void process_received_bpdu(struct rstp_port *, const void *, size_t);
+
+/* SM functions */
+void updt_role_disabled_tree(struct rstp *);
+void clear_reselect_tree(struct rstp *);
+void updt_roles_tree(struct rstp *);
+void set_selected_tree(struct rstp *);
+
+void updt_bpdu_version(struct rstp_port *);
+void record_agreement(struct rstp_port *);
+void set_tc_flags(struct rstp_port * );
+void record_dispute(struct rstp_port *);
+void record_proposal(struct rstp_port *);
+void record_priority(struct rstp_port *);
+void record_times(struct rstp_port *);
+void updt_rcvd_info_while(struct rstp_port *);
+void time_encode(unsigned int, uint8_t *);
+unsigned int time_decode(uint8_t *);
+void tx_config(struct rstp_port *);
+void tx_tcn(struct rstp_port *);
+void tx_rstp(struct rstp_port *);
+int rcv_info(struct rstp_port *);
+int better_or_same_info(struct rstp_port *, int);
+void set_re_root_tree(struct rstp_port *);
+void set_sync_tree(struct rstp_port *);
+int hello_time(struct rstp_port *);
+int fwd_delay(struct rstp_port *);
+int forward_delay(struct rstp_port *);
+int edge_delay(struct rstp_port *);
+int check_selected_role_change(struct rstp_port *, int);
+int re_rooted(struct rstp_port *);
+int all_synced(struct rstp *);
+void enable_learning(struct rstp_port *);
+void enable_forwarding(struct rstp_port *);
+void disable_learning(struct rstp_port *);
+void disable_forwarding(struct rstp_port *);
+void new_tc_while(struct rstp_port *);
+void set_tc_prop_tree(struct rstp_port *);
+void set_tc_prop_bridge(struct rstp_port *);
+
+#define NOT_SUPERIOR 0
+#define SUPERIOR_ABSOLUTE 1
+#define SUPERIOR_SAME_DES 2
+#define SAME 3
+int rstp_priority_vector_is_superior(struct rstp_priority_vector * v1,
+ struct rstp_priority_vector * v2);
+
+#endif /* rstp-state-machines.h */
diff --git a/lib/rstp.c b/lib/rstp.c
new file mode 100644
index 0000000..d226967
--- /dev/null
+++ b/lib/rstp.c
@@ -0,0 +1,1160 @@
+/*
+ * Copyright (c) 2011-2014 M3S, Srl - Italy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Rapid Spanning Tree Protocol (IEEE 802.1D-2004) public interface.
+ *
+ * Authors:
+ * Martino Fornasa <mf at fornasa.it>
+ * Daniele Venturino <daniele.venturino at m3s.it>
+ *
+ * References to IEEE 802.1D-2004 standard are enclosed in square brackets.
+ * E.g. [17.3], [Table 17-1], etc.
+ *
+ */
+
+#include <config.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include "ofpbuf.h"
+#include "unixctl.h"
+#include "vlog.h"
+#include "connectivity.h"
+#include "seq.h"
+#include "byte-order.h"
+#include "packets.h"
+#include "util.h"
+#include "ofproto/ofproto.h"
+#include "rstp.h"
+#include "rstp-common.h"
+#include "rstp-state-machines.h"
+
+VLOG_DEFINE_THIS_MODULE(rstp);
+
+static struct ovs_mutex mutex;
+static struct list all_rstps__ = LIST_INITIALIZER(&all_rstps__);
+static struct list *const all_rstps OVS_GUARDED_BY(mutex) = &all_rstps__;
+
+/* Internal use only */
+void set_port_id__(struct rstp_port *);
+void update_port_enabled__(struct rstp_port *);
+void set_bridge_priority__(struct rstp *);
+void reinitialize_rstp__(struct rstp *);
+int is_port_number_taken__(struct rstp *, int);
+
+char *
+get_id_string_from_uint8_t(uint8_t* m, int length)
+{
+ int i;
+ char *string = malloc(length*3-1);
+ if (length != 0) {
+ sprintf(string,"%02X", m[0]);
+ for (i = 1; i< length; i++) {
+ sprintf(string+(i*3-1),":%02X", m[i]);
+ }
+ return string;
+ } else {
+ return NULL;
+ }
+}
+
+char *
+rstp_state_name(enum rstp_state state)
+{
+ switch (state) {
+ case RSTP_DISABLED:
+ return "Disabled";
+ case RSTP_LEARNING:
+ return "Learning";
+ case RSTP_FORWARDING:
+ return "Forwarding";
+ case RSTP_DISCARDING:
+ return "Discarding";
+ default:
+ return "Unknown";
+ }
+}
+
+char *
+rstp_port_role_name(enum rstp_port_role role)
+{
+ switch (role) {
+ case ROLE_ROOT:
+ return "Root";
+ case ROLE_DESIGNATED:
+ return "Designated";
+ case ROLE_ALTERNATE:
+ return "Alternate";
+ case ROLE_BACKUP:
+ return "Backup";
+ case ROLE_DISABLED:
+ return "Disabled";
+ default:
+ return "Unknown";
+ }
+}
+
+struct rstp *
+rstp_ref(const struct rstp *rstp_)
+{
+ struct rstp *rstp = CONST_CAST(struct rstp *, rstp_);
+ if (rstp) {
+ ovs_refcount_ref(&rstp->ref_cnt);
+ }
+ return rstp;
+}
+
+/* Frees RSTP struct */
+void
+rstp_unref(struct rstp *rstp)
+{
+ if (rstp && ovs_refcount_unref(&rstp->ref_cnt) == 1) {
+ ovs_mutex_lock(&mutex);
+ list_remove(&rstp->node);
+ ovs_mutex_unlock(&mutex);
+ free(rstp->name);
+ free(rstp);
+ }
+}
+
+/* Returns the port index in the port array. */
+int
+rstp_port_no(const struct rstp_port *p)
+{
+ struct rstp *rstp;
+ int index;
+ ovs_mutex_lock(&mutex);
+ rstp = p->rstp;
+ ovs_assert(p >= rstp->ports && p < &rstp->ports[ARRAY_SIZE(rstp->ports)]);
+ index = p - p->rstp->ports;
+ ovs_mutex_unlock(&mutex);
+ return index;
+}
+
+static void rstp_unixctl_tcn(struct unixctl_conn *, int argc,
+ const char *argv[], void *aux);
+static int rstp_initialize_port(struct rstp_port *p);
+
+/* Decrements the State Machines' timers. */
+int
+rstp_tick_timers(struct rstp *rstp)
+{
+ decrease_rstp_port_timers(rstp);
+ return 0;
+}
+
+/* Processes an incoming BPDU. */
+int
+rstp_received_bpdu(struct rstp_port *p, const void *bpdu, size_t bpdu_size)
+{
+ process_received_bpdu(p, bpdu, bpdu_size);
+ return 0;
+}
+
+void
+rstp_init(void)
+{
+ unixctl_command_register("rstp/tcn", "[bridge]", 0, 1, rstp_unixctl_tcn,
+ NULL);
+}
+
+/* Creates and returns a new RSTP instance that initially has no ports enabled. */
+struct rstp *
+rstp_create(const char *name, uint8_t * bridge_address,
+ void (*send_bpdu)(struct ofpbuf *bpdu, int port_no, void *aux),
+ void *aux)
+{
+ static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+ struct rstp *rstp;
+ struct rstp_port *p;
+ VLOG_DBG("Creating RSTP instance");
+ if (ovsthread_once_start(&once)) {
+ ovs_mutex_init_recursive(&mutex);
+ ovsthread_once_done(&once);
+ }
+
+ ovs_mutex_lock(&mutex);
+ rstp = xzalloc(sizeof *rstp);
+ rstp->name = xstrdup(name);
+ /* Set bridge address. */
+ rstp_set_bridge_address(rstp, bridge_address);
+ /* Set default parameters values. */
+ rstp_set_bridge_priority(rstp, RSTP_DEFAULT_PRIORITY);
+ rstp_set_bridge_ageing_time(rstp, RSTP_DEFAULT_AGEING_TIME);
+ rstp_set_bridge_force_protocol_version(rstp, FPV_DEFAULT);
+ rstp_set_bridge_forward_delay(rstp, RSTP_DEFAULT_BRIDGE_FORWARD_DELAY);
+ rstp_set_bridge_hello_time(rstp);
+ rstp_set_bridge_max_age(rstp, RSTP_DEFAULT_BRIDGE_MAX_AGE);
+ rstp_set_bridge_migrate_time(rstp);
+ rstp_set_bridge_transmit_hold_count(rstp, RSTP_DEFAULT_TRANSMIT_HOLD_COUNT);
+ rstp_set_bridge_times(rstp, RSTP_DEFAULT_BRIDGE_FORWARD_DELAY,
+ RSTP_BRIDGE_HELLO_TIME, RSTP_DEFAULT_BRIDGE_MAX_AGE, 0);
+ rstp->send_bpdu = send_bpdu;
+ rstp->aux = aux;
+ rstp->changes = false;
+ rstp->begin = true;
+ rstp->first_changed_port = &rstp->ports[ARRAY_SIZE(rstp->ports)];
+ /* Initialize the ports array. */
+ for (p = rstp->ports; p < &rstp->ports[ARRAY_SIZE(rstp->ports)]; p++) {
+ p->rstp = rstp;
+ rstp_initialize_port(p);
+ rstp_port_set_state(p, RSTP_DISABLED);
+ }
+ ovs_refcount_init(&rstp->ref_cnt);
+ list_push_back(all_rstps, &rstp->node);
+ ovs_mutex_unlock(&mutex);
+ VLOG_DBG("RSTP instance creation done");
+ return rstp;
+}
+
+/* Called by rstp_set_bridge_address() and rstp_set_bridge_priority(),
+ it updates the bridge priority vector according to the values passed by
+ those setters. */
+void
+set_bridge_priority__(struct rstp *rstp)
+{
+ memcpy(rstp->bridge_priority.root_bridge_id, rstp->bridge_identifier, 8);
+ memcpy(rstp->bridge_priority.designated_bridge_id, rstp->bridge_identifier, 8);
+}
+
+/* Sets the bridge address. */
+int
+rstp_set_bridge_address(struct rstp *rstp, uint8_t bridge_address[6])
+{
+ struct rstp_port *p;
+ VLOG_DBG("%s: set bridge address to %s",
+ rstp->name, get_id_string_from_uint8_t(bridge_address, 6));
+ ovs_mutex_lock(&mutex);
+ memcpy(rstp->address, bridge_address, 6);
+ memcpy(rstp->bridge_identifier+2, bridge_address, 6);
+ set_bridge_priority__(rstp);
+
+ /* [17.13] When the bridge address changes, recalculates all priority vectors. */
+ for (p = rstp->ports; p < &rstp->ports[ARRAY_SIZE(rstp->ports)]; p++) {
+ p->selected = 0;
+ p->reselect = 1;
+ }
+ rstp->changes = true;
+ updt_roles_tree(rstp);
+ ovs_mutex_unlock(&mutex);
+ return 0;
+}
+
+const char *
+rstp_get_name(const struct rstp *rstp)
+{
+ char *name;
+ ovs_mutex_lock(&mutex);
+ name = rstp->name;
+ ovs_mutex_unlock(&mutex);
+ return name;
+}
+
+uint8_t *
+rstp_get_bridge_id(const struct rstp *rstp)
+{
+ uint8_t *bridge_id;
+ ovs_mutex_lock(&mutex);
+ bridge_id = (uint8_t *) rstp->bridge_identifier;
+ ovs_mutex_unlock(&mutex);
+ return bridge_id;
+}
+
+/* Sets the bridge priority. */
+int
+rstp_set_bridge_priority(struct rstp *rstp, int new_priority)
+{
+ struct rstp_port *p;
+ if (new_priority >= RSTP_MIN_PRIORITY && new_priority <= RSTP_MAX_PRIORITY) {
+ ovs_mutex_lock(&mutex);
+ VLOG_DBG("%s: set bridge priority to %d", rstp->name, (new_priority/4096)*4096);
+ rstp->priority = (new_priority/4096)*4096;
+ rstp->bridge_identifier[0] = (new_priority/4096)<<4;
+ set_bridge_priority__(rstp);
+
+ /* [17.13] */
+ for (p = rstp->ports; p < &rstp->ports[ARRAY_SIZE(rstp->ports)]; p++) {
+ p->selected = 0;
+ p->reselect = 1;
+ }
+ rstp->changes = true;
+ updt_roles_tree(rstp);
+ ovs_mutex_unlock(&mutex);
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+/* Sets the bridge ageing time. */
+int
+rstp_set_bridge_ageing_time(struct rstp *rstp, int new_ageing_time)
+{
+ if (new_ageing_time >= RSTP_MIN_AGEING_TIME && new_ageing_time <= RSTP_MAX_AGEING_TIME) {
+ ovs_mutex_lock(&mutex);
+ VLOG_DBG("%s: set ageing time to %d", rstp->name, new_ageing_time);
+ rstp->ageing_time = new_ageing_time;
+ ovs_mutex_unlock(&mutex);
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+/* Reinitializes RSTP when switching from RSTP mode to STP mode
+ or vice versa. */
+void
+reinitialize_rstp__(struct rstp *rstp)
+{
+ char *name;
+ uint8_t * bridge_address;
+ void *send_bpdu;
+ void *aux;
+ struct ovs_refcount ref_count;
+ struct list node;
+ struct rstp_port *p;
+ /* Copy name, bridge_address, ref_cnt, send_bpdu, aux, node */
+ name = xstrdup(rstp->name);
+ bridge_address = malloc(sizeof(rstp->address));
+ memcpy(bridge_address, rstp->address, sizeof(rstp->address));
+ memcpy(&ref_count, &rstp->ref_cnt, sizeof(struct ovs_refcount));
+ send_bpdu = rstp->send_bpdu;
+ aux = rstp->aux;
+ node = rstp->node;
+ /* stop and clear rstp */
+ rstp->changes = false;
+ bzero(rstp, sizeof(struct rstp));
+
+ /* Initialize rstp. */
+ rstp->name = xstrdup(name);
+ /* Set bridge address. */
+ rstp_set_bridge_address(rstp, bridge_address);
+ /* Set default parameters values. */
+ rstp_set_bridge_priority(rstp, RSTP_DEFAULT_PRIORITY);
+ rstp_set_bridge_ageing_time(rstp, RSTP_DEFAULT_AGEING_TIME);
+ rstp_set_bridge_forward_delay(rstp, RSTP_DEFAULT_BRIDGE_FORWARD_DELAY);
+ rstp_set_bridge_hello_time(rstp);
+ rstp_set_bridge_max_age(rstp, RSTP_DEFAULT_BRIDGE_MAX_AGE);
+ rstp_set_bridge_migrate_time(rstp);
+ rstp_set_bridge_transmit_hold_count(rstp, RSTP_DEFAULT_TRANSMIT_HOLD_COUNT);
+ rstp_set_bridge_times(rstp, RSTP_DEFAULT_BRIDGE_FORWARD_DELAY,
+ RSTP_BRIDGE_HELLO_TIME, RSTP_DEFAULT_BRIDGE_MAX_AGE, 0);
+
+ rstp->send_bpdu = send_bpdu;
+ rstp->aux = aux;
+ rstp->node = node;
+ rstp->changes = false;
+ rstp->begin = true;
+ rstp->first_changed_port = &rstp->ports[ARRAY_SIZE(rstp->ports)];
+ for (p = rstp->ports; p < &rstp->ports[ARRAY_SIZE(rstp->ports)]; p++) {
+ p->rstp = rstp;
+ rstp_initialize_port(p);
+ rstp_port_set_state(p, RSTP_DISABLED);
+ }
+ memcpy(&rstp->ref_cnt, &ref_count, sizeof(struct ovs_refcount));
+}
+
+/* Sets the force protocol version parameter. */
+int
+rstp_set_bridge_force_protocol_version(struct rstp *rstp, enum rstp_force_protocol_version new_force_protocol_version)
+{
+ if (new_force_protocol_version != FPV_STP_COMPATIBILITY &&
+ new_force_protocol_version != FPV_DEFAULT) {
+ return -1;
+ }
+ if (new_force_protocol_version != rstp->force_protocol_version) {
+ VLOG_DBG("%s: set bridge Force Protocol Version to %d", rstp->name, new_force_protocol_version);
+ ovs_mutex_lock(&mutex);
+ /*
+ [17.13] The Spanning Tree Protocol Entity shall be reinitialized, as
+ specified by the assertion of BEGIN (17.18.1) in the state machine
+ specification.
+ */
+ reinitialize_rstp__(rstp);
+ rstp->force_protocol_version = new_force_protocol_version;
+ if (rstp->force_protocol_version < 2) {
+ rstp->stp_version = true;
+ rstp->rstp_version = false;
+ } else {
+ rstp->stp_version = false;
+ rstp->rstp_version = true;
+ }
+ rstp->changes = true;
+ ovs_mutex_unlock(&mutex);
+ }
+ return 0;
+}
+
+/* Sets the bridge Hello Time parameter. */
+int
+rstp_set_bridge_hello_time(struct rstp *rstp)
+{
+ VLOG_DBG("%s: set RSTP Hello Time to %d", rstp->name, RSTP_BRIDGE_HELLO_TIME);
+ /* 2 is the only acceptable value. */
+ ovs_mutex_lock(&mutex);
+ rstp->bridge_hello_time = RSTP_BRIDGE_HELLO_TIME;
+ ovs_mutex_unlock(&mutex);
+ return 0;
+}
+
+/* Sets the bridge max age parameter. */
+int
+rstp_set_bridge_max_age(struct rstp *rstp, int new_max_age)
+{
+ if (new_max_age >= RSTP_MIN_BRIDGE_MAX_AGE &&
+ new_max_age <= RSTP_MAX_BRIDGE_MAX_AGE) {
+ /* [17.13] */
+ if ((2*(rstp->bridge_forward_delay - 1) >= new_max_age) &&
+ (new_max_age >= 2*rstp->bridge_hello_time)) {
+ VLOG_DBG("%s: set RSTP bridge Max Age to %d", rstp->name, new_max_age);
+ ovs_mutex_lock(&mutex);
+ rstp->bridge_max_age = new_max_age;
+ rstp->bridge_times.max_age = new_max_age;
+ ovs_mutex_unlock(&mutex);
+ return 0;
+ } else {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+}
+
+/* Sets the bridge forward delay parameter. */
+int
+rstp_set_bridge_forward_delay(struct rstp *rstp, int new_forward_delay)
+{
+ if (new_forward_delay >= RSTP_MIN_BRIDGE_FORWARD_DELAY &&
+ new_forward_delay <= RSTP_MAX_BRIDGE_FORWARD_DELAY) {
+ if (2*(new_forward_delay - 1) >= rstp->bridge_max_age) {
+ VLOG_DBG("%s: set RSTP Forward Delay to %d", rstp->name, new_forward_delay);
+ ovs_mutex_lock(&mutex);
+ rstp->bridge_forward_delay = new_forward_delay;
+ rstp->bridge_times.forward_delay = new_forward_delay;
+ ovs_mutex_unlock(&mutex);
+ return 0;
+ } else {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+}
+
+/* Sets the bridge transmit hold count parameter. */
+int
+rstp_set_bridge_transmit_hold_count(struct rstp *rstp, int new_transmit_hold_count)
+{
+ int port_no;
+ if (new_transmit_hold_count >= RSTP_MIN_TRANSMIT_HOLD_COUNT &&
+ new_transmit_hold_count <= RSTP_MAX_TRANSMIT_HOLD_COUNT) {
+ VLOG_DBG("%s: set RSTP Transmit Hold Count to %d", rstp->name, new_transmit_hold_count);
+ /* Resetting txCount on all ports [17.13]. */
+ ovs_mutex_lock(&mutex);
+ rstp->transmit_hold_count = new_transmit_hold_count;
+ for (port_no = 0; port_no < RSTP_MAX_PORTS; port_no++) {
+ struct rstp_port *p = rstp_get_port(rstp, port_no);
+ p->tx_count=0;
+ }
+ ovs_mutex_unlock(&mutex);
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+/* Sets the bridge migrate time parameter. */
+int
+rstp_set_bridge_migrate_time(struct rstp *rstp)
+{
+ VLOG_DBG("%s: set RSTP Migrate Time to %d", rstp->name, RSTP_MIGRATE_TIME);
+ /* 3 is the only acceptable value */
+ ovs_mutex_lock(&mutex);
+ rstp->migrate_time = RSTP_MIGRATE_TIME;
+ ovs_mutex_unlock(&mutex);
+ return 0;
+}
+
+/* Sets the bridge times. */
+int
+rstp_set_bridge_times(struct rstp *rstp, int new_forward_delay, int new_hello_time, int new_max_age, int new_message_age)
+{
+ VLOG_DBG("%s: set RSTP times to (%d, %d, %d, %d)", rstp->name, new_forward_delay, new_hello_time, new_max_age, new_message_age);
+ if (new_forward_delay >= RSTP_MIN_BRIDGE_FORWARD_DELAY && new_forward_delay <= RSTP_MAX_BRIDGE_FORWARD_DELAY)
+ rstp->bridge_times.forward_delay = new_forward_delay;
+ if (new_hello_time == RSTP_BRIDGE_HELLO_TIME)
+ rstp->bridge_times.hello_time = new_hello_time;
+ if (new_max_age >= RSTP_MIN_BRIDGE_MAX_AGE && new_max_age <= RSTP_MAX_BRIDGE_MAX_AGE)
+ rstp->bridge_times.max_age = new_max_age;
+ rstp->bridge_times.message_age = new_message_age;
+ return 0;
+}
+
+/* Sets the port id, it is called by rstp_port_set_port_number() or
+ rstp_port_set_priority(). */
+void
+set_port_id__(struct rstp_port * p) /* normally used when mutex is already locked */
+{
+ struct rstp *rstp = p->rstp;
+ /* [9.2.7] Port identifier. */
+ uint16_t temp = htons(p->port_number);
+ uint8_t * ptemp = (uint8_t *)&temp;
+ p->port_id[1] = ptemp[1];
+ p->port_id[0] = ptemp[0] | p->priority;
+ VLOG_DBG("%s: new RSTP port id %s", rstp->name, get_id_string_from_uint8_t(p->port_id, 2));
+}
+
+/* Sets the port priority. */
+int
+rstp_port_set_priority(struct rstp_port *rstp_port, int new_port_priority)
+{
+ struct rstp *rstp = rstp_port->rstp;
+ if (new_port_priority >= RSTP_MIN_PORT_PRIORITY && new_port_priority <= RSTP_MAX_PORT_PRIORITY) {
+ VLOG_DBG("%s, port %u: set RSTP port priority to %d", rstp->name, rstp_port->port_number, new_port_priority);
+ ovs_mutex_lock(&mutex);
+ new_port_priority = new_port_priority - new_port_priority%RSTP_STEP_PORT_PRIORITY; /* floor */
+ rstp_port->priority = new_port_priority;
+ set_port_id__(rstp_port);
+ rstp_port->selected = 0;
+ rstp_port->reselect = 1;
+ ovs_mutex_unlock(&mutex);
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+/* Checks if a port number is already taken by an active port. */
+int
+is_port_number_taken__(struct rstp *rstp, int n)
+{
+ struct rstp_port *p;
+ for (p = rstp->ports; p < &rstp->ports[ARRAY_SIZE(rstp->ports)]; p++) {
+ if (p->port_number == n && p->rstp_state != RSTP_DISABLED) {
+ VLOG_DBG("%s: port number %d already taken by port with state = %s", rstp->name, n,
+ rstp_state_name(p->rstp_state));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/* Sets the port number. */
+int
+rstp_port_set_port_number(struct rstp_port *rstp_port, uint16_t new_port_number)
+{
+ struct rstp *rstp = rstp_port->rstp;
+ if (new_port_number >= 1 && new_port_number <= RSTP_MAX_PORT_NUMBER) {
+ ovs_mutex_lock(&mutex);
+ if (is_port_number_taken__(rstp_port->rstp, new_port_number) == -1) {
+ /* Port already exists, do nothing. */
+ ovs_mutex_unlock(&mutex);
+ return -1;
+ }
+ VLOG_DBG("%s: set new RSTP port number %d -> %d", rstp->name, rstp_port->port_number, new_port_number);
+ rstp_port->port_number = new_port_number;
+ set_port_id__(rstp_port);
+ /* [17.13] is not clear. I suppose that a port number change
+ should trigger reselection like a port priority change. */
+ rstp_port->selected = 0;
+ rstp_port->reselect = 1;
+ ovs_mutex_unlock(&mutex);
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+/* Converts the link speed to a port path cost [Table 17-3]. */
+uint32_t
+rstp_convert_speed_to_cost(unsigned int speed)
+{
+ uint32_t value;
+ ovs_mutex_lock(&mutex);
+ if (speed >= 10000000)
+ value = 2; /* 10 Tb/s. */
+ else if (speed >= 1000000)
+ value = 20; /* 1 Tb/s. */
+ else if (speed >= 100000)
+ value = 200; /* 100 Gb/s. */
+ else if (speed >= 10000)
+ value = 2000; /* 10 Gb/s. */
+ else if (speed >= 1000)
+ value =20000; /* 1 Gb/s. */
+ else if (speed >= 100)
+ value = 200000; /* 100 Mb/s. */
+ else if (speed >= 10)
+ value = 2000000; /* 10 Mb/s. */
+ else if (speed >= 1)
+ value = 20000000; /* 1 Mb/s. */
+ else
+ value = RSTP_DEFAULT_PORT_PATH_COST; /* 100 Mb/s. */
+ ovs_mutex_unlock(&mutex);
+ return value;
+}
+
+/* Sets the port path cost. */
+int
+rstp_port_set_path_cost(struct rstp_port *rstp_port, uint32_t new_port_path_cost)
+{
+ struct rstp *rstp = rstp_port->rstp;
+ if (new_port_path_cost >= RSTP_MIN_PORT_PATH_COST && new_port_path_cost <= RSTP_MAX_PORT_PATH_COST) {
+ VLOG_DBG("%s, port %u, set RSTP port path cost to %d", rstp->name, rstp_port->port_number, new_port_path_cost);
+ ovs_mutex_lock(&mutex);
+ rstp_port->port_path_cost = new_port_path_cost;
+ rstp_port->selected = 0;
+ rstp_port->reselect = 1;
+ ovs_mutex_unlock(&mutex);
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+/* Gets the root path cost. */
+uint8_t *
+rstp_get_root_path_cost(const struct rstp *rstp)
+{
+ uint8_t *cost;
+ ovs_mutex_lock(&mutex);
+ cost = (uint8_t *) rstp->root_priority.root_path_cost;
+ ovs_mutex_unlock(&mutex);
+ return cost;
+}
+
+/* Returns true if something has happened to 'rstp' which necessitates flushing
+ * the client's MAC learning table.
+ */
+bool
+rstp_check_and_reset_fdb_flush(struct rstp *rstp)
+{
+ bool needs_flush = false;
+ struct rstp_port *p, *end;
+ ovs_mutex_lock(&mutex);
+ end = &rstp->ports[ARRAY_SIZE(rstp->ports)];
+ for (p = rstp->first_changed_port; p < end; p++) {
+ if(p->fdb_flush == true) {
+ needs_flush = true;
+ /* fdb_flush should be reset by the filtering database
+ * once the entries are removed if rstp_version is TRUE, and
+ * immediately if stp_version is TRUE.*/
+ p->fdb_flush = false;
+ }
+ }
+ ovs_mutex_unlock(&mutex);
+ return needs_flush;
+}
+
+/* Finds a port whose state has changed. If successful, stores the port whose
+ * state changed in '*portp' and returns true. If no port has changed, stores
+ * NULL in '*portp' and returns false. */
+bool
+rstp_get_changed_port(struct rstp *rstp, struct rstp_port **portp)
+{
+ struct rstp_port *end, *p;
+ bool changed = false;
+
+ ovs_mutex_lock(&mutex);
+ end = &rstp->ports[ARRAY_SIZE(rstp->ports)];
+ for (p = rstp->first_changed_port; p < end; p++) {
+ if (p->state_changed) {
+ p->state_changed = false;
+ rstp->first_changed_port = p + 1;
+ *portp = p;
+ changed = true;
+ goto out;
+ }
+ }
+ rstp->first_changed_port = end;
+ *portp = NULL;
+out:
+ ovs_mutex_unlock(&mutex);
+ return changed;
+}
+
+/* Returns the port in 'rstp' with index 'port_no', which must be between 0 and
+ * RSTP_MAX_PORTS. */
+struct rstp_port *
+rstp_get_port(struct rstp *rstp, int port_no)
+{
+ struct rstp_port *port;
+ ovs_mutex_lock(&mutex);
+ ovs_assert(port_no >= 0 && port_no < ARRAY_SIZE(rstp->ports));
+ port = &rstp->ports[port_no];
+ ovs_mutex_unlock(&mutex);
+ return port;
+}
+
+/* Updates the port_enabled parameter. */
+void
+update_port_enabled__(struct rstp_port * p)
+{
+ if (p->mac_operational && p->is_administrative_bridge_port == RSTP_ADMIN_BRIDGE_PORT_STATE_ENABLED) {
+ p->port_enabled = true;
+ } else {
+ p->port_enabled = false;
+ }
+}
+
+/* Sets the port MAC_Operational parameter [6.4.2]. */
+int
+rstp_port_set_mac_operational(struct rstp_port * p, uint8_t new_mac_operational)
+{
+ struct rstp *rstp;
+ if (new_mac_operational == 0 || new_mac_operational == 1) {
+ ovs_mutex_lock(&mutex);
+ rstp = p->rstp;
+ p->mac_operational = new_mac_operational;
+ update_port_enabled__(p);
+ rstp->changes = true;
+ move_rstp(rstp);
+ ovs_mutex_unlock(&mutex);
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+/* Gets the port MAC_Operational parameter [6.4.2]. */
+uint8_t
+rstp_port_get_mac_operational(struct rstp_port * p)
+{
+ uint8_t value;
+ ovs_mutex_lock(&mutex);
+ value = p->mac_operational;
+ ovs_mutex_unlock(&mutex);
+ return value;
+}
+
+/* Sets the port Administrative Bridge Port parameter. */
+int
+rstp_port_set_administrative_bridge_port(struct rstp_port *p, uint8_t new_admin_port_state)
+{
+ if (new_admin_port_state == RSTP_ADMIN_BRIDGE_PORT_STATE_DISABLED ||
+ new_admin_port_state == RSTP_ADMIN_BRIDGE_PORT_STATE_ENABLED) {
+ p->is_administrative_bridge_port = new_admin_port_state;
+ update_port_enabled__(p);
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+/* Sets the port oper_point_to_point_mac parameter. */
+int
+rstp_port_set_oper_point_to_point_mac(struct rstp_port *p, uint8_t new_oper_p2p_mac)
+{
+ if (new_oper_p2p_mac == RSTP_OPER_P2P_MAC_STATE_DISABLED ||
+ new_oper_p2p_mac == RSTP_OPER_P2P_MAC_STATE_ENABLED) {
+ p->oper_point_to_point_mac = new_oper_p2p_mac;
+ update_port_enabled__(p);
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+/* Initializes a port with the defaults values for its parameters. */
+static int
+rstp_initialize_port(struct rstp_port *p)
+OVS_REQUIRES(mutex)
+{
+ struct rstp *rstp = p->rstp;
+
+ rstp_port_set_administrative_bridge_port(p, RSTP_ADMIN_BRIDGE_PORT_STATE_ENABLED);
+ rstp_port_set_oper_point_to_point_mac(p, 1);
+ rstp_port_set_path_cost(p, RSTP_DEFAULT_PORT_PATH_COST);
+ rstp_port_set_priority(p, RSTP_DEFAULT_PORT_PRIORITY);
+ rstp_port_set_port_number(p, rstp_port_no(p) + 1);
+ rstp_port_set_path_cost(p, RSTP_DEFAULT_PORT_PATH_COST);
+ rstp_port_set_auto_edge(p, true);
+
+ p->port_receive_sm_state = PORT_RECEIVE_SM_INIT;
+ p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_INIT;
+ p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_INIT;
+ p->port_transmit_sm_state = PORT_TRANSMIT_SM_INIT;
+ p->port_information_sm_state = PORT_INFORMATION_SM_INIT;
+ p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_INIT;
+ p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_INIT;
+ p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_INIT;
+
+ p->uptime = 0;
+
+ VLOG_DBG("%s: init RSTP port %s", rstp->name, get_id_string_from_uint8_t(p->port_id, 2));
+ return 0;
+}
+
+/* Sets the port state. */
+int
+rstp_port_set_state(struct rstp_port *p, enum rstp_state state)
+OVS_REQUIRES(mutex)
+{
+ struct rstp *rstp = p->rstp;
+ VLOG_DBG("%s, port %u: set RSTP port state %s -> %s", rstp->name,
+ p->port_number,
+ rstp_state_name(p->rstp_state), rstp_state_name(state));
+
+ if (state != p->rstp_state && !p->state_changed) {
+ p->state_changed = true;
+ if (p < p->rstp->first_changed_port) {
+ p->rstp->first_changed_port = p;
+ }
+ seq_change(connectivity_seq_get());
+ }
+ p->rstp_state = state;
+ return 0;
+}
+
+
+/* Enables RSTP on port 'p'. The port will initially be in DISCARDING state. */
+int
+rstp_port_enable(struct rstp_port *p)
+{
+ struct rstp *rstp;
+ ovs_mutex_lock(&mutex);
+ rstp = p->rstp;
+ if (p->rstp_state == RSTP_DISABLED) {
+ rstp_initialize_port(p);
+ rstp_port_set_state(p, RSTP_DISCARDING);
+ p->rstp->ports_count++;
+ VLOG_DBG("%s: enabling RSPT port %u", rstp->name, p->port_number);
+ rstp->changes = true;
+ move_rstp(rstp);
+ }
+ ovs_mutex_unlock(&mutex);
+ return 0;
+}
+
+/* Disable RSTP on port 'p'. */
+int
+rstp_port_disable(struct rstp_port *p)
+{
+ struct rstp *rstp;
+ ovs_mutex_lock(&mutex);
+ rstp = p->rstp;
+ if (p->rstp_state != RSTP_DISABLED) {
+ VLOG_DBG("%s: disabling RSPT port %u", rstp->name, p->port_number);
+ bzero(p, sizeof(struct rstp_port));
+ p->rstp = rstp;
+ rstp_initialize_port(p);
+ rstp_port_set_state(p, RSTP_DISABLED);
+ p->rstp->ports_count--;
+ rstp->changes = true;
+ move_rstp(rstp);
+ }
+ ovs_mutex_unlock(&mutex);
+ return 0;
+}
+
+/* Sets the port Admin Edge parameter. */
+int
+rstp_port_set_admin_edge(struct rstp_port *rstp_port, bool new_admin_edge)
+{
+ struct rstp *rstp = rstp_port->rstp;
+ if (rstp_port->admin_edge != new_admin_edge) {
+ VLOG_DBG("%s, port %u: set RSTP Admin Edge to %d", rstp->name, rstp_port->port_number, new_admin_edge);
+ ovs_mutex_lock(&mutex);
+ rstp_port->admin_edge = new_admin_edge;
+ ovs_mutex_unlock(&mutex);
+ return 0;
+ }
+ else
+ return -1;
+}
+
+/* Sets the port Auto Edge parameter. */
+int
+rstp_port_set_auto_edge(struct rstp_port *rstp_port, bool new_auto_edge)
+{
+ struct rstp *rstp = rstp_port->rstp;
+ if (rstp_port->auto_edge != new_auto_edge) {
+ VLOG_DBG("%s, port %u: set RSTP Auto Edge to %d", rstp->name, rstp_port->port_number, new_auto_edge);
+ ovs_mutex_lock(&mutex);
+ rstp_port->auto_edge = new_auto_edge;
+ ovs_mutex_unlock(&mutex);
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+/* Sets the port mcheck parameter. */
+int
+rstp_port_set_mcheck(struct rstp_port *rstp_port, bool new_mcheck)
+{
+ struct rstp *rstp;
+ ovs_mutex_lock(&mutex);
+ rstp = rstp_port->rstp;
+ if (new_mcheck == true && rstp_port->rstp->force_protocol_version >= 2) {
+ rstp_port->mcheck = true;
+ VLOG_DBG("%s, port %u: set RSTP mcheck to %d", rstp->name, rstp_port->port_number, new_mcheck);
+ ovs_mutex_unlock(&mutex);
+ return 0;
+ } else {
+ ovs_mutex_unlock(&mutex);
+ return -1;
+ }
+}
+
+/* Returns the designated bridge id. */
+uint8_t *
+rstp_get_designated_id(const struct rstp *rstp)
+{
+ uint8_t *designated_id;
+ ovs_mutex_lock(&mutex);
+ designated_id = (uint8_t *)rstp->root_priority.designated_bridge_id;
+ ovs_mutex_unlock(&mutex);
+ return designated_id;
+}
+
+/* Returns the root bridge id. */
+uint8_t *
+rstp_get_root_id(const struct rstp *rstp)
+{
+ uint8_t *root_id;
+ ovs_mutex_lock(&mutex);
+ root_id = (uint8_t *) rstp->root_priority.root_bridge_id;
+ ovs_mutex_unlock(&mutex);
+ return root_id;
+}
+
+/* Returns the designated port id. */
+uint8_t *
+rstp_get_designated_port_id(const struct rstp *rstp)
+{
+ uint8_t *designated_port_id;
+ ovs_mutex_lock(&mutex);
+ designated_port_id = (uint8_t *) rstp->root_priority.designated_port_id;
+ ovs_mutex_unlock(&mutex);
+ return designated_port_id;
+}
+
+/* Return the bridge port id. */
+uint8_t *
+rstp_get_bridge_port_id(const struct rstp *rstp)
+{
+ uint8_t *bridge_port_id;
+ ovs_mutex_lock(&mutex);
+ bridge_port_id = (uint8_t *) rstp->root_priority.bridge_port_id;
+ ovs_mutex_unlock(&mutex);
+ return bridge_port_id;
+}
+
+/* Returns true if the bridge believes to the be root of the spanning tree,
+ * false otherwise. */
+bool
+rstp_is_root_bridge(const struct rstp *rstp)
+{
+ bool is_root;
+
+ ovs_mutex_lock(&mutex);
+ if (memcmp(rstp->bridge_identifier, rstp->root_priority.designated_bridge_id, 8) == 0) {
+ is_root = 1;
+ } else {
+ is_root = 0;
+ }
+ ovs_mutex_unlock(&mutex);
+ return is_root;
+}
+
+/* Returns the bridge ID of the bridge currently believed to be the root. */
+uint8_t *
+rstp_get_designated_root(const struct rstp *rstp)
+{
+ uint8_t * designated_root;
+ designated_root = xzalloc(sizeof(uint8_t [8]));
+ ovs_mutex_lock(&mutex);
+ memcpy(designated_root, rstp->root_priority.designated_bridge_id, 8);
+ ovs_mutex_unlock(&mutex);
+ return designated_root;
+}
+
+/* Returns the port connecting 'rstp' to the root bridge, or a null pointer if
+ * there is no such port. */
+struct rstp_port *
+rstp_get_root_port(struct rstp *rstp)
+{
+ struct rstp_port *p;
+ int i, ret_val;
+ i = 0;
+ ret_val = -1;
+ ovs_mutex_lock(&mutex);
+ for (p = rstp->ports; p < &rstp->ports[ARRAY_SIZE(rstp->ports)]; p++) {
+ if (p->role == ROLE_ROOT && p->rstp_state != RSTP_DISABLED) {
+ ret_val = i;
+ p = &rstp->ports[ARRAY_SIZE(rstp->ports)];
+ }
+ i++;
+ }
+ ovs_mutex_unlock(&mutex);
+ p = rstp->ports + ret_val;
+ if (ret_val != -1) {
+ return p;
+ } else {
+ return NULL;
+ }
+}
+
+/* Returns the port ID for 'p'. */
+uint8_t *
+rstp_port_get_id(const struct rstp_port *p)
+{
+ uint8_t *port_id;
+ ovs_mutex_lock(&mutex);
+ port_id = (uint8_t *) p->port_id;
+ ovs_mutex_unlock(&mutex);
+ return port_id;
+}
+
+/* Returns the state of port 'p'. */
+enum rstp_state
+rstp_port_get_state(const struct rstp_port *p)
+{
+ enum rstp_state state;
+ ovs_mutex_lock(&mutex);
+ state = p->rstp_state;
+ ovs_mutex_unlock(&mutex);
+ return state;
+}
+
+/* Returns the role of port 'p'. */
+enum rstp_port_role
+rstp_port_get_role(const struct rstp_port *p)
+{
+ enum rstp_port_role role;
+ ovs_mutex_lock(&mutex);
+ role = p->role;
+ ovs_mutex_unlock(&mutex);
+ return role;
+}
+
+/* Retrieves BPDU transmit and receive counts for 'p'. */
+void
+rstp_port_get_counts(const struct rstp_port *p,
+ int *tx_count, int *rx_count, int *error_count, int *uptime)
+{
+ ovs_mutex_lock(&mutex);
+ *tx_count = p->tx_count;
+ *rx_count = p->rx_rstp_bpdu_cnt;
+ *error_count = p->error_count;
+ *uptime = p->uptime;
+ ovs_mutex_unlock(&mutex);
+}
+
+int
+rstp_port_set_aux(struct rstp_port *p, void *aux)
+{
+ ovs_mutex_lock(&mutex);
+ p->aux = aux;
+ ovs_mutex_unlock(&mutex);
+ return 0;
+}
+
+void *
+rstp_port_get_aux(struct rstp_port *p)
+{
+ void *aux;
+ ovs_mutex_lock(&mutex);
+ aux = p->aux;
+ ovs_mutex_unlock(&mutex);
+ return aux;
+}
+
+/* Returns true if 'state' is one in which BPDU packets should be received
+ * and transmitted on a port, false otherwise.
+ */
+ bool
+ rstp_should_manage_bpdu(enum rstp_state state)
+ {
+ if (state == RSTP_DISCARDING || state == RSTP_LEARNING ||
+ state == RSTP_FORWARDING) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+/* Returns true if 'state' is one in which packets received on a port should
+ * be forwarded, false otherwise.
+ *
+ * Returns true if 'state' is RSTP_DISABLED, since presumably in that case the
+ * port should still work, just not have RSTP applied to it. */
+bool
+rstp_forward_in_state(enum rstp_state state)
+{
+ if (state == RSTP_DISABLED || state == RSTP_FORWARDING) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/* Returns true if 'state' is one in which MAC learning should be done on
+ * packets received on a port, false otherwise.
+ *
+ * Returns true if 'state' is RSTP_DISABLED, since presumably in that case the
+ * port should still work, just not have RSTP applied to it. */
+bool
+rstp_learn_in_state(enum rstp_state state)
+{
+ if (state == RSTP_DISABLED || state == RSTP_LEARNING || state == RSTP_FORWARDING) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/* Unixctl. */
+static struct rstp *
+rstp_find(const char *name) OVS_REQUIRES(mutex)
+{
+ struct rstp *rstp;
+ LIST_FOR_EACH(rstp, node, all_rstps) {
+ if (!strcmp(rstp->name, name)) {
+ return rstp;
+ }
+ }
+ return NULL;
+}
+
+static void
+rstp_unixctl_tcn(struct unixctl_conn *conn, int argc,
+ const char *argv[], void *aux OVS_UNUSED)
+{
+ ovs_mutex_lock(&mutex);
+ if (argc > 1) {
+ struct rstp *rstp = rstp_find(argv[1]);
+ if (!rstp) {
+ unixctl_command_reply_error(conn, "No such RSTP object");
+ goto out;
+ }
+ rstp->changes = true;
+ move_rstp(rstp);
+ } else {
+ struct rstp *rstp;
+ LIST_FOR_EACH (rstp, node, all_rstps) {
+ rstp->changes = true;
+ move_rstp(rstp);
+ }
+ }
+ unixctl_command_reply(conn, "OK");
+
+out:
+ ovs_mutex_unlock(&mutex);
+}
diff --git a/lib/rstp.h b/lib/rstp.h
new file mode 100644
index 0000000..d3eb6a6
--- /dev/null
+++ b/lib/rstp.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2011-2014 M3S, Srl - Italy
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Rapid Spanning Tree Protocol (IEEE 802.1D-2004) public interface (header
+ * file).
+ *
+ * Authors:
+ * Martino Fornasa <mf at fornasa.it>
+ * Daniele Venturino <daniele.venturino at m3s.it>
+ *
+ * References to IEEE 802.1D-2004 standard are enclosed in square brackets.
+ * E.g. [17.3], [Table 17-1], etc.
+ *
+ */
+
+#ifndef RSTP_H
+#define RSTP_H 1
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "compiler.h"
+#include "util.h"
+
+#define RSTP_MAX_PORTS 4095
+
+struct ofpbuf;
+
+/* Bridge priority defaults [Table 17-2] */
+#define RSTP_MIN_PRIORITY 0
+#define RSTP_MAX_PRIORITY 61440
+#define RSTP_PRIORITY_STEP 4096
+#define RSTP_DEFAULT_PRIORITY 32768
+
+/* Port priority defaults [Table 17-2] */
+#define RSTP_MIN_PORT_PRIORITY 0
+#define RSTP_MAX_PORT_PRIORITY 240
+#define RSTP_STEP_PORT_PRIORITY 16
+#define RSTP_DEFAULT_PORT_PRIORITY 128
+
+/* Performance parameters defaults. [Table 7-5] and [Table 17-1] */
+#define RSTP_DEFAULT_AGEING_TIME 300
+#define RSTP_MIN_AGEING_TIME 10
+#define RSTP_MAX_AGEING_TIME 1000000
+
+#define RSTP_DEFAULT_BRIDGE_MAX_AGE 20
+#define RSTP_MIN_BRIDGE_MAX_AGE 6
+#define RSTP_MAX_BRIDGE_MAX_AGE 40
+
+#define RSTP_DEFAULT_BRIDGE_FORWARD_DELAY 15
+#define RSTP_MIN_BRIDGE_FORWARD_DELAY 4
+#define RSTP_MAX_BRIDGE_FORWARD_DELAY 30
+
+#define RSTP_DEFAULT_TRANSMIT_HOLD_COUNT 6
+#define RSTP_MIN_TRANSMIT_HOLD_COUNT 1
+#define RSTP_MAX_TRANSMIT_HOLD_COUNT 10
+
+#define RSTP_BRIDGE_HELLO_TIME 2 /* Value is fixed [Table 17-1] */
+
+#define RSTP_MIGRATE_TIME 3 /* Value is fixed [Table 17-1] */
+
+/* Port path cost [Table 17-3] */
+#define RSTP_MIN_PORT_PATH_COST 1
+#define RSTP_MAX_PORT_PATH_COST 200000000
+#define RSTP_DEFAULT_PORT_PATH_COST 200000
+
+/* Port state encoding [9.3.3] */
+enum rstp_state {
+ RSTP_DISABLED = 1 << 0,
+ RSTP_LEARNING = 1 << 1,
+ RSTP_FORWARDING = 1 << 2,
+ RSTP_DISCARDING = 1 << 3
+};
+
+/* Force Protocol Version [17.13.4] */
+enum rstp_force_protocol_version {
+ FPV_STP_COMPATIBILITY = 0,
+ FPV_DEFAULT = 2
+};
+
+enum rstp_port_role {
+ ROLE_ROOT,
+ ROLE_DESIGNATED,
+ ROLE_ALTERNATE,
+ ROLE_BACKUP,
+ ROLE_DISABLED
+};
+
+struct rstp;
+struct rstp_port;
+struct ofproto_rstp_settings;
+
+char *get_id_string_from_uint8_t(uint8_t *, int);
+char *rstp_state_name(enum rstp_state);
+bool rstp_forward_in_state(enum rstp_state);
+bool rstp_learn_in_state(enum rstp_state);
+bool rstp_should_manage_bpdu(enum rstp_state state);
+char *rstp_port_role_name(enum rstp_port_role);
+
+void rstp_init(void);
+
+struct rstp * rstp_create(const char *, uint8_t *,
+ void (*send_bpdu)(struct ofpbuf *, int port_no, void *),
+ void *);
+struct rstp *rstp_ref(const struct rstp *);
+void rstp_unref(struct rstp *);
+
+/* Functions used outside RSTP, to call functions defined in
+ rstp-state-machines.h */
+int rstp_tick_timers(struct rstp *);
+int rstp_received_bpdu(struct rstp_port *, const void *, size_t);
+
+bool rstp_check_and_reset_fdb_flush(struct rstp *);
+bool rstp_get_changed_port(struct rstp *, struct rstp_port **);
+int rstp_port_set_mac_operational(struct rstp_port *,
+ uint8_t new_mac_operational);
+uint8_t rstp_port_get_mac_operational(struct rstp_port *);
+
+/* Bridge setters */
+int rstp_set_bridge_address(struct rstp *, uint8_t []);
+int rstp_set_bridge_priority(struct rstp *, int new_priority);
+int rstp_set_bridge_ageing_time(struct rstp *, int new_ageing_time);
+int rstp_set_bridge_force_protocol_version(struct rstp *,
+ enum rstp_force_protocol_version new_force_protocol_version);
+int rstp_set_bridge_hello_time(struct rstp *);
+int rstp_set_bridge_max_age(struct rstp *, int new_max_age);
+int rstp_set_bridge_forward_delay(struct rstp *, int new_forward_delay);
+int rstp_set_bridge_transmit_hold_count(struct rstp *,
+ int new_transmit_hold_count);
+int rstp_set_bridge_migrate_time(struct rstp *);
+int rstp_set_bridge_times(struct rstp *, int new_forward_delay, int new_hello_time,
+ int new_max_age, int new_message_age);
+
+/* Port setters */
+int rstp_port_set_priority(struct rstp_port *, int new_port_priority);
+int rstp_port_set_port_number(struct rstp_port *, uint16_t new_port_number);
+uint32_t rstp_convert_speed_to_cost(unsigned int speed);
+int rstp_port_set_path_cost(struct rstp_port *, uint32_t new_port_path_cost);
+int rstp_port_set_admin_edge(struct rstp_port *, bool new_admin_edge);
+int rstp_port_set_auto_edge(struct rstp_port *, bool new_auto_edge);
+int rstp_port_set_state(struct rstp_port *, enum rstp_state new_state);
+int rstp_port_enable(struct rstp_port *);
+int rstp_port_disable(struct rstp_port *);
+int rstp_port_set_aux(struct rstp_port *, void *aux);
+int rstp_port_set_administrative_bridge_port(struct rstp_port *, uint8_t);
+int rstp_port_set_oper_point_to_point_mac(struct rstp_port *, uint8_t);
+int rstp_port_set_mcheck(struct rstp_port *, bool new_mcheck);
+
+/* Bridge getters */
+const char * rstp_get_name(const struct rstp *);
+uint8_t * rstp_get_root_id(const struct rstp *);
+uint8_t * rstp_get_bridge_id(const struct rstp *);
+uint8_t * rstp_get_designated_id(const struct rstp *);
+uint8_t * rstp_get_root_path_cost(const struct rstp *);
+uint8_t * rstp_get_designated_port_id(const struct rstp *);
+uint8_t * rstp_get_bridge_port_id(const struct rstp *);
+struct rstp_port * rstp_get_root_port(struct rstp *);
+uint8_t * rstp_get_designated_root(const struct rstp *);
+bool rstp_is_root_bridge(const struct rstp *);
+
+/* Port getters */
+int rstp_port_no(const struct rstp_port *);
+struct rstp_port *rstp_get_port(struct rstp *, int port_no);
+uint8_t * rstp_port_get_id(const struct rstp_port *);
+enum rstp_state rstp_port_get_state(const struct rstp_port *);
+enum rstp_port_role rstp_port_get_role(const struct rstp_port *);
+void rstp_port_get_counts(const struct rstp_port *, int *tx_count, int *rx_count,
+ int *error_count, int *uptime);
+void * rstp_port_get_aux(struct rstp_port *);
+
+#endif /* rstp.h */
diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
index 5b5fb6e..8a330f1 100644
--- a/ofproto/ofproto-dpif-upcall.c
+++ b/ofproto/ofproto-dpif-upcall.c
@@ -921,7 +921,7 @@ compose_slow_path(struct udpif *udpif, struct xlate_out *xout,
cookie.slow_path.unused = 0;
cookie.slow_path.reason = xout->slow;
- port = xout->slow & (SLOW_CFM | SLOW_BFD | SLOW_LACP | SLOW_STP)
+ port = xout->slow & (SLOW_CFM | SLOW_BFD | SLOW_LACP | SLOW_STP | SLOW_RSTP)
? ODPP_NONE
: odp_in_port;
pid = dpif_port_get_pid(udpif->dpif, port, 0);
@@ -1128,8 +1128,8 @@ handle_upcalls(struct handler *handler, struct list *upcalls)
* case each packet of a miss can share the same actions, but slow-pathed
* packets need to be translated individually:
*
- * - For SLOW_CFM, SLOW_LACP, SLOW_STP, and SLOW_BFD, translation is what
- * processes received packets for these protocols.
+ * - For SLOW_CFM, SLOW_LACP, SLOW_STP, SLOW_RSTP and SLOW_BFD,
+ * translation is what processes received packets for these protocols.
*
* - For SLOW_CONTROLLER, translation sends the packet to the OpenFlow
* controller.
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index e26b7c9..9a0c718 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -80,6 +80,7 @@ struct xbridge {
struct dpif_ipfix *ipfix; /* Ipfix handle, or null. */
struct netflow *netflow; /* Netflow handle, or null. */
struct stp *stp; /* STP or null if disabled. */
+ struct rstp *rstp; /* RSTP or null if disabled. */
/* Special rules installed by ofproto-dpif. */
struct rule_dpif *miss_rule;
@@ -140,6 +141,7 @@ struct xport {
enum ofputil_port_config config; /* OpenFlow port configuration. */
enum ofputil_port_state state; /* OpenFlow port state. */
int stp_port_no; /* STP port number or -1 if not in use. */
+ int rstp_port_no; /* RSTP port number or -1 if not in use. */
struct hmap skb_priorities; /* Map of 'skb_priority_to_dscp's. */
@@ -252,6 +254,7 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
struct dpif *dpif, struct rule_dpif *miss_rule,
struct rule_dpif *no_packet_in_rule,
const struct mac_learning *ml, struct stp *stp,
+ struct rstp *rstp,
const struct mbridge *mbridge,
const struct dpif_sflow *sflow,
const struct dpif_ipfix *ipfix,
@@ -296,6 +299,11 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
xbridge->stp = stp_ref(stp);
}
+ if (xbridge->rstp != rstp) {
+ rstp_unref(xbridge->rstp);
+ xbridge->rstp = rstp_ref(rstp);
+ }
+
if (xbridge->netflow != netflow) {
netflow_unref(xbridge->netflow);
xbridge->netflow = netflow_ref(netflow);
@@ -339,6 +347,7 @@ xlate_remove_ofproto(struct ofproto_dpif *ofproto)
dpif_sflow_unref(xbridge->sflow);
dpif_ipfix_unref(xbridge->ipfix);
stp_unref(xbridge->stp);
+ rstp_unref(xbridge->rstp);
hmap_destroy(&xbridge->xports);
free(xbridge->name);
free(xbridge);
@@ -413,7 +422,7 @@ xlate_ofport_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle,
struct ofport_dpif *ofport, ofp_port_t ofp_port,
odp_port_t odp_port, const struct netdev *netdev,
const struct cfm *cfm, const struct bfd *bfd,
- struct ofport_dpif *peer, int stp_port_no,
+ struct ofport_dpif *peer, int stp_port_no, int rstp_port_no,
const struct ofproto_port_queue *qdscp_list, size_t n_qdscp,
enum ofputil_port_config config,
enum ofputil_port_state state, bool is_tunnel,
@@ -439,6 +448,7 @@ xlate_ofport_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle,
xport->config = config;
xport->state = state;
xport->stp_port_no = stp_port_no;
+ xport->rstp_port_no = rstp_port_no;
xport->is_tunnel = is_tunnel;
xport->may_enable = may_enable;
xport->odp_port = odp_port;
@@ -721,6 +731,67 @@ stp_process_packet(const struct xport *xport, const struct ofpbuf *packet)
}
}
+static struct rstp_port *
+xport_get_rstp_port(const struct xport *xport)
+{
+ return xport->xbridge->rstp && xport->rstp_port_no != -1
+ ? rstp_get_port(xport->xbridge->rstp, xport->rstp_port_no)
+ : NULL;
+}
+
+static bool
+xport_rstp_learn_state(const struct xport *xport)
+{
+ struct rstp_port *rp = xport_get_rstp_port(xport);
+ return rstp_learn_in_state(rp ? rstp_port_get_state(rp) : RSTP_DISABLED);
+}
+
+static bool
+xport_rstp_forward_state(const struct xport *xport)
+{
+ struct rstp_port *rp = xport_get_rstp_port(xport);
+ return rstp_forward_in_state(rp ? rstp_port_get_state(rp) : RSTP_DISABLED);
+}
+
+static bool
+xport_rstp_should_manage_bpdu(const struct xport *xport)
+{
+ struct rstp_port *rp = xport_get_rstp_port(xport);
+ return rstp_should_manage_bpdu(rp ? rstp_port_get_state(rp) : RSTP_DISABLED);
+}
+
+/* Returns true if RSTP should process 'flow'. Sets fields in 'wc' that
+ * were used to make the determination.*/
+static bool
+rstp_should_process_flow(const struct flow *flow, struct flow_wildcards *wc)
+{
+ memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
+ return eth_addr_equals(flow->dl_dst, eth_addr_rstp);
+}
+
+static void
+rstp_process_packet(const struct xport *xport, const struct ofpbuf *packet)
+{
+ struct rstp_port *rp = xport_get_rstp_port(xport);
+ struct ofpbuf payload = *packet;
+ struct eth_header *eth = payload.data;
+
+ /* Sink packets on ports that have RSTP disabled when the bridge has
+ * RSTP enabled. */
+ if (!rp || rstp_port_get_state(rp) == RSTP_DISABLED) {
+ return;
+ }
+
+ /* Trim off padding on payload. */
+ if (payload.size > ntohs(eth->eth_type) + ETH_HEADER_LEN) {
+ payload.size = ntohs(eth->eth_type) + ETH_HEADER_LEN;
+ }
+
+ if (ofpbuf_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) {
+ rstp_received_bpdu(rp, payload.data, payload.size);
+ }
+}
+
static struct xport *
get_ofp_port(const struct xbridge *xbridge, ofp_port_t ofp_port)
{
@@ -1673,6 +1744,11 @@ process_special(struct xlate_ctx *ctx, const struct flow *flow,
stp_process_packet(xport, packet);
}
return SLOW_STP;
+ } else if (xbridge->rstp && rstp_should_process_flow(flow, wc)) {
+ if (packet) {
+ rstp_process_packet(xport, packet);
+ }
+ return SLOW_RSTP;
} else {
return 0;
}
@@ -1680,7 +1756,7 @@ process_special(struct xlate_ctx *ctx, const struct flow *flow,
static void
compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
- bool check_stp)
+ bool check_stp, bool check_rstp)
{
const struct xport *xport = get_ofp_port(ctx->xbridge, ofp_port);
struct flow_wildcards *wc = &ctx->xout->wc;
@@ -1701,17 +1777,22 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
} else if (xport->config & OFPUTIL_PC_NO_FWD) {
xlate_report(ctx, "OFPPC_NO_FWD set, skipping output");
return;
- } else if (check_stp) {
- if (eth_addr_equals(ctx->base_flow.dl_dst, eth_addr_stp)) {
- if (!xport_stp_listen_state(xport)) {
+ } else if (check_stp && check_rstp) {
+ if (eth_addr_equals(ctx->base_flow.dl_dst, eth_addr_stp) &&
+ eth_addr_equals(ctx->base_flow.dl_dst, eth_addr_rstp)) {
+ if (!xport_stp_listen_state(xport) &&
+ !xport_rstp_should_manage_bpdu(xport)) {
xlate_report(ctx, "STP not in listening state, "
- "skipping bpdu output");
+ "RSTP does not manage BPDU in this state, "
+ "skipping bpdu output");
+ return;
+ } else if (!xport_stp_forward_state(xport) &&
+ !xport_rstp_forward_state(xport)) {
+ xlate_report(ctx, "STP not in forwarding state, "
+ "RSTP not in forwarding state, "
+ "skipping output");
return;
}
- } else if (!xport_stp_forward_state(xport)) {
- xlate_report(ctx, "STP not in forwarding state, "
- "skipping output");
- return;
}
}
@@ -1738,9 +1819,11 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
} else if (may_receive(peer, ctx)) {
if (xport_stp_forward_state(peer)) {
xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true);
+ } else if (xport_rstp_forward_state(peer)) {
+ xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true);
} else {
- /* Forwarding is disabled by STP. Let OFPP_NORMAL and the
- * learning action look at the packet, then drop it. */
+ /* Forwarding is disabled by STP and RSTP. Let OFPP_NORMAL and
+ * the learning action look at the packet, then drop it. */
struct flow old_base_flow = ctx->base_flow;
size_t old_size = ctx->xout->odp_actions.size;
mirror_mask_t old_mirrors = ctx->xout->mirrors;
@@ -1835,7 +1918,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
static void
compose_output_action(struct xlate_ctx *ctx, ofp_port_t ofp_port)
{
- compose_output_action__(ctx, ofp_port, true);
+ compose_output_action__(ctx, ofp_port, true, true);
}
static void
@@ -2106,7 +2189,7 @@ flood_packets(struct xlate_ctx *ctx, bool all)
}
if (all) {
- compose_output_action__(ctx, xport->ofp_port, false);
+ compose_output_action__(ctx, xport->ofp_port, false, false);
} else if (!(xport->config & OFPUTIL_PC_NO_FLOOD)) {
compose_output_action(ctx, xport->ofp_port);
}
@@ -2528,7 +2611,8 @@ may_receive(const struct xport *xport, struct xlate_ctx *ctx)
* disabled. If just learning is enabled, we need to have
* OFPP_NORMAL and the learning action have a look at the packet
* before we can drop it. */
- if (!xport_stp_forward_state(xport) && !xport_stp_learn_state(xport)) {
+ if (!xport_stp_forward_state(xport) && !xport_stp_learn_state(xport)
+ && !xport_rstp_forward_state(xport) && !xport_rstp_learn_state(xport)) {
return false;
}
@@ -3141,6 +3225,9 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout)
if (in_port && !xport_stp_forward_state(in_port)) {
ctx.xout->odp_actions.size = sample_actions_len;
}
+ else if (in_port && !xport_rstp_forward_state(in_port)) {
+ ctx.xout->odp_actions.size = sample_actions_len;
+ }
}
if (ctx.action_set.size) {
diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h
index 8b01d4e..086a061 100644
--- a/ofproto/ofproto-dpif-xlate.h
+++ b/ofproto/ofproto-dpif-xlate.h
@@ -126,6 +126,7 @@ void xlate_ofproto_set(struct ofproto_dpif *, const char *name,
struct dpif *, struct rule_dpif *miss_rule,
struct rule_dpif *no_packet_in_rule,
const struct mac_learning *, struct stp *,
+ struct rstp *,
const struct mbridge *, const struct dpif_sflow *,
const struct dpif_ipfix *, const struct netflow *,
enum ofp_config_flags, bool forward_bpdu,
@@ -145,7 +146,8 @@ void xlate_ofport_set(struct ofproto_dpif *, struct ofbundle *,
struct ofport_dpif *, ofp_port_t, odp_port_t,
const struct netdev *, const struct cfm *,
const struct bfd *, struct ofport_dpif *peer,
- int stp_port_no, const struct ofproto_port_queue *qdscp,
+ int stp_port_no, int rstp_port_no,
+ const struct ofproto_port_queue *qdscp,
size_t n_qdscp, enum ofputil_port_config,
enum ofputil_port_state, bool is_tunnel,
bool may_enable) OVS_REQ_WRLOCK(xlate_rwlock);
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 7172cb2..c8e36f4 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -141,6 +141,10 @@ static void stp_wait(struct ofproto_dpif *ofproto);
static int set_stp_port(struct ofport *,
const struct ofproto_port_stp_settings *);
+static void rstp_run(struct ofproto_dpif *ofproto);
+static int set_rstp_port(struct ofport *,
+ const struct ofproto_port_rstp_settings *);
+
struct ofport_dpif {
struct hmap_node odp_port_node; /* In dpif_backer's "odp_to_ofport_map". */
struct ofport up;
@@ -161,6 +165,10 @@ struct ofport_dpif {
enum stp_state stp_state; /* Always STP_DISABLED if STP not in use. */
long long int stp_state_entered;
+ /*Rapid Spanning Tree. */
+ struct rstp_port *rstp_port; /* Rapid Spanning Tree Protocol, if any. */
+ enum rstp_state rstp_state; /* Always RSTP_DISABLED if RSTP not in use. */
+
/* Queue to DSCP mapping. */
struct ofproto_port_queue *qdscp;
size_t n_qdscp;
@@ -225,6 +233,7 @@ struct dpif_completion {
enum revalidate_reason {
REV_RECONFIGURE = 1, /* Switch configuration changed. */
REV_STP, /* Spanning tree protocol port status change. */
+ REV_RSTP, /* RSTP port status change. */
REV_BOND, /* Bonding changed. */
REV_PORT_TOGGLED, /* Port enabled or disabled by CFM, LACP, ...*/
REV_FLOW_TABLE, /* Flow table changed. */
@@ -232,6 +241,7 @@ enum revalidate_reason {
};
COVERAGE_DEFINE(rev_reconfigure);
COVERAGE_DEFINE(rev_stp);
+COVERAGE_DEFINE(rev_rstp);
COVERAGE_DEFINE(rev_bond);
COVERAGE_DEFINE(rev_port_toggled);
COVERAGE_DEFINE(rev_flow_table);
@@ -298,6 +308,10 @@ struct ofproto_dpif {
struct stp *stp;
long long int stp_last_tick;
+ /*Rapid Spanning Tree. */
+ struct rstp *rstp;
+ long long int rstp_last_tick;
+
/* VLAN splinters. */
struct ovs_mutex vsp_mutex;
struct hmap realdev_vid_map OVS_GUARDED; /* (realdev,vid) -> vlandev. */
@@ -547,6 +561,7 @@ type_run(const char *type)
switch (backer->need_revalidate) {
case REV_RECONFIGURE: COVERAGE_INC(rev_reconfigure); break;
case REV_STP: COVERAGE_INC(rev_stp); break;
+ case REV_RSTP: COVERAGE_INC(rev_rstp); break;
case REV_BOND: COVERAGE_INC(rev_bond); break;
case REV_PORT_TOGGLED: COVERAGE_INC(rev_port_toggled); break;
case REV_FLOW_TABLE: COVERAGE_INC(rev_flow_table); break;
@@ -566,7 +581,7 @@ type_run(const char *type)
xlate_ofproto_set(ofproto, ofproto->up.name,
ofproto->backer->dpif, ofproto->miss_rule,
ofproto->no_packet_in_rule, ofproto->ml,
- ofproto->stp, ofproto->mbridge,
+ ofproto->stp, ofproto->rstp, ofproto->mbridge,
ofproto->sflow, ofproto->ipfix,
ofproto->netflow, ofproto->up.frag_handling,
ofproto->up.forward_bpdu,
@@ -586,14 +601,19 @@ type_run(const char *type)
int stp_port = ofport->stp_port
? stp_port_no(ofport->stp_port)
: -1;
+ int rstp_port = ofport->rstp_port
+ ? rstp_port_no(ofport->rstp_port)
+ : -1;
xlate_ofport_set(ofproto, ofport->bundle, ofport,
ofport->up.ofp_port, ofport->odp_port,
ofport->up.netdev, ofport->cfm,
ofport->bfd, ofport->peer, stp_port,
+ rstp_port,
ofport->qdscp, ofport->n_qdscp,
ofport->up.pp.config, ofport->up.pp.state,
ofport->is_tunnel, ofport->may_enable);
}
+
ovs_rwlock_unlock(&xlate_rwlock);
}
@@ -1046,6 +1066,7 @@ construct(struct ofproto *ofproto_)
ofproto->sflow = NULL;
ofproto->ipfix = NULL;
ofproto->stp = NULL;
+ ofproto->rstp = NULL;
ofproto->dump_seq = 0;
hmap_init(&ofproto->bundles);
ofproto->ml = mac_learning_create(MAC_ENTRY_DEFAULT_IDLE_TIME);
@@ -1288,6 +1309,7 @@ run(struct ofproto *ofproto_)
}
stp_run(ofproto);
+ rstp_run(ofproto);
ovs_rwlock_wrlock(&ofproto->ml->rwlock);
if (mac_learning_run(ofproto->ml)) {
ofproto->backer->need_revalidate = REV_MAC_LEARNING;
@@ -1459,6 +1481,8 @@ port_construct(struct ofport *port_)
port->may_enable = true;
port->stp_port = NULL;
port->stp_state = STP_DISABLED;
+ port->rstp_port = NULL;
+ port->rstp_state = RSTP_DISABLED;
port->is_tunnel = false;
port->peer = NULL;
port->qdscp = NULL;
@@ -1562,6 +1586,9 @@ port_destruct(struct ofport *port_)
if (port->stp_port) {
stp_port_disable(port->stp_port);
}
+ if (port->rstp_port) {
+ rstp_port_disable(port->rstp_port);
+ }
if (ofproto->sflow) {
dpif_sflow_del_port(ofproto->sflow, port->odp_port);
}
@@ -1604,7 +1631,7 @@ port_reconfigured(struct ofport *port_, enum ofputil_port_config old_config)
struct ofport_dpif *port = ofport_dpif_cast(port_);
struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
enum ofputil_port_config changed = old_config ^ port->up.pp.config;
-
+ /* FIXME add OFPUTIL_PC_NO_RECV_RSTP? */
if (changed & (OFPUTIL_PC_NO_RECV | OFPUTIL_PC_NO_RECV_STP |
OFPUTIL_PC_NO_FWD | OFPUTIL_PC_NO_FLOOD |
OFPUTIL_PC_NO_PACKET_IN)) {
@@ -1757,6 +1784,31 @@ get_bfd_status(struct ofport *ofport_, struct smap *smap)
/* Spanning Tree. */
static void
+rstp_send_bpdu_cb(struct ofpbuf *pkt, int port_num, void *ofproto_)
+{
+ struct ofproto_dpif *ofproto = ofproto_;
+ struct rstp_port *rp = rstp_get_port(ofproto->rstp, port_num);
+ struct ofport_dpif *ofport;
+
+ ofport = rstp_port_get_aux(rp);
+ if (!ofport) {
+ VLOG_WARN_RL(&rl, "%s: cannot send BPDU on unknown port %d",
+ ofproto->up.name, port_num);
+ } else {
+ struct eth_header *eth = pkt->l2;
+
+ netdev_get_etheraddr(ofport->up.netdev, eth->eth_src);
+ if (eth_addr_is_zero(eth->eth_src)) {
+ VLOG_WARN_RL(&rl, "%s: cannot send BPDU on port %d "
+ "with unknown MAC", ofproto->up.name, port_num);
+ } else {
+ ofproto_dpif_send_packet(ofport, pkt);
+ }
+ }
+ ofpbuf_delete(pkt);
+}
+
+static void
send_bpdu_cb(struct ofpbuf *pkt, int port_num, void *ofproto_)
{
struct ofproto_dpif *ofproto = ofproto_;
@@ -1781,12 +1833,149 @@ send_bpdu_cb(struct ofpbuf *pkt, int port_num, void *ofproto_)
ofpbuf_delete(pkt);
}
+/* Configure RSTP on 'ofproto_' using the settings defined in 's'. */
+static int
+set_rstp(struct ofproto *ofproto_, const struct ofproto_rstp_settings *s)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+
+ /* Only revalidate flows if the configuration changed. */
+ if (!s != !ofproto->rstp) {
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
+ }
+
+ if (s) {
+ if (!ofproto->rstp) {
+ ofproto->rstp = rstp_create(ofproto_->name, (uint8_t *)s->address,
+ rstp_send_bpdu_cb, ofproto);
+ ofproto->rstp_last_tick = time_msec();
+ }
+ rstp_set_bridge_address(ofproto->rstp, (uint8_t *)s->address);
+ rstp_set_bridge_priority(ofproto->rstp, s->priority);
+ rstp_set_bridge_ageing_time(ofproto->rstp, s->ageing_time);
+ rstp_set_bridge_force_protocol_version(ofproto->rstp,
+ s->force_protocol_version);
+ rstp_set_bridge_max_age(ofproto->rstp, s->bridge_max_age);
+ rstp_set_bridge_forward_delay(ofproto->rstp, s->bridge_forward_delay);
+ rstp_set_bridge_transmit_hold_count(ofproto->rstp,
+ s->transmit_hold_count);
+ } else {
+ struct ofport *ofport;
+ HMAP_FOR_EACH (ofport, hmap_node, &ofproto->up.ports) {
+ set_rstp_port(ofport, NULL);
+ }
+ rstp_unref(ofproto->rstp);
+ ofproto->rstp = NULL;
+ }
+
+ return 0;
+}
+
+static int
+get_rstp_status(struct ofproto *ofproto_, struct ofproto_rstp_status *s)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+
+ if (ofproto->rstp) {
+ s->enabled = true;
+ memcpy(s->root_id, rstp_get_root_id(ofproto->rstp),
+ sizeof(s->root_id));
+ memcpy(s->bridge_id, rstp_get_bridge_id(ofproto->rstp),
+ sizeof(s->bridge_id));
+ memcpy(s->designated_id, rstp_get_designated_id(ofproto->rstp),
+ sizeof(s->designated_id));
+ memcpy(s->root_path_cost, rstp_get_root_path_cost(ofproto->rstp),
+ sizeof(s->root_path_cost));
+ memcpy(s->designated_port_id, rstp_get_designated_port_id(ofproto->rstp),
+ sizeof(s->designated_port_id));
+ memcpy(s->bridge_port_id, rstp_get_bridge_port_id(ofproto->rstp),
+ sizeof(s->bridge_port_id));
+ } else {
+ s->enabled = false;
+ }
+
+ return 0;
+}
+
+static void
+update_rstp_port_state(struct ofport_dpif *ofport)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+ enum rstp_state state;
+
+ /* Figure out new state. */
+ state = ofport->rstp_port ? rstp_port_get_state(ofport->rstp_port)
+ : RSTP_DISABLED;
+
+ /* Update state. */
+ if (ofport->rstp_state != state) {
+ enum ofputil_port_state of_state;
+ bool fwd_change;
+ VLOG_DBG_RL(&rl, "port %s: RSTP state changed from %s to %s",
+ netdev_get_name(ofport->up.netdev),
+ rstp_state_name(ofport->rstp_state),
+ rstp_state_name(state));
+ if (rstp_learn_in_state(ofport->rstp_state)
+ != rstp_learn_in_state(state)) {
+ /* xxx Learning action flows should also be flushed. */
+ ovs_rwlock_wrlock(&ofproto->ml->rwlock);
+ mac_learning_flush(ofproto->ml);
+ ovs_rwlock_unlock(&ofproto->ml->rwlock);
+ }
+ fwd_change = rstp_forward_in_state(ofport->rstp_state)
+ != rstp_forward_in_state(state);
+
+ ofproto->backer->need_revalidate = REV_RSTP;
+ ofport->rstp_state = state;
+
+ if (fwd_change && ofport->bundle) {
+ bundle_update(ofport->bundle);
+ }
+
+ /* Update the RSTP state bits in the OpenFlow port description. */
+ of_state = ofport->up.pp.state & ~OFPUTIL_PS_STP_MASK;
+ of_state |= (state == RSTP_LEARNING ? OFPUTIL_PS_STP_LEARN
+ : state == RSTP_FORWARDING ? OFPUTIL_PS_STP_FORWARD
+ : state == RSTP_DISCARDING ? OFPUTIL_PS_STP_LISTEN
+ : 0);
+ /* FIXME: check if the Discarding[Listening] behaviour is correct. */
+ ofproto_port_set_state(&ofport->up, of_state);
+ }
+}
+
+static void
+rstp_run(struct ofproto_dpif *ofproto)
+{
+ if (ofproto->rstp) {
+ long long int now = time_msec();
+ long long int elapsed = now - ofproto->rstp_last_tick;
+ struct rstp_port *rp;
+ /* Every second, decrease the values of the timers. */
+ if (elapsed >= 1000) {
+ rstp_tick_timers(ofproto->rstp);
+ ofproto->rstp_last_tick = now;
+ }
+ while (rstp_get_changed_port(ofproto->rstp, &rp)) {
+ struct ofport_dpif *ofport = rstp_port_get_aux(rp);
+ if (ofport) {
+ update_rstp_port_state(ofport);
+ }
+ }
+ /* FIXME: This check should be done on-event (i.e., when setting p->fdb_flush) and not periodically. */
+ if (rstp_check_and_reset_fdb_flush(ofproto->rstp)) {
+ ovs_rwlock_wrlock(&ofproto->ml->rwlock);
+ /* FIXME: RSTP should be able to flush the entries pertaining to a single port, not the whole table. */
+ mac_learning_flush(ofproto->ml);
+ ovs_rwlock_unlock(&ofproto->ml->rwlock);
+ }
+ }
+}
+
/* Configures STP on 'ofproto_' using the settings defined in 's'. */
static int
set_stp(struct ofproto *ofproto_, const struct ofproto_stp_settings *s)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-
/* Only revalidate flows if the configuration changed. */
if (!s != !ofproto->stp) {
ofproto->backer->need_revalidate = REV_RECONFIGURE;
@@ -1810,7 +1999,6 @@ set_stp(struct ofproto *ofproto_, const struct ofproto_stp_settings *s)
HMAP_FOR_EACH (ofport, hmap_node, &ofproto->up.ports) {
set_stp_port(ofport, NULL);
}
-
stp_unref(ofproto->stp);
ofproto->stp = NULL;
}
@@ -1849,7 +2037,6 @@ update_stp_port_state(struct ofport_dpif *ofport)
if (ofport->stp_state != state) {
enum ofputil_port_state of_state;
bool fwd_change;
-
VLOG_DBG_RL(&rl, "port %s: STP state changed from %s to %s",
netdev_get_name(ofport->up.netdev),
stp_state_name(ofport->stp_state),
@@ -1893,7 +2080,6 @@ set_stp_port(struct ofport *ofport_,
struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
struct stp_port *sp = ofport->stp_port;
-
if (!s || !s->enable) {
if (sp) {
ofport->stp_port = NULL;
@@ -1975,7 +2161,6 @@ stp_run(struct ofproto_dpif *ofproto)
}
while (stp_get_changed_port(ofproto->stp, &sp)) {
struct ofport_dpif *ofport = stp_port_get_aux(sp);
-
if (ofport) {
update_stp_port_state(ofport);
}
@@ -1996,6 +2181,72 @@ stp_wait(struct ofproto_dpif *ofproto)
poll_timer_wait(1000);
}
}
+
+/* Configures RSTP on 'ofport_' using the settings defined in 's'. The
+ * caller is responsible for assigning RSTP port numbers and ensuring
+ * there are no duplicates. */
+static int
+set_rstp_port(struct ofport *ofport_,
+ const struct ofproto_port_rstp_settings *s)
+{
+ struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+ struct rstp_port *rp = ofport->rstp_port;
+ int port_index;
+ if (!s || !s->enable) {
+ if (rp) {
+ ofport->rstp_port = NULL;
+ rstp_port_disable(rp);
+ update_rstp_port_state(ofport);
+ }
+ return 0;
+ } else if ( rp && rstp_port_no(rp) != s->port_num
+ && ofport == rstp_port_get_aux(rp)) {
+ /* The port-id changed, so disable the old one if it's not
+ * already in use by another port. */
+ rstp_port_disable(rp);
+ }
+ port_index = s->port_num - 1;
+ rp = ofport->rstp_port = rstp_get_port(ofproto->rstp, port_index);
+ /* Enable RSTP on port */
+ rstp_port_enable(rp);
+ /* Setters */
+ rstp_port_set_aux(rp, ofport);
+ rstp_port_set_priority(rp, s->priority);
+ rstp_port_set_port_number(rp, s->port_num);
+ rstp_port_set_path_cost(rp, s->path_cost);
+ rstp_port_set_admin_edge(rp, s->admin_edge_port);
+ rstp_port_set_auto_edge(rp, s->auto_edge);
+ rstp_port_set_mcheck(rp, s->mcheck);
+
+ update_rstp_port_state(ofport);
+
+ return 0;
+}
+
+static int
+get_rstp_port_status(struct ofport *ofport_,
+ struct ofproto_port_rstp_status *s)
+{
+ struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+ struct rstp_port *rp = ofport->rstp_port;
+
+ if (!ofproto->rstp || !rp) {
+ s->enabled = false;
+ return 0;
+ }
+
+ s->enabled = true;
+ memcpy(s->port_id, rstp_port_get_id(rp), sizeof(s->port_id));
+ s->state = rstp_port_get_state(rp);
+ s->role = rstp_port_get_role(rp);
+ rstp_port_get_counts(rp, &s->tx_count, &s->rx_count,
+ &s->error_count, &s->uptime);
+
+ return 0;
+}
+
static int
set_queues(struct ofport *ofport_, const struct ofproto_port_queue *qdscp,
@@ -2682,6 +2933,17 @@ port_run(struct ofport_dpif *ofport)
}
ofport->may_enable = enable;
+
+ if (ofport->rstp_port) {
+ if (rstp_port_get_mac_operational(ofport->rstp_port) == false &&
+ enable == true) {
+ rstp_port_set_mac_operational(ofport->rstp_port, 1);
+ }
+ if (rstp_port_get_mac_operational(ofport->rstp_port) == true &&
+ enable == false) {
+ rstp_port_set_mac_operational(ofport->rstp_port, 0);
+ }
+ }
}
static int
@@ -3521,7 +3783,7 @@ ofproto_dpif_send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet
ovs_mutex_unlock(&ofproto->stats_mutex);
return error;
}
-
+
static bool
set_frag_handling(struct ofproto *ofproto_,
enum ofp_config_flags frag_handling)
@@ -4694,6 +4956,10 @@ const struct ofproto_class ofproto_dpif_class = {
set_stp_port,
get_stp_port_status,
get_stp_port_stats,
+ set_rstp,
+ get_rstp_status,
+ set_rstp_port,
+ get_rstp_port_status,
set_queues,
bundle_set,
bundle_remove,
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index a7bf4df..44b55bd 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -1540,6 +1540,53 @@ struct ofproto_class {
int (*get_stp_port_stats)(struct ofport *ofport,
struct ofproto_port_stp_stats *s);
+ /* Configures Rapid Spanning Tree Protocol (RSTP) on 'ofproto' using the
+ * settings defined in 's'.
+ *
+ * If 's' is nonnull, configures RSTP according to its members.
+ *
+ * If 's' is null, removes any RSTP configuration from 'ofproto'.
+ *
+ * EOPNOTSUPP as a return value indicates that this ofproto_class does not
+ * support RSTP, as does a null pointer. */
+ int (*set_rstp)(struct ofproto *ofproto,
+ const struct ofproto_rstp_settings *s);
+
+ /* Retrieves state of Rapid Spanning Tree Protocol (RSTP) on 'ofproto'.
+ *
+ * Stores RSTP state for 'ofproto' in 's'. If the 'enabled' member
+ * is false, the other member values are not meaningful.
+ *
+ * EOPNOTSUPP as a return value indicates that this ofproto_class does not
+ * support RSTP, as does a null pointer. */
+ int (*get_rstp_status)(struct ofproto *ofproto,
+ struct ofproto_rstp_status *s);
+
+ /* Configures Rapid Spanning Tree Protocol (RSTP) on 'ofport' using the
+ * settings defined in 's'.
+ *
+ * If 's' is nonnull, configures RSTP according to its members. The
+ * caller is responsible for assigning RSTP port numbers (using the
+ * 'port_num' member in the range of 1 through 255, inclusive) and
+ * ensuring there are no duplicates.
+ *
+ * If 's' is null, removes any RSTP configuration from 'ofport'.
+ *
+ * EOPNOTSUPP as a return value indicates that this ofproto_class does not
+ * support STP, as does a null pointer. */
+ int (*set_rstp_port)(struct ofport *ofport,
+ const struct ofproto_port_rstp_settings *s);
+
+ /* Retrieves Rapid Spanning Tree Protocol (RSTP) port status of 'ofport'.
+ *
+ * Stores RSTP state for 'ofport' in 's'. If the 'enabled' member is
+ * false, the other member values are not meaningful.
+ *
+ * EOPNOTSUPP as a return value indicates that this ofproto_class does not
+ * support RSTP, as does a null pointer. */
+ int (*get_rstp_port_status)(struct ofport *ofport,
+ struct ofproto_port_rstp_status *s);
+
/* Registers meta-data associated with the 'n_qdscp' Qualities of Service
* 'queues' attached to 'ofport'. This data is not intended to be
* sufficient to implement QoS. Instead, providers may use this
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index b2d6526..a3f6e25 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -903,12 +903,12 @@ ofproto_port_get_stp_status(struct ofproto *ofproto, ofp_port_t ofp_port,
* Returns 0 if successful, otherwise a positive errno value.*/
int
ofproto_port_get_stp_stats(struct ofproto *ofproto, ofp_port_t ofp_port,
- struct ofproto_port_stp_stats *s)
+ struct ofproto_port_stp_stats *s)
{
struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
if (!ofport) {
VLOG_WARN_RL(&rl, "%s: cannot get STP stats on nonexistent "
- "port %"PRIu16, ofproto->name, ofp_port);
+ "port %"PRIu16, ofproto->name, ofp_port);
return ENODEV;
}
@@ -916,6 +916,79 @@ ofproto_port_get_stp_stats(struct ofproto *ofproto, ofp_port_t ofp_port,
? ofproto->ofproto_class->get_stp_port_stats(ofport, s)
: EOPNOTSUPP);
}
+
+/* Rapid Spanning Tree Protocol (RSTP) configuration. */
+
+/* Configures RSTP on 'ofproto' using the settings defined in 's'. If
+ * 's' is NULL, disables RSTP.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. */
+int
+ofproto_set_rstp(struct ofproto *ofproto,
+ const struct ofproto_rstp_settings *s)
+{
+ return (ofproto->ofproto_class->set_rstp
+ ? ofproto->ofproto_class->set_rstp(ofproto, s)
+ : EOPNOTSUPP);
+}
+
+/* Retrieves RSTP status of 'ofproto' and stores it in 's'. If the
+ * 'enabled' member of 's' is false, then the other members are not
+ * meaningful.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. */
+int
+ofproto_get_rstp_status(struct ofproto *ofproto,
+ struct ofproto_rstp_status *s)
+{
+ return (ofproto->ofproto_class->get_rstp_status
+ ? ofproto->ofproto_class->get_rstp_status(ofproto, s)
+ : EOPNOTSUPP);
+}
+
+/* Configures RSTP on 'ofp_port' of 'ofproto' using the settings defined
+ * in 's'. The caller is responsible for assigning RSTP port numbers
+ * (using the 'port_num' member in the range of 1 through 255, inclusive)
+ * and ensuring there are no duplicates. If the 's' is NULL, then RSTP
+ * is disabled on the port.
+ *
+ * Returns 0 if successful, otherwise a positive errno value.*/
+int
+ofproto_port_set_rstp(struct ofproto *ofproto, ofp_port_t ofp_port,
+ const struct ofproto_port_rstp_settings *s)
+{
+ struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
+ if (!ofport) {
+ VLOG_WARN("%s: cannot configure RSTP on nonexistent port %"PRIu16,
+ ofproto->name, ofp_port);
+ return ENODEV;
+ }
+
+ return (ofproto->ofproto_class->set_rstp_port
+ ? ofproto->ofproto_class->set_rstp_port(ofport, s)
+ : EOPNOTSUPP);
+}
+
+/* Retrieves RSTP port status of 'ofp_port' on 'ofproto' and stores it in
+ * 's'. If the 'enabled' member in 's' is false, then the other members
+ * are not meaningful.
+ *
+ * Returns 0 if successful, otherwise a positive errno value.*/
+int
+ofproto_port_get_rstp_status(struct ofproto *ofproto, ofp_port_t ofp_port,
+ struct ofproto_port_rstp_status *s)
+{
+ struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
+ if (!ofport) {
+ VLOG_WARN_RL(&rl, "%s: cannot get RSTP status on nonexistent "
+ "port %"PRIu16, ofproto->name, ofp_port);
+ return ENODEV;
+ }
+
+ return (ofproto->ofproto_class->get_rstp_port_status
+ ? ofproto->ofproto_class->get_rstp_port_status(ofport, s)
+ : EOPNOTSUPP);
+}
/* Queue DSCP configuration. */
@@ -2285,6 +2358,9 @@ ofproto_port_unregister(struct ofproto *ofproto, ofp_port_t ofp_port)
if (port->ofproto->ofproto_class->set_stp_port) {
port->ofproto->ofproto_class->set_stp_port(port, NULL);
}
+ if (port->ofproto->ofproto_class->set_rstp_port) {
+ port->ofproto->ofproto_class->set_rstp_port(port, NULL);
+ }
if (port->ofproto->ofproto_class->set_cfm) {
port->ofproto->ofproto_class->set_cfm(port, NULL);
}
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index 309511c..d52b9fa 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -29,6 +29,7 @@
#include "netflow.h"
#include "sset.h"
#include "stp.h"
+#include "rstp.h"
#ifdef __cplusplus
extern "C" {
@@ -81,6 +82,47 @@ struct ofproto_ipfix_flow_exporter_options {
uint32_t cache_max_flows;
};
+struct ofproto_rstp_status {
+ bool enabled; /* If false, ignore other members. */
+ uint8_t root_id[8];
+ uint8_t bridge_id[8];
+ uint8_t designated_id[8];
+ uint8_t root_path_cost[4];
+ uint8_t designated_port_id[2];
+ uint8_t bridge_port_id[2];
+};
+
+struct ofproto_rstp_settings {
+ uint8_t address[6];
+ uint16_t priority;
+ unsigned int ageing_time;
+ enum rstp_force_protocol_version force_protocol_version;
+ unsigned int bridge_forward_delay;
+ unsigned int bridge_max_age;
+ unsigned int transmit_hold_count;
+};
+
+struct ofproto_port_rstp_status {
+ bool enabled; /* If false, ignore other members. */
+ uint8_t port_id[2];
+ enum rstp_port_role role;
+ enum rstp_state state;
+ int tx_count; /* Number of BPDUs transmitted. */
+ int rx_count; /* Number of valid BPDUs received. */
+ int error_count; /* Number of bad BPDUs received. */
+ int uptime;
+};
+
+struct ofproto_port_rstp_settings {
+ bool enable;
+ uint16_t port_num; /* In the range 1-4095, inclusive. */
+ uint8_t priority;
+ uint32_t path_cost;
+ bool admin_edge_port;
+ bool auto_edge;
+ bool mcheck;
+};
+
struct ofproto_stp_settings {
stp_identifier system_id;
uint16_t priority;
@@ -256,6 +298,9 @@ bool ofproto_get_flow_restore_wait(void);
int ofproto_set_stp(struct ofproto *, const struct ofproto_stp_settings *);
int ofproto_get_stp_status(struct ofproto *, struct ofproto_stp_status *);
+int ofproto_set_rstp(struct ofproto *, const struct ofproto_rstp_settings *);
+int ofproto_get_rstp_status(struct ofproto *, struct ofproto_rstp_status *);
+
/* Configuration of ports. */
void ofproto_port_unregister(struct ofproto *, ofp_port_t ofp_port);
@@ -276,6 +321,11 @@ int ofproto_port_get_stp_stats(struct ofproto *, ofp_port_t ofp_port,
int ofproto_port_set_queues(struct ofproto *, ofp_port_t ofp_port,
const struct ofproto_port_queue *,
size_t n_queues);
+int ofproto_port_get_rstp_status(struct ofproto *, ofp_port_t ofp_port,
+ struct ofproto_port_rstp_status *);
+
+int ofproto_port_set_rstp(struct ofproto *, ofp_port_t ofp_port,
+ const struct ofproto_port_rstp_settings *);
/* The behaviour of the port regarding VLAN handling */
enum port_vlan_mode {
diff --git a/tests/automake.mk b/tests/automake.mk
index 739d79e..6d99f2c 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -63,6 +63,7 @@ TESTSUITE_AT = \
tests/ovs-monitor-ipsec.at \
tests/ovs-xapi-sync.at \
tests/stp.at \
+ tests/rstp.at \
tests/interface-reconfigure.at \
tests/vlog.at \
tests/vtep-ctl.at
@@ -121,6 +122,7 @@ valgrind_wrappers = \
tests/valgrind/test-packets \
tests/valgrind/test-random \
tests/valgrind/test-reconnect \
+ tests/valgrind/test-rstp \
tests/valgrind/test-sha1 \
tests/valgrind/test-stp \
tests/valgrind/test-type-props \
@@ -259,6 +261,10 @@ noinst_PROGRAMS += tests/test-random
tests_test_random_SOURCES = tests/test-random.c
tests_test_random_LDADD = lib/libopenvswitch.la
+noinst_PROGRAMS += tests/test-rstp
+tests_test_rstp_SOURCES = tests/test-rstp.c
+tests_test_rstp_LDADD = lib/libopenvswitch.la
+
noinst_PROGRAMS += tests/test-stp
tests_test_stp_SOURCES = tests/test-stp.c
tests_test_stp_LDADD = lib/libopenvswitch.la
diff --git a/tests/ovs-vsctl.at b/tests/ovs-vsctl.at
index 440bf1a..33cd967 100644
--- a/tests/ovs-vsctl.at
+++ b/tests/ovs-vsctl.at
@@ -645,6 +645,8 @@ netflow : []
other_config : {}
ports : []
protocols : []
+rstp_enable : false
+rstp_status : {}
sflow : []
status : {}
stp_enable : false
@@ -1137,6 +1139,8 @@ netflow : []
other_config : {}
ports : []
protocols : []
+rstp_enable : false
+rstp_status : {}
sflow : []
status : {}
stp_enable : false
diff --git a/tests/rstp.at b/tests/rstp.at
new file mode 100644
index 0000000..5189a61
--- /dev/null
+++ b/tests/rstp.at
@@ -0,0 +1,149 @@
+AT_BANNER([Rapid Spanning Tree Protocol unit tests])
+
+AT_SETUP([RSTP Single bridge])
+AT_KEYWORDS([RSTP])
+AT_DATA([test-rstp-num1],
+[bridge 0 0x111 = a b
+run 1000
+check 0 = root
+])
+AT_CHECK([test-rstp test-rstp-num1], [0], [])
+AT_CLEANUP
+
+AT_SETUP([RSTP Link failure])
+AT_KEYWORDS([RSTP])
+AT_DATA([test-rstp-num2],
+[bridge 0 0x111 = a b
+bridge 1 0x222 = a c
+bridge 2 0x333 = b c
+run 1000
+check 0 = root
+check 1 = F:200000 F
+check 2 = F:200000 Di
+# Link b goes down
+bridge 2 = X c
+run 1000
+check 1 = F:200000 F
+check 2 = D F:400000
+])
+AT_CHECK([test-rstp test-rstp-num2], [0], [])
+AT_CLEANUP
+
+AT_SETUP([RSTP Double link Failure])
+AT_KEYWORDS([RSTP])
+AT_DATA([test-rstp-num3],
+[bridge 0 0x111 = a b
+bridge 1 0x222 = a c d
+bridge 2 0x333 = b c e
+bridge 3 0x444 = d f
+bridge 4 0x555 = e f
+run 1000
+check 0 = root
+check 1 = F:200000 F F
+check 2 = F:200000 Di F
+check 3 = F:400000 F
+check 4 = F:400000 Di
+# Link b goes down
+bridge 2 = X c e
+run 1000
+check 0 = root
+check 1 = F:200000 F F
+check 2 = D F:400000 F
+check 3 = F:400000 F
+check 4 = F:600000 Di
+# Link e goes down
+bridge 4 = X f
+run 1000
+check 0 = root
+check 1 = F:200000 F F
+check 2 = D F:400000 F
+check 3 = F:400000 F
+check 4 = D F:600000
+# Link f cost changes
+bridge 4 = X f:100000
+run 1000
+check 4 = D F:500000
+# Bridge 4 becomes root and
+bridge 4 ^ 31000
+run 1000
+check 4 = root
+])
+AT_CHECK([test-rstp test-rstp-num3], [0], [])
+AT_CLEANUP
+
+AT_SETUP([RSTP example from IEEE 802.1D-2004 figures 17.4 and 17.5])
+AT_KEYWORDS([RSTP])
+AT_DATA([test-rstp-ieee802.1d-2004-fig17.4],
+[bridge 0 0x111 = a b e c
+bridge 1 0x222 = a b d f
+bridge 2 0x333 = c d l j h g
+bridge 3 0x444 = e f n m k i
+bridge 4 0x555 = g i 0 0
+bridge 5 0x666 = h k 0 0
+bridge 6 0x777 = j m 0 0
+bridge 7 0x888 = l n 0 0
+run 1000
+check 0 = root
+check 1 = F:200000 Di F F
+check 2 = F:200000 Di F F F F
+check 3 = F:200000 Di F F F F
+check 4 = F:400000 Di F F
+check 5 = F:400000 Di F F
+check 6 = F:400000 Di F F
+check 7 = F:400000 Di F F
+
+# Now connect two ports of bridge 7 to the same LAN.
+bridge 7 = l n o o
+# Same results except for bridge 7:
+run 1000
+check 0 = root
+check 1 = F:200000 Di F F
+check 2 = F:200000 Di F F F F
+check 3 = F:200000 Di F F F F
+check 4 = F:400000 Di F F
+check 5 = F:400000 Di F F
+check 6 = F:400000 Di F F
+check 7 = F:400000 Di F Di
+])
+AT_CHECK([test-rstp test-rstp-ieee802.1d-2004-fig17.4], [0], [])
+AT_CLEANUP
+
+AT_SETUP([RSTP example from IEEE 802.1D-2004 figure 17.6])
+AT_KEYWORDS([RSTP])
+AT_DATA([test-rstp-ieee802.1d-2004-fig17.6],
+[bridge 0 0x111 = a b l
+bridge 1 0x222 = b c d
+bridge 2 0x333 = d e f
+bridge 3 0x444 = f g h
+bridge 4 0x555 = j h i
+bridge 5 0x666 = l j k
+run 1000
+check 0 = root
+check 1 = F:200000 F F
+check 2 = F:400000 F F
+check 3 = F:600000 F Di
+check 4 = F:400000 F F
+check 5 = F:200000 F F
+])
+AT_CHECK([test-rstp test-rstp-ieee802.1d-2004-fig17.6], [0], [])
+AT_CLEANUP
+
+AT_SETUP([RSTP example from IEEE 802.1D-2004 figure 17.7])
+AT_KEYWORDS([RSTP])
+AT_DATA([test-rstp-ieee802.1d-2004-fig17.7],
+[bridge 0 0x000 = b
+bridge 1 0x111 = a b d f h g e c
+bridge 2 0x222 = g h j l n m k i
+run 1000
+check 0 = root
+check 1 = F F:200000 F F F F F F
+check 2 = Di F:400000 F F F F F F
+# Link g priority increment
+bridge 1 = a b d f h g^112 e c
+run 1000
+check 0 = root
+check 1 = F F:200000 F F F F F F
+check 2 = F:400000 Di F F F F F F
+])
+AT_CHECK([test-rstp test-rstp-ieee802.1d-2004-fig17.7], [0], [])
+AT_CLEANUP
diff --git a/tests/test-rstp.c b/tests/test-rstp.c
new file mode 100644
index 0000000..e4377b5
--- /dev/null
+++ b/tests/test-rstp.c
@@ -0,0 +1,660 @@
+#include <config.h>
+
+#include "rstp.h"
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include "ofpbuf.h"
+#include "packets.h"
+#include "vlog.h"
+
+struct bpdu {
+ int port_no;
+ void *data;
+ size_t size;
+};
+
+struct bridge {
+ struct test_case *tc;
+ int id;
+ bool reached;
+
+ struct rstp *rstp;
+
+ struct lan *ports[RSTP_MAX_PORTS];
+ int n_ports;
+
+#define RXQ_SIZE 16
+ struct bpdu rxq[RXQ_SIZE];
+ int rxq_head, rxq_tail;
+};
+
+struct lan_conn {
+ struct bridge *bridge;
+ int port_no;
+};
+
+struct lan {
+ struct test_case *tc;
+ const char *name;
+ bool reached;
+ struct lan_conn conns[16];
+ int n_conns;
+};
+
+struct test_case {
+ struct bridge *bridges[16];
+ int n_bridges;
+ struct lan *lans[26];
+ int n_lans;
+};
+
+static const char *file_name;
+static int line_number;
+static char line[128];
+static char *pos, *token;
+static int n_warnings;
+
+static struct test_case *
+new_test_case(void)
+{
+ struct test_case *tc = xmalloc(sizeof *tc);
+ tc->n_bridges = 0;
+ tc->n_lans = 0;
+ return tc;
+}
+
+static void
+send_bpdu(struct ofpbuf *pkt, int port_no, void *b_)
+{
+ struct bridge *b = b_;
+ struct lan *lan;
+
+ assert(port_no < b->n_ports);
+ lan = b->ports[port_no];
+ if (lan) {
+ const void *data = ofpbuf_get_l3(pkt);
+ size_t size = (char *) ofpbuf_tail(pkt) - (char *) data;
+ int i;
+
+ for (i = 0; i < lan->n_conns; i++) {
+ struct lan_conn *conn = &lan->conns[i];
+ if (conn->bridge != b || conn->port_no != port_no) {
+ struct bridge *dst = conn->bridge;
+ struct bpdu *bpdu = &dst->rxq[dst->rxq_head++ % RXQ_SIZE];
+ assert(dst->rxq_head - dst->rxq_tail <= RXQ_SIZE);
+ bpdu->data = xmemdup(data, size);
+ bpdu->size = size;
+ bpdu->port_no = conn->port_no;
+ }
+ }
+ }
+ ofpbuf_delete(pkt);
+}
+
+static struct bridge *
+new_bridge(struct test_case *tc, int id)
+{
+ struct bridge *b = xmalloc(sizeof *b);
+ char name[16];
+ uint8_t identifier[6];
+ int i;
+ for (i=0;i<6;i++){
+ identifier[i] = id;
+ }
+ b->tc = tc;
+ b->id = id;
+ snprintf(name, sizeof name, "rstp%x", id);
+ b->rstp = rstp_create(name, identifier+2, send_bpdu, b);
+ assert(tc->n_bridges < ARRAY_SIZE(tc->bridges));
+ b->n_ports = 0;
+ b->rxq_head = b->rxq_tail = 0;
+ tc->bridges[tc->n_bridges++] = b;
+ return b;
+}
+
+static struct lan *
+new_lan(struct test_case *tc, const char *name)
+{
+ struct lan *lan = xmalloc(sizeof *lan);
+ lan->tc = tc;
+ lan->name = xstrdup(name);
+ lan->n_conns = 0;
+ assert(tc->n_lans < ARRAY_SIZE(tc->lans));
+ tc->lans[tc->n_lans++] = lan;
+ return lan;
+}
+
+static void
+reconnect_port(struct bridge *b, int port_no, struct lan *new_lan)
+{
+ struct lan *old_lan;
+ int j;
+
+ assert(port_no < b->n_ports);
+ old_lan = b->ports[port_no];
+ if (old_lan == new_lan) {
+ return;
+ }
+
+ /* Disconnect from old_lan. */
+ if (old_lan) {
+ for (j = 0; j < old_lan->n_conns; j++) {
+ struct lan_conn *c = &old_lan->conns[j];
+ if (c->bridge == b && c->port_no == port_no) {
+ memmove(c, c + 1, sizeof *c * (old_lan->n_conns - j - 1));
+ old_lan->n_conns--;
+ break;
+ }
+ }
+ }
+
+ /* Connect to new_lan. */
+ b->ports[port_no] = new_lan;
+ if (new_lan) {
+ int conn_no = new_lan->n_conns++;
+ assert(conn_no < ARRAY_SIZE(new_lan->conns));
+ new_lan->conns[conn_no].bridge = b;
+ new_lan->conns[conn_no].port_no = port_no;
+ }
+}
+
+static void
+new_port(struct bridge *b, struct lan *lan, uint32_t path_cost)
+{
+ int port_no = b->n_ports++;
+ struct rstp_port *p = rstp_get_port(b->rstp, port_no);
+ assert(port_no < ARRAY_SIZE(b->ports));
+ b->ports[port_no] = NULL;
+ rstp_port_set_path_cost(p, path_cost);
+ rstp_port_set_mac_operational(p, 1);
+ rstp_port_enable(p);
+ reconnect_port(b, port_no, lan);
+}
+
+static void
+dump(struct test_case *tc)
+{
+ int i;
+
+ for (i = 0; i < tc->n_bridges; i++) {
+ struct bridge *b = tc->bridges[i];
+ struct rstp *rstp = b->rstp;
+ int j;
+
+ printf("%s:", rstp_get_name(rstp));
+ if (rstp_is_root_bridge(rstp)) {
+ printf(" root");
+ }
+ printf("\n");
+ for (j = 0; j < b->n_ports; j++) {
+ struct rstp_port *p = rstp_get_port(rstp, j);
+ enum rstp_state state = rstp_port_get_state(p);
+
+ printf("\tport %d", j);
+ if (b->ports[j]) {
+ printf(" (lan %s)", b->ports[j]->name);
+ } else {
+ printf(" (disconnected)");
+ }
+ printf(": %s", rstp_state_name(state));
+ if (p == rstp_get_root_port(rstp)) {
+ printf(" (root port, root_path_cost=%s)",
+ get_id_string_from_uint8_t(rstp_get_root_path_cost(rstp),4));
+ }
+ printf("\n");
+ }
+ }
+}
+
+static void dump_lan_tree(struct test_case *, struct lan *, int level);
+
+static void
+dump_bridge_tree(struct test_case *tc, struct bridge *b, int level)
+{
+ int i;
+
+ if (b->reached) {
+ return;
+ }
+ b->reached = true;
+ for (i = 0; i < level; i++) {
+ printf("\t");
+ }
+ printf("%s\n", rstp_get_name(b->rstp));
+ for (i = 0; i < b->n_ports; i++) {
+ struct lan *lan = b->ports[i];
+ struct rstp_port *p = rstp_get_port(b->rstp, i);
+ if (rstp_port_get_state(p) == RSTP_FORWARDING && lan) {
+ dump_lan_tree(tc, lan, level + 1);
+ }
+ }
+}
+
+static void
+dump_lan_tree(struct test_case *tc, struct lan *lan, int level)
+{
+ int i;
+
+ if (lan->reached) {
+ return;
+ }
+ lan->reached = true;
+ for (i = 0; i < level; i++) {
+ printf("\t");
+ }
+ printf("%s\n", lan->name);
+ for (i = 0; i < lan->n_conns; i++) {
+ struct bridge *b = lan->conns[i].bridge;
+ dump_bridge_tree(tc, b, level + 1);
+ }
+}
+
+static void
+tree(struct test_case *tc)
+{
+ int i;
+
+ for (i = 0; i < tc->n_bridges; i++) {
+ struct bridge *b = tc->bridges[i];
+ b->reached = false;
+ }
+ for (i = 0; i < tc->n_lans; i++) {
+ struct lan *lan = tc->lans[i];
+ lan->reached = false;
+ }
+ for (i = 0; i < tc->n_bridges; i++) {
+ struct bridge *b = tc->bridges[i];
+ struct rstp *rstp = b->rstp;
+ if (rstp_is_root_bridge(rstp)) {
+ dump_bridge_tree(tc, b, 0);
+ }
+ }
+}
+
+static void
+simulate(struct test_case *tc, int granularity)
+{
+ int time, i, round_trips;
+ for (time = 0; time < 1000 * 180; time += granularity) {
+
+ for (i = 0; i < tc->n_bridges; i++) {
+ rstp_tick_timers(tc->bridges[i]->rstp);
+ }
+ for (round_trips = 0; round_trips < granularity; round_trips++) {
+ bool any = false;
+ for (i = 0; i < tc->n_bridges; i++) {
+ struct bridge *b = tc->bridges[i];
+ for (; b->rxq_tail != b->rxq_head; b->rxq_tail++) {
+ struct bpdu *bpdu = &b->rxq[b->rxq_tail % RXQ_SIZE];
+ rstp_received_bpdu(rstp_get_port(b->rstp, bpdu->port_no),
+ bpdu->data, bpdu->size);
+ free(bpdu->data);
+ any = true;
+ }
+ }
+ if (!any) {
+ break;
+ }
+ }
+ }
+}
+
+static void
+err(const char *message, ...)
+PRINTF_FORMAT(1, 2)
+ NO_RETURN;
+
+static void
+err(const char *message, ...)
+{
+ va_list args;
+
+ fprintf(stderr, "%s:%d:%"PRIdPTR": ", file_name, line_number, pos - line);
+ va_start(args, message);
+ vfprintf(stderr, message, args);
+ va_end(args);
+ putc('\n', stderr);
+
+ exit(EXIT_FAILURE);
+}
+
+static void
+warn(const char *message, ...)
+ PRINTF_FORMAT(1, 2);
+
+static void
+warn(const char *message, ...)
+{
+ va_list args;
+
+ fprintf(stderr, "%s:%d: ", file_name, line_number);
+ va_start(args, message);
+ vfprintf(stderr, message, args);
+ va_end(args);
+ putc('\n', stderr);
+
+ n_warnings++;
+}
+
+static bool
+get_token(void)
+{
+ char *start;
+
+ while (isspace((unsigned char) *pos)) {
+ pos++;
+ }
+ if (*pos == '\0') {
+ free(token);
+ token = NULL;
+ return false;
+ }
+
+ start = pos;
+ if (isalpha((unsigned char) *pos)) {
+ while (isalpha((unsigned char) *++pos)) {
+ continue;
+ }
+ } else if (isdigit((unsigned char) *pos)) {
+ if (*pos == '0' && (pos[1] == 'x' || pos[1] == 'X')) {
+ pos += 2;
+ while (isxdigit((unsigned char) *pos)) {
+ pos++;
+ }
+ } else {
+ while (isdigit((unsigned char) *++pos)) {
+ continue;
+ }
+ }
+ } else {
+ pos++;
+ }
+
+ free(token);
+ token = xmemdup0(start, pos - start);
+ return true;
+}
+
+static bool
+get_int(int *intp)
+{
+ char *save_pos = pos;
+ if (token && isdigit((unsigned char) *token)) {
+ *intp = strtol(token, NULL, 0);
+ get_token();
+ return true;
+ } else {
+ pos = save_pos;
+ return false;
+ }
+}
+
+static bool
+match(const char *want)
+{
+ if (token && !strcmp(want, token)) {
+ get_token();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static int
+must_get_int(void)
+{
+ int x;
+ if (!get_int(&x)) {
+ err("expected integer");
+ }
+ return x;
+}
+
+static void
+must_match(const char *want)
+{
+ if (!match(want)) {
+ err("expected \"%s\"", want);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct test_case *tc;
+ FILE *input_file;
+ int i;
+
+ vlog_set_pattern(VLF_CONSOLE, "%c|%p|%m");
+ vlog_set_levels(NULL, VLF_SYSLOG, VLL_OFF);
+
+ if (argc != 2) {
+ ovs_fatal(0, "usage: test-rstp INPUT.RSTP\n");
+ }
+ file_name = argv[1];
+
+ input_file = fopen(file_name, "r");
+ if (!input_file) {
+ ovs_fatal(errno, "error opening \"%s\"", file_name);
+ }
+
+ tc = new_test_case();
+ for (i = 0; i < 26; i++) {
+ char name[2];
+ name[0] = 'a' + i;
+ name[1] = '\0';
+ new_lan(tc, name);
+ }
+
+ for (line_number = 1; fgets(line, sizeof line, input_file);
+ line_number++)
+ {
+ char *newline, *hash;
+
+ newline = strchr(line, '\n');
+ if (newline) {
+ *newline = '\0';
+ }
+ hash = strchr(line, '#');
+ if (hash) {
+ *hash = '\0';
+ }
+
+ pos = line;
+ if (!get_token()) {
+ continue;
+ }
+ if (match("bridge")) {
+ struct bridge *bridge;
+ int bridge_no, port_no;
+
+ bridge_no = must_get_int();
+ if (bridge_no < tc->n_bridges) {
+ bridge = tc->bridges[bridge_no];
+ } else if (bridge_no == tc->n_bridges) {
+ bridge = new_bridge(tc, must_get_int());
+ } else {
+ err("bridges must be numbered consecutively from 0");
+ }
+ if (match("^")) {
+ rstp_set_bridge_priority(bridge->rstp, must_get_int());
+ }
+ if (match("=")) {
+ for (port_no = 0; port_no < RSTP_MAX_PORTS; port_no++) {
+ struct rstp_port *p = rstp_get_port(bridge->rstp, port_no);
+ if (!token || match("X")) {
+ rstp_port_disable(p);
+ } else if (match("_")) {
+ /* Nothing to do. */
+ } else {
+ struct lan *lan;
+ uint32_t path_cost;
+
+ if (!strcmp(token, "0")) {
+ lan = NULL;
+ } else if (strlen(token) == 1
+ && islower((unsigned char)*token)) {
+ lan = tc->lans[*token - 'a'];
+ } else {
+ err("%s is not a valid LAN name "
+ "(0 or a lowercase letter)", token);
+ }
+ get_token();
+
+ path_cost = match(":") ? must_get_int() : RSTP_DEFAULT_PORT_PATH_COST;
+ if (port_no < bridge->n_ports) {
+ rstp_port_set_path_cost(p, path_cost);
+ rstp_port_set_mac_operational(p, 1);
+ rstp_port_enable(p);
+ reconnect_port(bridge, port_no, lan);
+ } else if (port_no == bridge->n_ports) {
+ new_port(bridge, lan, path_cost);
+ } else {
+ err("ports must be numbered consecutively");
+ }
+ if (match("^")) {
+ rstp_port_set_priority(p, must_get_int());
+ }
+ }
+ }
+ }
+ } else if (match("run")) {
+ simulate(tc, must_get_int());
+ } else if (match("dump")) {
+ dump(tc);
+ } else if (match("tree")) {
+ tree(tc);
+ } else if (match("check")) {
+ struct bridge *b;
+ struct rstp *rstp;
+ int bridge_no, port_no;
+ uint32_t cost_value, temp;
+
+ bridge_no = must_get_int();
+ if (bridge_no >= tc->n_bridges) {
+ err("no bridge numbered %d", bridge_no);
+ }
+ b = tc->bridges[bridge_no];
+ rstp = b->rstp;
+
+ must_match("=");
+
+ if (match("rootid")) {
+ uint64_t rootid;
+ must_match(":");
+ rootid = must_get_int();
+ if (match("^")) {
+ rootid |= (uint64_t) must_get_int() << 48;
+ } else {
+ rootid |= UINT64_C(0x8000) << 48;
+ }
+ if (memcmp(rstp_get_designated_root(rstp),&rootid,8)!=0) {
+ warn("%s: root %s, not %"PRIx64,
+ rstp_get_name(rstp),
+ get_id_string_from_uint8_t(rstp_get_designated_root(rstp),8),
+ rootid);
+ }
+ }
+ memcpy(&temp, rstp_get_root_path_cost(rstp), sizeof(uint32_t));
+ cost_value = ntohl(temp);
+ if (match("root")) {
+ if (cost_value != 0) {
+ warn("%s: root path cost of root is %d instead of 0 \n",
+ rstp_get_name(rstp), cost_value);
+ }
+ if (!rstp_is_root_bridge(rstp)) {
+ warn("%s: root is %s, not %s",
+ rstp_get_name(rstp),
+ get_id_string_from_uint8_t(rstp_get_designated_root(rstp),8),
+ get_id_string_from_uint8_t(rstp_get_bridge_id(rstp),8));
+ }
+ for (port_no = 0; port_no < b->n_ports; port_no++) {
+ struct rstp_port *p = rstp_get_port(rstp, port_no);
+ enum rstp_state state = rstp_port_get_state(p);
+ if (!(state & (RSTP_DISABLED | RSTP_FORWARDING))) {
+ warn("%s: root port %d in state %s",
+ rstp_get_name(b->rstp), port_no,
+ rstp_state_name(state));
+ }
+ }
+ } else {
+ for (port_no = 0; port_no < RSTP_MAX_PORTS; port_no++) {
+ struct rstp_port *p = rstp_get_port(rstp, port_no);
+ enum rstp_state state;
+ if (token == NULL || match("D")) {
+ state = RSTP_DISABLED;
+ } else if (match("Di")) {
+ state = RSTP_DISCARDING;
+ } else if (match("Le")) {
+ state = RSTP_LEARNING;
+ } else if (match("F")) {
+ state = RSTP_FORWARDING;
+ } else if (match("_")) {
+ continue;
+ } else {
+ err("unknown port state %s", token);
+ }
+ if (rstp_port_get_state(p) != state) {
+ warn("%s port %d: state is %s but should be %s",
+ rstp_get_name(rstp), port_no,
+ rstp_state_name(rstp_port_get_state(p)),
+ rstp_state_name(state));
+ }
+ if (state == RSTP_FORWARDING) {
+ struct rstp_port *root_port = rstp_get_root_port(rstp);
+ if (match(":")) {
+ int root_path_cost = must_get_int();
+ if (p != root_port) {
+ warn("%s: port %d is not the root port",
+ rstp_get_name(rstp), port_no);
+ if (!root_port) {
+ warn("%s: (there is no root port)",
+ rstp_get_name(rstp));
+ } else {
+ warn("%s: (port %d is the root port)",
+ rstp_get_name(rstp),
+ rstp_port_no(root_port));
+ }
+ } else if (cost_value != root_path_cost) {
+ warn("%s: root path cost is %d, should be %d",
+ rstp_get_name(rstp),
+ cost_value,
+ root_path_cost);
+ }
+ } else if (p == root_port) {
+ warn("%s: port %d is the root port but "
+ "not expected to be",
+ rstp_get_name(rstp), port_no);
+ }
+ }
+ }
+ }
+ if (n_warnings) {
+ printf("failing because of %d warnings\n", n_warnings);
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (get_token()) {
+ printf("failing because of errors\n");
+ err("trailing garbage on line");
+ }
+ }
+ free(token);
+
+ for (i = 0; i < tc->n_lans; i++) {
+ struct lan *lan = tc->lans[i];
+ free(CONST_CAST(char *, lan->name));
+ free(lan);
+ }
+ for (i = 0; i < tc->n_bridges; i++) {
+ struct bridge *bridge = tc->bridges[i];
+ rstp_unref(bridge->rstp);
+ free(bridge);
+ }
+ free(tc);
+ return 0;
+}
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 772a7eb..be95f5f 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -106,5 +106,6 @@ m4_include([tests/ovs-monitor-ipsec.at])
m4_include([tests/ovs-xapi-sync.at])
m4_include([tests/interface-reconfigure.at])
m4_include([tests/stp.at])
+m4_include([tests/rstp.at])
m4_include([tests/vlog.at])
m4_include([tests/vtep-ctl.at])
diff --git a/utilities/ovs-vsctl.8.in b/utilities/ovs-vsctl.8.in
index 43b00bf..bbc7285 100644
--- a/utilities/ovs-vsctl.8.in
+++ b/utilities/ovs-vsctl.8.in
@@ -968,6 +968,85 @@ Deconfigure STP from above:
.IP
.B "ovs\-vsctl set Bridge br0 stp_enable=false"
.PP
+.SS "802.1D-2004 Rapid Spanning Tree Protocol (RSTP)"
+.PP
+Configure bridge \fBbr0\fR to participate in an 802.1D-2004 Rapid Spanning Tree:
+.IP
+.B "ovs\-vsctl set Bridge br0 rstp_enable=true"
+.PP
+Set the bridge address of \fBbr0\fR to 00:aa:aa:aa:aa:aa :
+.IP
+.B "ovs\-vsctl set Bridge br0 other_config:rstp-address=00:aa:aa:aa:aa:aa"
+.PP
+Set the bridge priority of \fBbr0\fR to 0x7000. The value must be specified in
+decimal notation and should be a multiple of 4096 (if not, it is rounded down to
+the nearest multiple of 4096). The default priority value is 0x800 (32768).
+.IP
+.B "ovs\-vsctl set Bridge br0 other_config:rstp-priority=28672"
+.PP
+Set the bridge ageing time of \fBbr0\fR to 1000 s. The ageing time value should be
+between 10 s and 1000000 s. The default value is 300 s.
+.IP
+.B "ovs\-vsctl set Bridge br0 other_config:rstp-ageing-time=1000"
+.PP
+Set the bridge force protocol version of \fBbr0\fR to 0. The force protocol version
+has two acceptable values: 0 (STP compatibility mode) and 2 (normal operation).
+.IP
+.B "ovs\-vsctl set Bridge br0 other_config:rstp-force-protocol-version=0"
+.PP
+Set the bridge max age of \fBbr0\fR to 10 s. The max age value should be between 6 s
+and 40 s. The default value is 20 s.
+.IP
+.B "ovs\-vsctl set Bridge br0 other_config:rstp-max-age=10"
+.PP
+Set the bridge forward delay of \fBbr0\fR to 15 s.
+This value should be between 4 s and 30 s. The default value is 15 s.
+.IP
+.B "ovs\-vsctl set Bridge br0 other_config:rstp-forward-delay=15"
+.PP
+Set the bridge transmit hold count of \fBbr0\fR to 7 s. This value should be between
+1 s and 10 s. The default value is 6 s.
+.IP
+.B "ovs\-vsctl set Bridge br0 other_config:rstp-transmit-hold-count=7"
+.PP
+Enable RSTP on the Port \fBeth0\fR.
+.IP
+.B "ovs\-vsctl set Port eth0 other_config:rstp-enable=true"
+.PP
+Disable RSTP on the Port \fBeth0\fR.
+.IP
+.B "ovs\-vsctl set Port eth0 other_config:rstp-enable=false"
+.PP
+Set the priority of port \fBeth0\fR to 20. The value must be specified in
+decimal notation and should be a multiple of 16 (if not, it is rounded down to the
+nearest multiple of 16). The default priority value is 0x80 (128).
+.IP
+.B "ovs\-vsctl set Port eth0 other_config:rstp-port-priority=32"
+.PP
+Set the port number of port \fBeth0\fR to 3:
+.IP
+.B "ovs\-vsctl set Port eth0 other_config:rstp-port-num=3"
+.PP
+Set the path cost of port \fBeth0\fR to 150:
+.IP
+.B "ovs\-vsctl set Port eth0 other_config:rstp-path-cost=150"
+.PP
+Set the admin edge value of port \fBeth0\fR:
+.IP
+.B "ovs\-vsctl set Port eth0 other_config:rstp-port-admin-edge=true"
+.PP
+Set the auto edge value of port \fBeth0\fR:
+.IP
+.B "ovs\-vsctl set Port eth0 other_config:rstp-port-auto-edge=true"
+.PP
+Set the mcheck value of port \fBeth0\fR:
+.IP
+.B "ovs\-vsctl set Port eth0 other_config:rstp-port-mcheck=true"
+.PP
+Deconfigure RSTP from above:
+.IP
+.B "ovs\-vsctl set Bridge br0 rstp_enable=false"
+.PP
.SS "OpenFlow Version"
.PP
Configure bridge \fBbr0\fR to support OpenFlow versions 1.0, 1.2, and
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index db85856..d7f568e 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -206,6 +206,7 @@ static void bridge_configure_mac_table(struct bridge *);
static void bridge_configure_sflow(struct bridge *, int *sflow_bridge_number);
static void bridge_configure_ipfix(struct bridge *);
static void bridge_configure_stp(struct bridge *);
+static void bridge_configure_rstp(struct bridge *);
static void bridge_configure_tables(struct bridge *);
static void bridge_configure_dp_desc(struct bridge *);
static void bridge_configure_remotes(struct bridge *,
@@ -354,9 +355,14 @@ bridge_init(const char *remote)
ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_datapath_id);
ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_status);
+ ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_rstp_status);
+ ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_stp_enable);
+ ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_rstp_enable);
ovsdb_idl_omit(idl, &ovsrec_bridge_col_external_ids);
ovsdb_idl_omit_alert(idl, &ovsrec_port_col_status);
+ ovsdb_idl_omit_alert(idl, &ovsrec_port_col_rstp_status);
+ ovsdb_idl_omit_alert(idl, &ovsrec_port_col_rstp_statistics);
ovsdb_idl_omit_alert(idl, &ovsrec_port_col_statistics);
ovsdb_idl_omit(idl, &ovsrec_port_col_external_ids);
@@ -417,6 +423,7 @@ bridge_init(const char *remote)
bond_init();
cfm_init();
stp_init();
+ rstp_init();
}
void
@@ -602,6 +609,7 @@ bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
bridge_configure_sflow(br, &sflow_bridge_number);
bridge_configure_ipfix(br);
bridge_configure_stp(br);
+ bridge_configure_rstp(br);
bridge_configure_tables(br);
bridge_configure_dp_desc(br);
@@ -1241,12 +1249,126 @@ port_configure_stp(const struct ofproto *ofproto, struct port *port,
}
}
+static void
+port_configure_rstp(const struct ofproto *ofproto, struct port *port,
+ struct ofproto_port_rstp_settings *port_s,
+ int *port_num_counter, unsigned long *port_num_bitmap)
+{
+ const char *config_str;
+ struct iface *iface;
+ int temp_num, done;
+
+ if (!smap_get_bool(&port->cfg->other_config, "rstp-enable", true)) {
+ port_s->enable = false;
+ return;
+ } else {
+ port_s->enable = true;
+ }
+
+ /* RSTP over bonds is not supported. */
+ if (!list_is_singleton(&port->ifaces)) {
+ VLOG_ERR("port %s: cannot enable RSTP on bonds, disabling",
+ port->name);
+ port_s->enable = false;
+ return;
+ }
+
+ iface = CONTAINER_OF(list_front(&port->ifaces), struct iface, port_elem);
+
+ /* Internal ports shouldn't participate in spanning tree, so
+ * skip them. */
+ if (!strcmp(iface->type, "internal")) {
+ VLOG_DBG("port %s: disable RSTP on internal ports", port->name);
+ port_s->enable = false;
+ return;
+ }
+
+ /* RSTP on mirror output ports is not supported. */
+ if (ofproto_is_mirror_output_bundle(ofproto, port)) {
+ VLOG_DBG("port %s: disable RSTP on mirror ports", port->name);
+ port_s->enable = false;
+ return;
+ }
+
+ config_str = smap_get(&port->cfg->other_config, "rstp-port-num");
+ if (config_str) {
+ unsigned long int port_num = strtoul(config_str, NULL, 0);
+ if (port_num < 1 || port_num > RSTP_MAX_PORTS) {
+ VLOG_ERR("port %s: invalid rstp-port-num", port->name);
+ port_s->enable = false;
+ return;
+ }
+ if (bitmap_is_set(port_num_bitmap, port_num)) {
+ VLOG_ERR("port %s: duplicate rstp-port-num %lu, disabling",
+ port->name, port_num);
+ port_s->enable = false;
+ return;
+ }
+ bitmap_set1(port_num_bitmap, port_num);
+ port_s->port_num = port_num;
+ }
+ else {
+ if (*port_num_counter >= RSTP_MAX_PORTS) {
+ VLOG_ERR("port %s: too many RSTP ports, disabling", port->name);
+ port_s->enable = false;
+ return;
+ }
+ /* If rstp-port-num is not specified, look for the first free one. */
+ done = 0;
+ for (temp_num = 1; temp_num <= RSTP_MAX_PORTS; temp_num++) {
+ if (!bitmap_is_set(port_num_bitmap, temp_num) && !done) {
+ bitmap_set1(port_num_bitmap, temp_num);
+ port_s->port_num = temp_num;
+ done = 1;
+ }
+ }
+ if (done == 0) {
+ VLOG_ERR("port %s: no rstp-port-num available", port->name);
+ port_s->enable = false;
+ return;
+ }
+ }
+
+ config_str = smap_get(&port->cfg->other_config, "rstp-path-cost");
+ if (config_str) {
+ port_s->path_cost = strtoul(config_str, NULL, 10);
+ } else {
+ enum netdev_features current;
+ unsigned int mbps;
+
+ netdev_get_features(iface->netdev, ¤t, NULL, NULL, NULL);
+ mbps = netdev_features_to_bps(current, 100 * 1000 * 1000) / 1000000;
+ port_s->path_cost = rstp_convert_speed_to_cost(mbps);
+ }
+
+ config_str = smap_get(&port->cfg->other_config, "rstp-port-priority");
+ if (config_str) {
+ port_s->priority = strtoul(config_str, NULL, 0);
+ } else {
+ port_s->priority = RSTP_DEFAULT_PORT_PRIORITY;
+ }
+
+ port_s->admin_edge_port = smap_get_bool(&port->cfg->other_config,
+ "rstp-port-admin-edge", false);
+ port_s->auto_edge = smap_get_bool(&port->cfg->other_config,
+ "rstp-port-auto-edge", true);
+ port_s->mcheck = smap_get_bool(&port->cfg->other_config,
+ "rstp-port-mcheck", false);
+}
+
/* Set spanning tree configuration on 'br'. */
static void
bridge_configure_stp(struct bridge *br)
{
+ struct ofproto_rstp_status rstp_status;
+ ofproto_get_rstp_status(br->ofproto, &rstp_status);
if (!br->cfg->stp_enable) {
ofproto_set_stp(br->ofproto, NULL);
+ } else if (rstp_status.enabled) {
+ /* Do not activate STP if RSTP is enabled. */
+ VLOG_ERR("STP cannot be enabled if RSTP is running.");
+ ofproto_set_stp(br->ofproto, NULL);
+ ovsrec_bridge_set_stp_enable(br->cfg, false);
} else {
struct ofproto_stp_settings br_s;
const char *config_str;
@@ -1336,6 +1458,114 @@ bridge_configure_stp(struct bridge *br)
}
}
+static void
+bridge_configure_rstp(struct bridge *br)
+{
+ struct ofproto_stp_status stp_status;
+ ofproto_get_stp_status(br->ofproto, &stp_status);
+ if (!br->cfg->rstp_enable) {
+ ofproto_set_rstp(br->ofproto, NULL);
+ } else if (stp_status.enabled) {
+ /* Do not activate RSTP if STP is enabled. */
+ VLOG_ERR("RSTP cannot be enabled if STP is running.");
+ ofproto_set_rstp(br->ofproto, NULL);
+ ovsrec_bridge_set_rstp_enable(br->cfg, false);
+ } else {
+ struct ofproto_rstp_settings br_s;
+ const char *config_str;
+ struct port *port;
+ int port_num_counter;
+ unsigned long *port_num_bitmap;
+
+ config_str = smap_get(&br->cfg->other_config, "rstp-address");
+ if (config_str) {
+ uint8_t ea[ETH_ADDR_LEN];
+
+ if (eth_addr_from_string(config_str, ea)) {
+ memcpy(&br_s.address, &ea, ETH_ADDR_LEN);
+ }
+ else {
+ memcpy(&br_s.address, br->ea, ETH_ADDR_LEN);
+ VLOG_ERR("bridge %s: invalid rstp-address, defaulting "
+ "to "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(br->ea));
+ }
+ }
+ else {
+ memcpy(&br_s.address, br->ea, ETH_ADDR_LEN);
+ }
+
+ config_str = smap_get(&br->cfg->other_config, "rstp-priority");
+ if (config_str) {
+ br_s.priority = strtoul(config_str, NULL, 0);
+ } else {
+ br_s.priority = RSTP_DEFAULT_PRIORITY;
+ }
+
+ config_str = smap_get(&br->cfg->other_config, "rstp-ageing-time");
+ if (config_str) {
+ br_s.ageing_time = strtoul(config_str, NULL, 0);
+ } else {
+ br_s.ageing_time = RSTP_DEFAULT_AGEING_TIME;
+ }
+
+ config_str = smap_get(&br->cfg->other_config,
+ "rstp-force-protocol-version");
+ if (config_str) {
+ br_s.force_protocol_version = strtoul(config_str, NULL, 0);
+ } else {
+ br_s.force_protocol_version = FPV_DEFAULT;
+ }
+
+ config_str = smap_get(&br->cfg->other_config, "rstp-max-age");
+ if (config_str) {
+ br_s.bridge_max_age = strtoul(config_str, NULL, 10);
+ } else {
+ br_s.bridge_max_age = RSTP_DEFAULT_BRIDGE_MAX_AGE;
+ }
+
+ config_str = smap_get(&br->cfg->other_config, "rstp-forward-delay");
+ if (config_str) {
+ br_s.bridge_forward_delay = strtoul(config_str, NULL, 10);
+ } else {
+ br_s.bridge_forward_delay = RSTP_DEFAULT_BRIDGE_FORWARD_DELAY;
+ }
+
+ config_str = smap_get(&br->cfg->other_config,
+ "rstp-transmit-hold-count");
+ if (config_str) {
+ br_s.transmit_hold_count = strtoul(config_str, NULL, 10);
+ } else {
+ br_s.transmit_hold_count = RSTP_DEFAULT_TRANSMIT_HOLD_COUNT;
+ }
+
+ /* Configure RSTP on the bridge. */
+ if (ofproto_set_rstp(br->ofproto, &br_s)) {
+ VLOG_ERR("bridge %s: could not enable RSTP", br->name);
+ return;
+ }
+
+ port_num_counter = 0;
+ port_num_bitmap = bitmap_allocate(RSTP_MAX_PORTS);
+ HMAP_FOR_EACH (port, hmap_node, &br->ports) {
+ struct ofproto_port_rstp_settings port_s;
+ struct iface *iface;
+
+ port_configure_rstp(br->ofproto, port, &port_s,
+ &port_num_counter, port_num_bitmap);
+
+ /* As bonds are not supported, just apply configuration to
+ * all interfaces. */
+ LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
+ if (ofproto_port_set_rstp(br->ofproto, iface->ofp_port,
+ &port_s)) {
+ VLOG_ERR("port %s: could not enable RSTP", port->name);
+ continue;
+ }
+ }
+ }
+ }
+}
+
static bool
bridge_has_bond_fake_iface(const struct bridge *br, const char *name)
{
@@ -2039,6 +2269,98 @@ port_refresh_stp_stats(struct port *port)
ARRAY_SIZE(int_values));
}
+static void
+br_refresh_rstp_status(struct bridge *br)
+{
+ char *temp;
+ struct smap smap = SMAP_INITIALIZER(&smap);
+ struct ofproto *ofproto = br->ofproto;
+ struct ofproto_rstp_status status;
+
+ if (ofproto_get_rstp_status(ofproto, &status)) {
+ return;
+ }
+ if (!status.enabled) {
+ ovsrec_bridge_set_rstp_status(br->cfg, NULL);
+ return;
+ }
+ temp = get_id_string_from_uint8_t(status.bridge_id, 8);
+ smap_add_format(&smap, "rstp_bridge_id", "%s", temp);
+
+ temp = get_id_string_from_uint8_t(status.root_path_cost, 4);
+ smap_add_format(&smap, "rstp_root_path_cost", "%s", temp);
+
+ temp = get_id_string_from_uint8_t(status.root_id, 8);
+ smap_add_format(&smap, "rstp_root_id", "%s", temp);
+
+ temp = get_id_string_from_uint8_t(status.designated_id, 8);
+ smap_add_format(&smap, "rstp_designated_id", "%s", temp);
+
+ temp = get_id_string_from_uint8_t(status.designated_port_id, 2);
+ smap_add_format(&smap, "rstp_designated_port_id", "%s", temp);
+
+ temp = get_id_string_from_uint8_t(status.bridge_port_id, 2);
+ smap_add_format(&smap, "rstp_bridge_port_id", "%s", temp);
+
+ ovsrec_bridge_set_rstp_status(br->cfg, &smap);
+ smap_destroy(&smap);
+}
+
+static void
+port_refresh_rstp_status(struct port *port)
+{
+ struct ofproto *ofproto = port->bridge->ofproto;
+ struct iface *iface;
+ struct ofproto_port_rstp_status status;
+ char *keys[3];
+ int64_t int_values[3];
+ struct smap smap;
+ char *temp;
+
+ if (port_is_synthetic(port)) {
+ return;
+ }
+
+ /* RSTP doesn't currently support bonds. */
+ if (!list_is_singleton(&port->ifaces)) {
+ ovsrec_port_set_rstp_status(port->cfg, NULL);
+ return;
+ }
+
+ iface = CONTAINER_OF(list_front(&port->ifaces), struct iface, port_elem);
+ if (ofproto_port_get_rstp_status(ofproto, iface->ofp_port, &status)) {
+ return;
+ }
+
+ if (!status.enabled) {
+ ovsrec_port_set_rstp_status(port->cfg, NULL);
+ ovsrec_port_set_rstp_statistics(port->cfg, NULL, NULL, 0);
+ return;
+ }
+ /* Set Status column. */
+ smap_init(&smap);
+
+ temp = get_id_string_from_uint8_t(status.port_id, 2);
+ smap_add_format(&smap, "rstp_port_id", "%s", temp);
+ temp = rstp_port_role_name(status.role);
+ smap_add_format(&smap, "rstp_port_role", "%s", temp);
+ temp = rstp_state_name(status.state);
+ smap_add_format(&smap, "rstp_port_state", "%s", temp);
+
+ ovsrec_port_set_rstp_status(port->cfg, &smap);
+ smap_destroy(&smap);
+
+ /* Set Statistics column. */
+ keys[0] = "rstp_tx_count";
+ int_values[0] = status.tx_count;
+ keys[1] = "rstp_rx_count";
+ int_values[1] = status.rx_count;
+ keys[2] = "rstp_uptime";
+ int_values[2] = status.uptime;
+ ovsrec_port_set_rstp_statistics(port->cfg, keys, int_values,
+ ARRAY_SIZE(int_values));
+}
+
static bool
enable_system_stats(const struct ovsrec_open_vswitch *cfg)
{
@@ -2204,9 +2526,11 @@ instant_stats_run(void)
struct port *port;
br_refresh_stp_status(br);
+ br_refresh_rstp_status(br);
HMAP_FOR_EACH (port, hmap_node, &br->ports) {
port_refresh_stp_status(port);
+ port_refresh_rstp_status(port);
}
HMAP_FOR_EACH (iface, name_node, &br->iface_by_name) {
diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema
index efaa1da..813039f 100644
--- a/vswitchd/vswitch.ovsschema
+++ b/vswitchd/vswitch.ovsschema
@@ -1,6 +1,6 @@
{"name": "Open_vSwitch",
"version": "7.4.0",
- "cksum": "2387737815 20431",
+ "cksum": "2392405263 20907",
"tables": {
"Open_vSwitch": {
"columns": {
@@ -54,6 +54,8 @@
"ephemeral": true},
"stp_enable": {
"type": "boolean"},
+ "rstp_enable": {
+ "type": "boolean"},
"ports": {
"type": {"key": {"type": "uuid",
"refTable": "Port"},
@@ -93,6 +95,9 @@
"status": {
"type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"},
"ephemeral": true},
+ "rstp_status": {
+ "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"},
+ "ephemeral": true},
"other_config": {
"type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}},
"external_ids": {
@@ -159,6 +164,12 @@
"status": {
"type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"},
"ephemeral": true},
+ "rstp_status": {
+ "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"},
+ "ephemeral": true},
+ "rstp_statistics": {
+ "type": {"key": "string", "value": "integer", "min": 0, "max": "unlimited"},
+ "ephemeral": true},
"statistics": {
"type": {"key": "string", "value": "integer", "min": 0, "max": "unlimited"},
"ephemeral": true},
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index b0a8577..9639f0a 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -633,6 +633,107 @@
</column>
</group>
+ <group title="Rapid Spanning Tree Configuration">
+ In IEEE Std 802.1D, 1998 Edition, and prior editions of this standard,
+ Clause 8 specified the spanning tree algorithm and protocol (STP).9 STP
+ has now been superseded by the Rapid Spanning Tree Protocol (RSTP)
+ specified in Clause 17 of the IEEE Std 802.1D, 2004 Edition.
+ The IEEE 802.1D-2004 Rapid Spanning Tree Algorithm Protocol configures
+ full, simple, and symmetric connectivity throughout a Bridged Local Area
+ Network that comprises individual LANs interconnected by Bridges.
+ Like STP, RSTP is a network protocol that ensures loop-free topologies.
+ It allows redundant links to be included in the network to provide
+ automatic backup paths if the active links fails.
+
+ <column name="rstp_enable">
+ Enable Rapid Spanning Tree on the bridge. By default, RSTP is disabled
+ on bridges. Bond, internal, and mirror ports are not supported
+ and will not participate in the spanning tree.
+ </column>
+
+ <column name="other_config" key="rstp-address">
+ The bridge's RSTP address (the lower 48 bits of the bridge-id)
+ in the form
+ <var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>.
+ By default, the address is the MAC address of the bridge.
+ </column>
+
+ <column name="other_config" key="rstp-priority"
+ type='{"type": "integer", "minInteger": 0, "maxInteger": 61440}'>
+ The bridge's relative priority value for determining the root
+ bridge (the upper 16 bits of the bridge-id). A bridge with the
+ lowest bridge-id is elected the root. By default, the priority
+ is 0x8000 (32768). This value needs to be a multiple of 4096, otherwise
+ it's rounded to the nearest inferior one.
+ </column>
+
+ <column name="other_config" key="rstp-ageing-time"
+ type='{"type": "integer", "minInteger": 10, "maxInteger": 1000000}'>
+ The Ageing Time parameter for the Bridge. The default value
+ is 300.
+ </column>
+
+ <column name="other_config" key="rstp-force-protocol-version">
+ The Force Protocol Version parameter for the Bridge. This
+ can take the value 0 (.STP Compatibility. mode) or 2
+ (the default, normal operation).
+ </column>
+
+ <column name="other_config" key="rstp-max-age"
+ type='{"type": "integer", "minInteger": 6, "maxInteger": 40}'>
+ The maximum age of the information transmitted by the Bridge
+ when it is the Root Bridge. The default value is 20.
+ </column>
+
+ <column name="other_config" key="rstp-forward-delay"
+ type='{"type": "integer", "minInteger": 4, "maxInteger": 30}'>
+ The delay used by STP Bridges to transition Root and Designated
+ Ports to Forwarding. The default value is 15.
+ </column>
+
+ <column name="other_config" key="rstp-transmit-hold-count"
+ type='{"type": "integer", "minInteger": 1, "maxInteger": 10}'>
+ The Transmit Hold Count used by the Port Transmit state machine
+ to limit transmission rate. The default value is 6.
+ </column>
+
+ <column name="other_config" key="rstp-enable">
+ The RSTP enable parameter of the Port.
+ </column>
+
+ <column name="other_config" key="rstp-port-priority"
+ type='{"type": "integer", "minInteger": 0, "maxInteger": 240}'>
+ The port's relative priority value for determining the root
+ port (the upper 8 bits of the port-id). A port with the lowest
+ port-id is elected the root.
+ By default, the port priority is 0x80 (128). This value needs
+ to be a multiple of 16, otherwise it's rounded to the nearest
+ inferior one.
+ </column>
+
+ <column name="other_config" key="rstp-port-num">
+ The port's relative id for determining the root port
+ (the lower 8 bits of the port-id). A port with the lowest
+ port-id is elected the root.
+ </column>
+
+ <column name="other_config" key="rstp-port-path-cost">
+ The port path cost. The Port.s contribution, when it is
+ the Root Port, to the Root Path Cost for the Bridge.
+ </column>
+
+ <column name="other_config" key="rstp-port-admin-edge">
+ The admin edge port parameter for the Port.
+ </column>
+
+ <column name="other_config" key="rstp-port-auto-edge">
+ The auto edge port parameter for the Port.
+ </column>
+ <column name="other_config" key="rstp-port-mcheck">
+ The mcheck port parameter for the Port.
+ </column>
+ </group>
+
<group title="Other Features">
<column name="datapath_type">
Name of datapath provider. The kernel datapath has
--
1.8.1.2
More information about the dev
mailing list