[ovs-dev] [PATCH v6 01/18] Rapid Spanning Tree Protocol (IEEE 802.1D).
Daniele Venturino
daniele.venturino at m3s.it
Tue Sep 9 10:25:56 UTC 2014
Acked-by: Daniele Venturino <daniele.venturino at m3s.it>
2014-08-21 1:57 GMT+02:00 Jarno Rajahalme <jrajahalme at nicira.com>:
> From: Daniele Venturino <daniele.venturino at m3s.it>
>
> This is the v5 from June 12th, 2014, rebased to OVS master, further
> changes in following patches.
>
> Signed-off by: Daniele Venturino <daniele.venturino at m3s.it>
> Signed-off by: Martino Fornasa <mf at fornasa.it>
> Signed-off-by: Jarno Rajahalme <jrajahalme at nicira.com>
> ---
> AUTHORS | 1 +
> NOTICE | 3 +
> lib/automake.mk | 5 +
> lib/packets.h | 5 +
> lib/rstp-common.h | 884 ++++++++++++++++++
> lib/rstp-state-machines.c | 2036
> ++++++++++++++++++++++++++++++++++++++++++
> lib/rstp-state-machines.h | 42 +
> lib/rstp.c | 1236 +++++++++++++++++++++++++
> lib/rstp.h | 199 +++++
> lib/stp.h | 5 -
> ofproto/ofproto-dpif-xlate.c | 158 +++-
> ofproto/ofproto-dpif-xlate.h | 5 +-
> ofproto/ofproto-dpif.c | 286 +++++-
> ofproto/ofproto-provider.h | 47 +
> ofproto/ofproto.c | 84 ++
> ofproto/ofproto.h | 50 ++
> tests/.gitignore | 1 +
> tests/automake.mk | 3 +
> tests/ovs-vsctl.at | 4 +
> tests/rstp.at | 149 ++++
> tests/test-rstp.c | 684 ++++++++++++++
> tests/testsuite.at | 1 +
> utilities/ovs-vsctl.8.in | 79 ++
> vswitchd/bridge.c | 295 ++++++
> vswitchd/vswitch.ovsschema | 15 +-
> vswitchd/vswitch.xml | 101 +++
> 26 files changed, 6325 insertions(+), 53 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 cfbb03d..2dd27d7 100644
> --- a/AUTHORS
> +++ b/AUTHORS
> @@ -92,6 +92,7 @@ Luigi Rizzo rizzo at iet.unipi.it
> Mark Hamilton mhamilton at nicira.com
> Mark Maglana mmaglana at gmail.com
> Martin Casado casado at nicira.com
> +Martino Fornasa mf at fornasa.it
> Maryam Tahhan maryam.tahhan at intel.com
> Mehak Mahajan mmahajan at nicira.com
> Murphy McCauley murphy.mccauley at gmail.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 d46613f..740a3de 100644
> --- a/lib/automake.mk
> +++ b/lib/automake.mk
> @@ -188,6 +188,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/packets.h b/lib/packets.h
> index 0258745..69012a9 100644
> --- a/lib/packets.h
> +++ b/lib/packets.h
> @@ -282,6 +282,11 @@ struct llc_header {
> });
> BUILD_ASSERT_DECL(LLC_HEADER_LEN == sizeof(struct llc_header));
>
> +/* LLC field values used for STP frames. */
> +#define STP_LLC_SSAP 0x42
> +#define STP_LLC_DSAP 0x42
> +#define STP_LLC_CNTL 0x03
> +
> #define SNAP_ORG_ETHERNET "\0\0" /* The compiler adds a null byte, so
> sizeof(SNAP_ORG_ETHERNET) == 3. */
> #define SNAP_HEADER_LEN 5
> diff --git a/lib/rstp-common.h b/lib/rstp-common.h
> new file mode 100644
> index 0000000..6bc04eb
> --- /dev/null
> +++ b/lib/rstp-common.h
> @@ -0,0 +1,884 @@
> +/*
> + * 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 "rstp.h"
> +#include <stdbool.h>
> +#include <stdint.h>
> +#include "list.h"
> +#include "ovs-atomic.h"
> +#include "packets.h"
> +
> +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 (expressed in seconds)
> 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
> + */
> + uint16_t 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
> + */
> + uint16_t 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 */
> + uint16_t max_age;
> +
> + uint16_t message_age;
> +};
> +
> +/* Priority vector [17.6] */
> +struct rstp_priority_vector {
> + rstp_identifier root_bridge_id;
> + uint32_t root_path_cost;
> + rstp_identifier designated_bridge_id;
> + uint16_t designated_port_id;
> + uint16_t bridge_port_id;
> +};
> +
> +struct rstp_priority_vector4 {
> + rstp_identifier root_bridge_id;
> + uint32_t root_path_cost;
> + rstp_identifier designated_bridge_id;
> + uint16_t designated_port_id;
> +};
> +
> +enum rstp_bpdu_type {
> + CONFIGURATION_BPDU = 0x0,
> + TOPOLOGY_CHANGE_NOTIFICATION_BPDU = 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 {
> + ovs_be16 protocol_identifier;
> + uint8_t protocol_version_identifier;
> + uint8_t bpdu_type;
> + uint8_t flags;
> + ovs_be64 root_bridge_id;
> + ovs_be32 root_path_cost;
> + ovs_be64 designated_bridge_id;
> + ovs_be16 designated_port_id;
> + ovs_be16 message_age;
> + ovs_be16 max_age;
> + ovs_be16 hello_time;
> + ovs_be16 forward_delay;
> + 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;
> + struct list node; /* Node in rstp->ports list. */
> + 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 variables are set by management actions on the bridge
> +
> ************************************************************************/
> +
> + /* Port number and priority
> + * >=1 (max 12 bits [9.2.7])
> + */
> + uint16_t port_number;
> +
> + /* 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 variables 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.
> + */
> + uint16_t 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.
> + */
> + uint16_t 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.
> + */
> + uint16_t 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).
> + */
> + uint16_t mdelay_while;
> +
> + /* [17.17.5 - rbWhile]
> + * The Recent Backup timer. Maintained at its initial value, twice
> + * HelloTime, while the Port is a Backup Port.
> + */
> + uint16_t 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.
> + */
> + uint16_t rcvd_info_while;
> +
> + /* [17.17.7 - rrWhile]
> + * The Recent Root timer.
> + */
> + uint16_t rr_while;
> +
> + /* [17.17.8 - tcWhile]
> + * The Topology Change timer. TCN Messages are sent while this timer
> is
> + * running.
> + */
> + uint16_t tc_while;
> +
> +
> +
> /*************************************************************************
> + * The following variables 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.
> + */
> + uint32_t 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.
> + */
> + uint16_t port_id;
> +
> + /* [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).
> + */
> + uint16_t 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 */
> + uint32_t rx_rstp_bpdu_cnt;
> +
> + /* Counter for bad RSTP received frames */
> + uint32_t 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.
> + */
> + uint32_t 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
> + * (stored in the least significant 48 bits of rstp_identifier).
> + */
> + rstp_identifier address; /* [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).
> + */
> + uint32_t 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).
> + */
> + uint16_t bridge_forward_delay;
> +
> + /* [17.13.6 - Bridge Hello Time]
> + * The interval between periodic transmissions of Configuration
> Messages
> + * by Designated Ports (Table 17-1).
> + */
> + uint16_t 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).
> + */
> + uint16_t 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).
> + */
> + uint16_t 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.
> + */
> + uint16_t transmit_hold_count;
> +
> +
> +
> /*************************************************************************
> + * The following variables 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.
> + */
> + rstp_identifier bridge_identifier;
> +
> + /* [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.
> + */
> + uint16_t root_port_id;
> +
> + /* [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 list ports;
> + uint16_t ports_count;
> +
> + struct ovs_refcount ref_cnt;
> +
> + /* Interface to client. */
> + 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..4aea49d
> --- /dev/null
> +++ b/lib/rstp-state-machines.c
> @@ -0,0 +1,2036 @@
> +/*
> + * 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 "rstp.h"
> +#include "rstp-state-machines.h"
> +#include <arpa/inet.h>
> +#include <inttypes.h>
> +#include <netinet/in.h>
> +#include <stdlib.h>
> +#include <sys/types.h>
> +#include "byte-order.h"
> +#include "connectivity.h"
> +#include "ofpbuf.h"
> +#include "packets.h"
> +#include "seq.h"
> +#include "unixctl.h"
> +#include "util.h"
> +#include "vlog.h"
> +
> +VLOG_DEFINE_THIS_MODULE(rstp_sm);
> +
> +#define ROLE_FLAG_MASK 0xC
> +#define ROLE_FLAG_SHIFT 2
> +
> +enum port_flag {
> + PORT_UNKN = 0,
> + PORT_ALT_BACK = 1,
> + PORT_ROOT = 2,
> + PORT_DES = 3
> +};
> +
> +enum bpdu_size {
> + CONFIGURATION_BPDU_SIZE = 35,
> + TOPOLOGY_CHANGE_NOTIFICATION_BPDU_SIZE = 4,
> + RAPID_SPANNING_TREE_BPDU_SIZE = 36
> +};
> +
> +enum vector_comparison {
> + INFERIOR = 0,
> + SUPERIOR = 1,
> + SAME = 2
> +};
> +
> +static void decrement_timer(uint16_t *);
> +static void rstp_send_bpdu(struct rstp_port *, const void *, size_t);
> +static int validate_received_bpdu(struct rstp_port *, const void *,
> size_t);
> +static ovs_be16 time_encode(uint8_t);
> +static uint8_t time_decode(ovs_be16);
> +static enum vector_comparison
> +compare_rstp_priority_vector(struct rstp_priority_vector *,
> + struct rstp_priority_vector *);
> +static bool rstp_times_equal(struct rstp_times *, struct rstp_times *);
> +
> +/* Per-Bridge State Machine */
> +static int port_role_selection_sm(struct rstp *);
> +/* Per-Port State Machines */
> +static int port_receive_sm(struct rstp_port *);
> +static int port_protocol_migration_sm(struct rstp_port *);
> +static int bridge_detection_sm(struct rstp_port *);
> +static int port_transmit_sm(struct rstp_port *);
> +static int port_information_sm(struct rstp_port *);
> +static int port_role_transition_sm(struct rstp_port *);
> +static int port_state_transition_sm(struct rstp_port *);
> +static int topology_change_sm(struct rstp_port *);
> +/* port_timers_sm() not defined as a state machine */
> +
> +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++;
> + }
> +}
> +
> +static int
> +validate_received_bpdu(struct rstp_port *p, const void *bpdu, size_t
> bpdu_size)
> +{
> + /* Validation of received BPDU, see [9.3.4]. */
> + const struct rstp_bpdu *temp;
> +
> + temp = bpdu;
> + if (bpdu_size < TOPOLOGY_CHANGE_NOTIFICATION_BPDU_SIZE ||
> + ntohs(temp->protocol_identifier) != 0) {
> + return -1;
> + } else {
> + if (temp->bpdu_type == CONFIGURATION_BPDU &&
> + bpdu_size >= CONFIGURATION_BPDU_SIZE &&
> + (time_decode(temp->message_age) <
> time_decode(temp->max_age)))
> + {
> + if ((ntohll(temp->designated_bridge_id) !=
> + p->rstp->bridge_identifier) ||
> + ((ntohll(temp->designated_bridge_id) ==
> + p->rstp->bridge_identifier) &&
> + (ntohs(temp->designated_port_id) != p->port_id))) {
> + return 0;
> + }
> + else {
> + return -1;
> + }
> + } else if (temp->bpdu_type == TOPOLOGY_CHANGE_NOTIFICATION_BPDU) {
> + return 0;
> + } else if (temp->bpdu_type == RAPID_SPANNING_TREE_BPDU &&
> + bpdu_size >= RAPID_SPANNING_TREE_BPDU_SIZE) {
> + 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)
> +{
> + struct rstp_port *p;
> + int 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);
> + if (rstp->ports_count > 0) {
> + LIST_FOR_EACH (p, node, &rstp->ports) {
> + 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)
> +{
> + struct rstp_port *p;
> +
> + if (r->ports_count > 0) {
> + LIST_FOR_EACH (p, node, &r->ports) {
> + 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);
> +}
> +
> +static void
> +decrement_timer(uint16_t *timer)
> +{
> + if (*timer != 0) {
> + *timer -= 1;
> + }
> +}
> +
> +/* Bridge State Machine. */
> +/* [17.28] Port Role Selection state machine. */
> +
> +static void
> +updt_role_disabled_tree(struct rstp *r)
> +{
> + struct rstp_port *p;
> +
> + if (r->ports_count > 0) {
> + LIST_FOR_EACH (p, node, &r->ports) {
> + p->selected_role = ROLE_DISABLED;
> + }
> + }
> +}
> +
> +static void
> +clear_reselect_tree(struct rstp *r)
> +{
> + struct rstp_port *p;
> +
> + if (r->ports_count > 0) {
> + LIST_FOR_EACH (p, node, &r->ports) {
> + p->reselect = false;
> + }
> + }
> +}
> +
> +void
> +updt_roles_tree(struct rstp *r)
> +{
> + struct rstp_port *p;
> + int vsel;
> + struct rstp_priority_vector best_vector, candidate_vector;
> +
> + vsel = -1;
> + best_vector = r->bridge_priority;
> + /* Letter c1) */
> + r->root_times = r->bridge_times;
> + /* Letters a) b) c) */
> + if (r->ports_count > 0) {
> + LIST_FOR_EACH (p, node, &r->ports) {
> + uint32_t old_root_path_cost;
> + uint32_t root_path_cost;
> + if (p->info_is != INFO_IS_RECEIVED) {
> + continue;
> + }
> + /* [17.6] */
> + candidate_vector = p->port_priority;
> + candidate_vector.bridge_port_id = p->port_id;
> + old_root_path_cost = candidate_vector.root_path_cost;
> + root_path_cost = old_root_path_cost + p->port_path_cost;
> + candidate_vector.root_path_cost = root_path_cost;
> +
> + if ((candidate_vector.designated_bridge_id &
> 0xffffffffffffULL) ==
> + (r->bridge_priority.designated_bridge_id &
> 0xffffffffffffULL))
> + {
> + break;
> + }
> + if (compare_rstp_priority_vector(&candidate_vector,
> &best_vector)
> + == SUPERIOR) {
> + best_vector = candidate_vector;
> + r->root_times = p->port_times;
> + r->root_times.message_age++;
> + vsel = p->port_number;
> + }
> + }
> + }
> + r->root_priority = best_vector;
> + r->root_port_id = best_vector.bridge_port_id;
> + VLOG_DBG("%s: new Root is "RSTP_ID_FMT"", r->name,
> + RSTP_ID_ARGS(r->root_priority.root_bridge_id));
> + /* Letters d) e) */
> + if (r->ports_count > 0) {
> + LIST_FOR_EACH (p, node, &r->ports) {
> + p->designated_priority_vector.root_bridge_id =
> + r->root_priority.root_bridge_id;
> + p->designated_priority_vector.root_path_cost =
> + r->root_priority.root_path_cost;
> + p->designated_priority_vector.designated_bridge_id =
> + r->bridge_identifier;
> + p->designated_priority_vector.designated_port_id =
> + p->port_id;
> + p->designated_times = r->root_times;
> + p->designated_times.hello_time = r->bridge_times.hello_time;
> + }
> + }
> + if (r->ports_count > 0) {
> + LIST_FOR_EACH (p, node, &r->ports) {
> + 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 ((compare_rstp_priority_vector(&p->port_priority,
> + &p->designated_priority_vector) != SAME) ||
> + !rstp_times_equal(&p->designated_times,
> &r->root_times)) {
> + 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 (compare_rstp_priority_vector(
> + &p->designated_priority_vector,
> &p->port_priority)
> + == INFERIOR) {
> + if (p->port_priority.designated_bridge_id !=
> + r->bridge_identifier) {
> + 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_NOT_REACHED();
> + /* no break */
> + }
> + }
> + }
> + seq_change(connectivity_seq_get());
> +}
> +
> +static void
> +set_selected_tree(struct rstp *r)
> +{
> + struct rstp_port *p;
> +
> + if (r->ports_count > 0) {
> + LIST_FOR_EACH (p, node, &r->ports) {
> + if (p->reselect) {
> + return;
> + }
> + }
> + LIST_FOR_EACH (p, node, &r->ports) {
> + p->selected = true;
> + }
> + }
> +}
> +
> +static int
> +port_role_selection_sm(struct rstp *r)
> +{
> + enum port_role_selection_state_machine old_state;
> + struct rstp_port *p;
> +
> + old_state = r->port_role_selection_sm_state;
> +
> + 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:
> + if (r->ports_count > 0) {
> + LIST_FOR_EACH (p, node, &r->ports) {
> + if (p->reselect) {
> + r->port_role_selection_sm_state =
> + PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC;
> + break;
> + }
> + }
> + }
> + break;
> + default:
> + OVS_NOT_REACHED();
> + /* 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] */
> +
> +static 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_BPDU:
> + 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_NOT_REACHED();
> + /* no break */
> + }
> +}
> +
> +static int
> +port_receive_sm(struct rstp_port *p)
> +{
> + enum port_receive_state_machine old_state;
> + struct rstp *r;
> +
> + old_state = p->port_receive_sm_state;
> + 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_NOT_REACHED();
> + /* 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] */
> +static int
> +port_protocol_migration_sm(struct rstp_port *p)
> +{
> + enum port_protocol_migration_state_machine old_state;
> + struct rstp *r;
> +
> + old_state = p->port_protocol_migration_sm_state;
> + 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;
> + }
> + else 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;
> + }
> + else if (p->send_rstp && p->rcvd_stp) {
> + p->port_protocol_migration_sm_state =
> + PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP_EXEC;
> + }
> + break;
> + default:
> + OVS_NOT_REACHED();
> + /* 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] */
> +static int
> +bridge_detection_sm(struct rstp_port *p)
> +{
> + enum bridge_detection_state_machine old_state;
> + struct rstp *r;
> +
> + old_state = p->bridge_detection_sm_state;
> + 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_NOT_REACHED();
> + /* 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)
> +{
> + struct eth_header *eth;
> + struct llc_header *llc;
> + struct ofpbuf *pkt;
> +
> + /* Skeleton. */
> + pkt = ofpbuf_new(ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size);
> + eth = ofpbuf_put_zeros(pkt, sizeof *eth);
> + llc = ofpbuf_put_zeros(pkt, sizeof *llc);
> + ofpbuf_set_frame(pkt, eth);
> + ofpbuf_set_l3(pkt, ofpbuf_put(pkt, bpdu, bpdu_size));
> +
> + /* 802.2 header. */
> + memcpy(eth->eth_dst, eth_addr_stp, ETH_ADDR_LEN);
> + /* p->rstp->send_bpdu() must fill in source address. */
> + eth->eth_type = htons(ofpbuf_size(pkt) - ETH_HEADER_LEN);
> +
> + /* LLC header. */
> + llc->llc_dsap = STP_LLC_DSAP;
> + llc->llc_ssap = STP_LLC_SSAP;
> + llc->llc_cntl = STP_LLC_CNTL;
> + p->rstp->send_bpdu(pkt, rstp_port_number(p), p->rstp->aux);
> +}
> +
> +static void
> +record_agreement(struct rstp_port *p)
> +{
> + struct rstp *r;
> +
> + r = p->rstp;
> + if (r->rstp_version && p->oper_point_to_point_mac &&
> + ((p->received_bpdu_buffer.flags & BPDU_FLAG_AGREEMENT))) {
> + p->agreed = true;
> + p->proposing = false;
> + } else {
> + p->agreed = false;
> + }
> +}
> +
> +static 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_BPDU) {
> + p->rcvd_tcn = true;
> + }
> +}
> +
> +static void
> +record_dispute(struct rstp_port *p)
> +{
> + if ((p->received_bpdu_buffer.flags & BPDU_FLAG_LEARNING) != 0) {
> + p->agreed = true;
> + p->proposing = false;
> + }
> +}
> +
> +static void
> +record_proposal(struct rstp_port *p)
> +{
> + enum port_flag role =
> + ((p->received_bpdu_buffer.flags) & ROLE_FLAG_MASK) >>
> ROLE_FLAG_SHIFT;
> + if ((role == PORT_DES) &&
> + ((p->received_bpdu_buffer.flags & BPDU_FLAG_PROPOSAL) != 0)) {
> + p->proposed = true;
> + }
> +}
> +
> +static void
> +record_priority(struct rstp_port *p)
> +{
> + p->port_priority.root_bridge_id = p->msg_priority.root_bridge_id;
> + p->port_priority.root_path_cost = p->msg_priority.root_path_cost;
> + p->port_priority.designated_bridge_id =
> + p->msg_priority.designated_bridge_id;
> + p->port_priority.designated_port_id =
> p->msg_priority.designated_port_id;
> +}
> +
> +static void
> +record_times(struct rstp_port *p)
> +{
> + p->port_times = p->msg_times;
> + if (p->msg_times.hello_time == 0) {
> + p->port_times.hello_time = 1;
> + }
> +}
> +
> +static void
> +updt_rcvd_info_while(struct rstp_port *p)
> +{
> + /* [17.21.23]
> + * The value assigned to rcvdInfoWhile is the three times the Hello
> Time,
> + * if Message Age, incremented by 1 second and rounded to the nearest
> whole
> + * second, does not exceed Max Age, and is zero otherwise.
> + */
> + if (p->port_times.message_age < p->port_times.max_age) {
> + p->rcvd_info_while = p->port_times.hello_time * 3;
> + } else {
> + p->rcvd_info_while = 0;
> + }
> +}
> +
> +/* Times are internally held in seconds, while the protocol uses 1/256
> seconds.
> + * time_encode() is used to convert time values sent in bpdus, while
> + * time_decode() is used to convert time values received in bpdus.
> + */
> +static ovs_be16
> +time_encode(uint8_t value)
> +{
> + return htons(value * 256);
> +}
> +
> +static uint8_t
> +time_decode(ovs_be16 encoded)
> +{
> + return ntohs(encoded) / 256;
> +}
> +
> +/* [17.21.19] */
> +static void
> +tx_config(struct rstp_port *p)
> +{
> + struct rstp_bpdu bpdu;
> +
> + bpdu.protocol_identifier = htons(0);
> + bpdu.protocol_version_identifier = 0;
> + bpdu.bpdu_type = CONFIGURATION_BPDU;
> + bpdu.root_bridge_id =
> htonll(p->designated_priority_vector.root_bridge_id);
> + bpdu.root_path_cost =
> htonl(p->designated_priority_vector.root_path_cost);
> + bpdu.designated_bridge_id =
> + htonll(p->designated_priority_vector.designated_bridge_id);
> + bpdu.designated_port_id =
> + htons(p->designated_priority_vector.designated_port_id);
> + bpdu.message_age = time_encode(p->designated_times.message_age);
> + bpdu.max_age = time_encode(p->designated_times.max_age);
> + bpdu.hello_time = time_encode(p->designated_times.hello_time);
> + bpdu.forward_delay = time_encode(p->designated_times.forward_delay);
> + bpdu.flags = 0;
> + 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] */
> +static void
> +tx_rstp(struct rstp_port *p)
> +{
> + struct rstp_bpdu bpdu;
> +
> + bpdu.protocol_identifier = htons(0);
> + bpdu.protocol_version_identifier = 2;
> + bpdu.bpdu_type = RAPID_SPANNING_TREE_BPDU;
> + bpdu.root_bridge_id =
> htonll(p->designated_priority_vector.root_bridge_id);
> + bpdu.root_path_cost =
> htonl(p->designated_priority_vector.root_path_cost);
> + bpdu.designated_bridge_id =
> + htonll(p->designated_priority_vector.designated_bridge_id);
> + bpdu.designated_port_id =
> + htons(p->designated_priority_vector.designated_port_id);
> + bpdu.message_age = time_encode(p->designated_times.message_age);
> + bpdu.max_age = time_encode(p->designated_times.max_age);
> + bpdu.hello_time = time_encode(p->designated_times.hello_time);
> + bpdu.forward_delay = time_encode(p->designated_times.forward_delay);
> + bpdu.flags = 0;
> + switch (p->role) {
> + case ROLE_ROOT:
> + bpdu.flags = PORT_ROOT << ROLE_FLAG_SHIFT;
> + break;
> + case ROLE_DESIGNATED:
> + bpdu.flags = PORT_DES << ROLE_FLAG_SHIFT;
> + break;
> + case ROLE_ALTERNATE:
> + case ROLE_BACKUP:
> + bpdu.flags = PORT_ALT_BACK << ROLE_FLAG_SHIFT;
> + break;
> + case ROLE_DISABLED:
> + /* should not happen! */
> + VLOG_ERR("%s transmitting bpdu in disabled role on port "
> + ""RSTP_PORT_ID_FMT"", p->rstp->name, p->port_id);
> + OVS_NOT_REACHED();
> + 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;
> + }
> + bpdu.version1_length = 0;
> + rstp_send_bpdu(p, &bpdu, sizeof(struct rstp_bpdu));
> +}
> +
> +/* [17.21.21] */
> +static void
> +tx_tcn(struct rstp_port *p)
> +{
> + struct rstp_bpdu bpdu;
> +
> + memset(&bpdu, 0, sizeof(struct rstp_bpdu));
> +
> + bpdu.protocol_identifier = htons(0);
> + bpdu.protocol_version_identifier = 0;
> + bpdu.bpdu_type = TOPOLOGY_CHANGE_NOTIFICATION_BPDU;
> + rstp_send_bpdu(p, &bpdu, sizeof(struct rstp_bpdu));
> +}
> +
> +static int
> +port_transmit_sm(struct rstp_port *p)
> +{
> + enum port_transmit_state_machine old_state;
> + struct rstp *r;
> +
> + old_state = p->port_transmit_sm_state;
> + 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;
> + }
> + else 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;
> + }
> + else if (p->hello_when == 0 && p->selected && !p->updt_info) {
> + p->port_transmit_sm_state =
> + PORT_TRANSMIT_SM_TRANSMIT_PERIODIC_EXEC;
> + }
> + else 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;
> + }
> + else 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 += 1;
> + 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_NOT_REACHED();
> + /* 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
> +
> +static int
> +rcv_info(struct rstp_port *p)
> +{
> + enum vector_comparison cp;
> + bool ct;
> + enum port_flag role;
> +
> + p->msg_priority.root_bridge_id =
> + ntohll(p->received_bpdu_buffer.root_bridge_id);
> + p->msg_priority.root_path_cost =
> + ntohl(p->received_bpdu_buffer.root_path_cost);
> + p->msg_priority.designated_bridge_id =
> + ntohll(p->received_bpdu_buffer.designated_bridge_id);
> + p->msg_priority.designated_port_id =
> + ntohs(p->received_bpdu_buffer.designated_port_id);
> +
> + 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 = compare_rstp_priority_vector(&p->msg_priority,
> &p->port_priority);
> + ct = rstp_times_equal(&p->port_times, &p->msg_times);
> + role =
> + ((p->received_bpdu_buffer.flags) & ROLE_FLAG_MASK) >>
> ROLE_FLAG_SHIFT;
> +
> + /* 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) || ((cp == SAME) && ct == false))) {
> + 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 == true)) {
> + 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 == INFERIOR)) {
> + 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 == INFERIOR || cp == SAME)) {
> + return INFERIOR_ROOT_ALTERNATE_INFO;
> + }
> +
> + /* Otherwise, returns OtherInfo. */
> + else {
> + return OTHER_INFO;
> + }
> +}
> +
> +static int
> +better_or_same_info(struct rstp_port *p, int new_info_is)
> +{
> + /* >= SUPERIOR means that the vector is better or the same. */
> + return ((new_info_is == RECEIVED && p->info_is == INFO_IS_RECEIVED &&
> + compare_rstp_priority_vector(&p->msg_priority,
> + &p->port_priority) >= SUPERIOR) ||
> + (new_info_is == MINE && p->info_is == INFO_IS_MINE &&
> + compare_rstp_priority_vector(&p->designated_priority_vector,
> + &p->port_priority) >= SUPERIOR));
> +}
> +
> +static int
> +port_information_sm(struct rstp_port *p)
> +{
> + enum port_information_state_machine old_state;
> + struct rstp *r;
> +
> + old_state = p->port_information_sm_state;
> + 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;
> + }
> + else 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;
> + /* MINE is not specified in Standard 802.1D-2004. */
> + p->agreed = p->agreed && better_or_same_info(p, MINE);
> + p->synced = p->synced && p->agreed;
> + p->port_priority.root_bridge_id =
> + p->designated_priority_vector.root_bridge_id;
> + p->port_priority.root_path_cost =
> + p->designated_priority_vector.root_path_cost;
> + p->port_priority.designated_bridge_id =
> + p->designated_priority_vector.designated_bridge_id;
> + p->port_priority.designated_port_id =
> + p->designated_priority_vector.designated_port_id;
> + p->port_times = p->designated_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_NOT_REACHED();
> + /* 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);
> + /* RECEIVED is not specified in Standard 802.1D-2004. */
> + p->agree = p->agree && better_or_same_info(p, RECEIVED);
> + 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_NOT_REACHED();
> + /* 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] */
> +
> +static void
> +set_re_root_tree(struct rstp_port *p)
> +{
> + struct rstp *r;
> + struct rstp_port *p1;
> +
> + r = p->rstp;
> + if (r->ports_count > 0) {
> + LIST_FOR_EACH (p1, node, &r->ports) {
> + p1->re_root = true;
> + }
> + }
> +}
> +
> +static void
> +set_sync_tree(struct rstp_port *p)
> +{
> + struct rstp *r;
> + struct rstp_port *p1;
> +
> + r = p->rstp;
> + if (r->ports_count > 0) {
> + LIST_FOR_EACH (p1, node, &r->ports) {
> + p1->sync = true;
> + }
> + }
> +}
> +
> +static int
> +hello_time(struct rstp_port *p)
> +{
> + return p->designated_times.hello_time;
> +}
> +
> +static int
> +fwd_delay(struct rstp_port *p)
> +{
> + return p->designated_times.forward_delay;
> +}
> +
> +static int
> +forward_delay(struct rstp_port *p)
> +{
> + if (p->send_rstp) {
> + return hello_time(p);
> + } else {
> + return fwd_delay(p);
> + }
> +}
> +
> +static int
> +edge_delay(struct rstp_port *p)
> +{
> + struct rstp *r;
> +
> + r = p->rstp;
> + if (p->oper_point_to_point_mac == 1) {
> + return r->migrate_time;
> + } else {
> + return p->designated_times.max_age;
> + }
> +}
> +
> +static 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)) {
> + VLOG_DBG("%s, port %u: 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);
> + switch (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;
> +}
> +
> +static int
> +re_rooted(struct rstp_port *p)
> +{
> + struct rstp *r;
> + struct rstp_port *p1;
> +
> + r = p->rstp;
> + if (r->ports_count > 0) {
> + LIST_FOR_EACH (p1, node, &r->ports) {
> + if ((p1 != p) && (p1->rr_while != 0)) {
> + return false;
> + }
> + }
> + }
> + return true;
> +}
> +
> +static int
> +all_synced(struct rstp *r)
> +{
> + struct rstp_port *p;
> +
> + if (r->ports_count > 0) {
> + LIST_FOR_EACH (p, node, &r->ports) {
> + if (!(p->selected && p->role == p->selected_role &&
> + (p->role == ROLE_ROOT || p->synced == true))) {
> + return false;
> + }
> + }
> + }
> + return true;
> +}
> +
> +static int
> +port_role_transition_sm(struct rstp_port *p)
> +{
> + enum port_role_transition_state_machine old_state;
> + struct rstp *r;
> + enum rstp_port_role last_role;
> +
> + old_state = p->port_role_transition_sm_state;
> + r = p->rstp;
> + 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;
> + }
> + else 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;
> + }
> + else 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;
> + }
> + else 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;
> + }
> + else if (p->re_root && p->forward) {
> + p->port_role_transition_sm_state =
> + PORT_ROLE_TRANSITION_SM_REROOTED_EXEC;
> + break;
> + }
> + else 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;
> + }
> + else 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;
> + }
> + else if (p->proposed && !p->agree) {
> + p->port_role_transition_sm_state =
> + PORT_ROLE_TRANSITION_SM_ROOT_PROPOSED_EXEC;
> + break;
> + }
> + else if ((all_synced(r) && !p->agree) ||
> + (p->proposed && p->agree)) {
> + p->port_role_transition_sm_state =
> + PORT_ROLE_TRANSITION_SM_ROOT_AGREED_EXEC;
> + break;
> + }
> + else 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;
> + }
> + else 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;
> + }
> + else 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;
> + }
> + else 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;
> + }
> + else if (!p->forward && !p->agreed && !p->proposing &&
> + !p->oper_edge) {
> + p->port_role_transition_sm_state =
> + PORT_ROLE_TRANSITION_SM_DESIGNATED_PROPOSE_EXEC;
> + }
> + else 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;
> + }
> + else 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;
> + }
> + else 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;
> + }
> + else 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;
> + }
> + else if (p->proposed && !p->agree) {
> + p->port_role_transition_sm_state =
> + PORT_ROLE_TRANSITION_SM_ALTERNATE_PROPOSED_EXEC;
> + }
> + else 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;
> + }
> + else 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_NOT_REACHED();
> + /* 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 ["RSTP_PORT_ID_FMT"] = %s",
> + p->rstp->name, p->port_number, p->port_id,
> + rstp_port_role_name(p->role));
> + }
> + return 0;
> +}
> +
> +/* [17.30 - Port state transition state machine] */
> +
> +static 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);
> +}
> +
> +static 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);
> +}
> +
> +static 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);
> +}
> +
> +static 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);
> +}
> +
> +static int
> +port_state_transition_sm(struct rstp_port *p)
> +{
> + enum port_state_transition_state_machine old_state;
> + struct rstp *r;
> +
> + old_state = p->port_state_transition_sm_state;
> + 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;
> + }
> + else 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_NOT_REACHED();
> + /* 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] */
> +
> +static void
> +new_tc_while(struct rstp_port *p)
> +{
> + struct rstp *r;
> +
> + r = p->rstp;
> + if (p->tc_while == 0 && p->send_rstp == true) {
> + p->tc_while = r->bridge_hello_time + 1;
> + p->new_info = true;
> + }
> + else 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.
> + */
> +static void
> +set_tc_prop_tree(struct rstp_port *p)
> +{
> + struct rstp *r;
> + struct rstp_port *p1;
> +
> + r = p->rstp;
> + if (r->ports_count > 0) {
> + LIST_FOR_EACH (p1, node, &r->ports) {
> + /* Set tc_prop on every port, except the one calling this
> + * function.
> + */
> + if (p1->port_number != p->port_number) {
> + p1->tc_prop = true;
> + }
> + }
> + }
> +}
> +
> +static void
> +set_tc_prop_bridge(struct rstp_port *p) /* not specified in 802.1D-2004.
> */
> +{
> + set_tc_prop_tree(p); /* see 802.1w-2001. */
> +}
> +
> +static int
> +topology_change_sm(struct rstp_port *p)
> +{
> + enum topology_change_state_machine old_state;
> + struct rstp *r;
> +
> + old_state = p->topology_change_sm_state;
> + 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;
> + }
> + else if (p->rcvd_tc || p->rcvd_tcn || p->rcvd_tc_ack ||
> p->tc_prop) {
> + p->topology_change_sm_state =
> TOPOLOGY_CHANGE_SM_LEARNING_EXEC;
> + }
> + else 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;
> + }
> + else if (p->rcvd_tcn) {
> + p->topology_change_sm_state =
> TOPOLOGY_CHANGE_SM_NOTIFIED_TCN_EXEC;
> + }
> + else if (p->rcvd_tc) {
> + p->topology_change_sm_state =
> TOPOLOGY_CHANGE_SM_NOTIFIED_TC_EXEC;
> + }
> + else if (p->tc_prop && !p->oper_edge) {
> + p->topology_change_sm_state =
> TOPOLOGY_CHANGE_SM_PROPAGATING_EXEC;
> + }
> + else 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_NOT_REACHED();
> + /* 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))
> + */
> +
> +/* compare_rstp_priority_vector() compares two struct
> rstp_priority_vector and
> + * returns a value indicating if the first rstp_priority_vector is
> superior,
> + * same or inferior to the second one.
> + */
> +static enum vector_comparison
> +compare_rstp_priority_vector(struct rstp_priority_vector *v1,
> + struct rstp_priority_vector *v2)
> +{
> + VLOG_DBG("v1: "RSTP_ID_FMT", %u, "RSTP_ID_FMT", %d",
> + RSTP_ID_ARGS(v1->root_bridge_id), v1->root_path_cost,
> + RSTP_ID_ARGS(v1->designated_bridge_id),
> v1->designated_port_id);
> + VLOG_DBG("v2: "RSTP_ID_FMT", %u, "RSTP_ID_FMT", %d",
> + RSTP_ID_ARGS(v2->root_bridge_id), v2->root_path_cost,
> + RSTP_ID_ARGS(v2->designated_bridge_id),
> v2->designated_port_id);
> +
> + if ((v1->root_bridge_id < v2->root_bridge_id) ||
> + ((v1->root_bridge_id == v2->root_bridge_id) &&
> + (v1->root_path_cost < v2->root_path_cost)) ||
> + ((v1->root_bridge_id == v2->root_bridge_id) &&
> + (v1->root_path_cost == v2->root_path_cost) &&
> + (v1->designated_bridge_id < v2->designated_bridge_id)) ||
> + ((v1->root_bridge_id == v2->root_bridge_id) &&
> + (v1->root_path_cost == v2->root_path_cost) &&
> + (v1->designated_bridge_id == v2->designated_bridge_id) &&
> + (v1->designated_port_id < v2->designated_port_id))) {
> + VLOG_DBG("superior_absolute");
> + return SUPERIOR;
> + }
> + else if (((v1->root_bridge_id > v2->root_bridge_id) ||
> + ((v1->root_bridge_id == v2->root_bridge_id) &&
> + (v1->root_path_cost > v2->root_path_cost)) ||
> + ((v1->root_bridge_id == v2->root_bridge_id) &&
> + (v1->root_path_cost == v2->root_path_cost) &&
> + (v1->designated_bridge_id > v2->designated_bridge_id)) ||
> + ((v1->root_bridge_id == v2->root_bridge_id) &&
> + (v1->root_path_cost == v2->root_path_cost) &&
> + (v1->designated_bridge_id == v2->designated_bridge_id) &&
> + (v1->designated_port_id > v2->designated_port_id))) &&
> + (v1->designated_bridge_id == v2->designated_bridge_id) &&
> + (v1->designated_port_id == v2->designated_port_id)) {
> + VLOG_DBG("superior_same_des");
> + return SUPERIOR;
> + }
> + else if ((v1->root_bridge_id == v2->root_bridge_id) &&
> + (v1->root_path_cost == v2->root_path_cost) &&
> + (v1->designated_bridge_id == v2->designated_bridge_id) &&
> + (v1->designated_port_id == v2->designated_port_id)) {
> + VLOG_DBG("same");
> + return SAME;
> + }
> + else {
> + VLOG_DBG("inferior");
> + return INFERIOR;
> + }
> +}
> +
> +static bool
> +rstp_times_equal(struct rstp_times *t1, struct rstp_times *t2) {
> + return ((t1->forward_delay == t2->forward_delay) &&
> + (t1->hello_time == t2->hello_time) &&
> + (t1->max_age == t2->max_age) &&
> + (t1->message_age == t2->message_age));
> +}
> diff --git a/lib/rstp-state-machines.h b/lib/rstp-state-machines.h
> new file mode 100644
> index 0000000..7b9f8bc
> --- /dev/null
> +++ b/lib/rstp-state-machines.h
> @@ -0,0 +1,42 @@
> +/*
> + * 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"
> +
> +/* Methods called by the Forwarding Layer, through functions of rstp.h. */
> +int move_rstp(struct rstp *);
> +void decrease_rstp_port_timers(struct rstp *);
> +void process_received_bpdu(struct rstp_port *, const void *, size_t);
> +
> +void updt_roles_tree(struct rstp *);
> +
> +#endif /* rstp-state-machines.h */
> diff --git a/lib/rstp.c b/lib/rstp.c
> new file mode 100644
> index 0000000..b0ad613
> --- /dev/null
> +++ b/lib/rstp.c
> @@ -0,0 +1,1236 @@
> +/*
> + * 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 "rstp.h"
> +#include "rstp-common.h"
> +#include "rstp-state-machines.h"
> +#include <arpa/inet.h>
> +#include <inttypes.h>
> +#include <netinet/in.h>
> +#include <stdlib.h>
> +#include <sys/types.h>
> +#include "byte-order.h"
> +#include "connectivity.h"
> +#include "ofpbuf.h"
> +#include "ofproto/ofproto.h"
> +#include "packets.h"
> +#include "seq.h"
> +#include "unixctl.h"
> +#include "util.h"
> +#include "vlog.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 */
> +static void set_port_id__(struct rstp_port *);
> +static void update_port_enabled__(struct rstp_port *);
> +static void set_bridge_priority__(struct rstp *);
> +static void reinitialize_rstp__(struct rstp *);
> +static bool is_port_number_taken__(struct rstp *, int, struct rstp_port
> *);
> +static uint16_t rstp_first_free_number__(struct rstp *, struct rstp_port
> *);
> +static void rstp_initialize_port(struct rstp_port *p);
> +
> +const 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";
> + }
> +}
> +
> +const 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(struct rstp *rstp_)
> +{
> + struct rstp *rstp;
> +
> + rstp = rstp_;
> + if (rstp) {
> + ovs_refcount_ref(&rstp->ref_cnt);
> + }
> + return rstp;
> +}
> +
> +/* Frees RSTP struct */
> +void
> +rstp_unref(struct rstp *rstp)
> +{
> + struct rstp_port *p;
> +
> + if (rstp && ovs_refcount_unref(&rstp->ref_cnt) == 1) {
> + ovs_mutex_lock(&mutex);
> + if (rstp->ports_count > 0) {
> + LIST_FOR_EACH (p, node, &rstp->ports) {
> + rstp_delete_port(p);
> + }
> + }
> + list_remove(&rstp->node);
> + ovs_mutex_unlock(&mutex);
> + free(rstp->name);
> + free(rstp);
> + }
> +}
> +
> +/* Returns the port number. */
> +int
> +rstp_port_number(const struct rstp_port *p)
> +{
> + int number;
> +
> + ovs_mutex_lock(&mutex);
> + number = p->port_number;
> + ovs_mutex_unlock(&mutex);
> + return number;
> +}
> +
> +static void rstp_unixctl_tcn(struct unixctl_conn *, int argc,
> + const char *argv[], void *aux);
> +
> +/* Decrements the State Machines' timers. */
> +void
> +rstp_tick_timers(struct rstp *rstp)
> +{
> + ovs_mutex_lock(&mutex);
> + decrease_rstp_port_timers(rstp);
> + ovs_mutex_unlock(&mutex);
> +}
> +
> +/* Processes an incoming BPDU. */
> +void
> +rstp_received_bpdu(struct rstp_port *p, const void *bpdu, size_t
> bpdu_size)
> +{
> + ovs_mutex_lock(&mutex);
> + process_received_bpdu(p, bpdu, bpdu_size);
> + ovs_mutex_unlock(&mutex);
> +}
> +
> +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. */
> +struct rstp *
> +rstp_create(const char *name, rstp_identifier 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;
> +
> + VLOG_DBG("Creating RSTP instance");
> + if (ovsthread_once_start(&once)) {
> + ovs_mutex_init_recursive(&mutex);
> + ovsthread_once_done(&once);
> + }
> +
> + 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;
> +
> + ovs_mutex_lock(&mutex);
> + /* Initialize the ports list. */
> + list_init(&rstp->ports);
> + 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.
> + */
> +static void
> +set_bridge_priority__(struct rstp *rstp)
> +{
> + rstp->bridge_priority.root_bridge_id = rstp->bridge_identifier;
> + rstp->bridge_priority.designated_bridge_id = rstp->bridge_identifier;
> + VLOG_DBG("%s: new bridge identifier: "RSTP_ID_FMT"", rstp->name,
> + RSTP_ID_ARGS(rstp->bridge_identifier));
> +}
> +
> +/* Sets the bridge address. */
> +void
> +rstp_set_bridge_address(struct rstp *rstp, rstp_identifier bridge_address)
> +{
> + struct rstp_port *p;
> +
> + VLOG_DBG("%s: set bridge address to: "RSTP_ID_FMT"", rstp->name,
> + RSTP_ID_ARGS(bridge_address));
> + ovs_mutex_lock(&mutex);
> + rstp->address = bridge_address;
> + rstp->bridge_identifier = bridge_address;
> + set_bridge_priority__(rstp);
> +
> + /* [17.13] When the bridge address changes, recalculates all priority
> + * vectors.
> + */
> + if (rstp->ports_count > 0) {
> + LIST_FOR_EACH (p, node, &rstp->ports) {
> + p->selected = 0;
> + p->reselect = 1;
> + }
> + }
> + rstp->changes = true;
> + updt_roles_tree(rstp);
> + ovs_mutex_unlock(&mutex);
> +}
> +
> +const char *
> +rstp_get_name(const struct rstp *rstp)
> +{
> + char *name;
> +
> + ovs_mutex_lock(&mutex);
> + name = rstp->name;
> + ovs_mutex_unlock(&mutex);
> + return name;
> +}
> +
> +rstp_identifier
> +rstp_get_bridge_id(const struct rstp *rstp)
> +{
> + rstp_identifier bridge_id;
> +
> + ovs_mutex_lock(&mutex);
> + bridge_id = rstp->bridge_identifier;
> + ovs_mutex_unlock(&mutex);
> + return bridge_id;
> +}
> +
> +/* Sets the bridge priority. */
> +void
> +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) {
> + VLOG_DBG("%s: set bridge priority to %d", rstp->name,
> + (new_priority / 4096) * 4096);
> + ovs_mutex_lock(&mutex);
> + rstp->priority = (new_priority / 4096) * 4096;
> + rstp->bridge_identifier &= 0xffffffffffffULL;
> + rstp->bridge_identifier |=
> + (uint64_t) ((new_priority / 4096) * 4096)
> << 48;
> + set_bridge_priority__(rstp);
> +
> + /* [17.13] */
> + if (rstp->ports_count > 0){
> + LIST_FOR_EACH (p, node, &rstp->ports) {
> + p->selected = 0;
> + p->reselect = 1;
> + }
> + }
> + rstp->changes = true;
> + updt_roles_tree(rstp);
> + ovs_mutex_unlock(&mutex);
> + }
> +}
> +
> +/* Sets the bridge ageing time. */
> +void
> +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) {
> + VLOG_DBG("%s: set ageing time to %d", rstp->name,
> new_ageing_time);
> + ovs_mutex_lock(&mutex);
> + rstp->ageing_time = new_ageing_time;
> + ovs_mutex_unlock(&mutex);
> + }
> +}
> +
> +/* Reinitializes RSTP when switching from RSTP mode to STP mode
> + * or vice versa.
> + */
> +static void
> +reinitialize_rstp__(struct rstp *rstp)
> +{
> + struct rstp temp;
> + struct rstp_port *p, temp_port;
> + static struct list ports;
> +
> + /* Copy rstp in temp */
> + temp = *rstp;
> + ports = rstp->ports;
> + /* stop and clear rstp */
> + memset(rstp, 0, sizeof(struct rstp));
> +
> + /* Initialize rstp. */
> + rstp->name = temp.name;
> + /* Set bridge address. */
> + rstp_set_bridge_address(rstp, temp.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 = temp.send_bpdu;
> + rstp->aux = temp.aux;
> + rstp->node = temp.node;
> + rstp->changes = false;
> + rstp->begin = true;
> + rstp->ports = ports;
> + rstp->ports_count = temp.ports_count;
> + if (rstp->ports_count > 0){
> + LIST_FOR_EACH (p, node, &rstp->ports) {
> + temp_port = *p;
> + memset(p, 0, sizeof(struct rstp_port));
> + p->rstp = rstp;
> + p->node = temp_port.node;
> + p->aux = temp_port.aux;
> + p->port_number = temp_port.port_number;
> + p->port_priority = temp_port.port_priority;
> + p->port_id = temp_port.port_id;
> + p->rstp_state = RSTP_DISCARDING;
> +
> + 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_auto_edge(p, true);
> + /* Initialize state machines. */
> + 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;
> + }
> + }
> + rstp->ref_cnt = temp.ref_cnt;
> +}
> +
> +/* Sets the force protocol version parameter. */
> +void
> +rstp_set_bridge_force_protocol_version(struct rstp *rstp,
> + enum rstp_force_protocol_version
> new_force_protocol_version)
> +{
> + if (new_force_protocol_version != rstp->force_protocol_version &&
> + (new_force_protocol_version == FPV_STP_COMPATIBILITY ||
> + new_force_protocol_version == FPV_DEFAULT)) {
> + 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;
> + move_rstp(rstp);
> + ovs_mutex_unlock(&mutex);
> + }
> +}
> +
> +/* Sets the bridge Hello Time parameter. */
> +void
> +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);
> +}
> +
> +/* Sets the bridge max age parameter. */
> +void
> +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);
> + }
> + }
> +}
> +
> +/* Sets the bridge forward delay parameter. */
> +void
> +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);
> + }
> + }
> +}
> +
> +/* Sets the bridge transmit hold count parameter. */
> +void
> +rstp_set_bridge_transmit_hold_count(struct rstp *rstp,
> + int new_transmit_hold_count)
> +{
> + struct rstp_port *p;
> +
> + 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;
> + if (rstp->ports_count > 0){
> + LIST_FOR_EACH (p, node, &rstp->ports) {
> + p->tx_count=0;
> + }
> + }
> + ovs_mutex_unlock(&mutex);
> + }
> +}
> +
> +/* Sets the bridge migrate time parameter. */
> +void
> +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);
> +}
> +
> +/* Sets the bridge times. */
> +void
> +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;
> +}
> +
> +/* Sets the port id, it is called by rstp_port_set_port_number() or
> + * rstp_port_set_priority().
> + */
> +static void
> +set_port_id__(struct rstp_port *p)
> +{
> + struct rstp *rstp;
> +
> + rstp = p->rstp;
> + /* [9.2.7] Port identifier. */
> + p->port_id = p->port_number | (p->priority << 8);
> + VLOG_DBG("%s: new RSTP port id "RSTP_PORT_ID_FMT"", rstp->name,
> + p->port_id);
> +}
> +
> +/* Sets the port priority. */
> +void
> +rstp_port_set_priority(struct rstp_port *rstp_port, int new_port_priority)
> +{
> + struct rstp *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 % RSTP_STEP_PORT_PRIORITY;
> + rstp_port->priority = new_port_priority;
> + set_port_id__(rstp_port);
> + rstp_port->selected = 0;
> + rstp_port->reselect = 1;
> + ovs_mutex_unlock(&mutex);
> + }
> +}
> +
> +/* Checks if a port number is already taken by an active port. */
> +static bool
> +is_port_number_taken__(struct rstp *rstp, int n, struct rstp_port
> *rstp_port)
> +{
> + struct rstp_port *p;
> +
> + if (rstp->ports_count > 0){
> + LIST_FOR_EACH (p, node, &rstp->ports) {
> + if (p->port_number == n && rstp_port != rstp_get_port(rstp,
> n)) {
> + VLOG_DBG("%s: port number %d not available", rstp->name,
> n);
> + return true;
> + }
> + }
> + }
> + VLOG_DBG("%s: port number %d is available", rstp->name, n);
> + return false;
> +}
> +
> +static uint16_t
> +rstp_first_free_number__(struct rstp *rstp, struct rstp_port *rstp_port) {
> + int free_number;
> +
> + free_number = 1;
> + ovs_mutex_lock(&mutex);
> + while (free_number <= RSTP_MAX_PORTS) {
> + if (!is_port_number_taken__(rstp, free_number, rstp_port)) {
> + ovs_mutex_unlock(&mutex);
> + return free_number;
> + }
> + free_number++;
> + }
> + ovs_mutex_unlock(&mutex);
> + VLOG_DBG("%s, No free port number available.", rstp->name);
> + return 0;
> +}
> +
> +/* Sets the port number. */
> +void
> +rstp_port_set_port_number(struct rstp_port *rstp_port,
> + uint16_t new_port_number)
> +{
> + struct rstp *rstp;
> +
> + rstp = rstp_port->rstp;
> + ovs_mutex_lock(&mutex);
> + /* If new_port_number is inside bounds and available, use it.
> + * If new_port_number is 0 or it is already taken, use the first free
> + * available port number.
> + */
> + if ((new_port_number >= 1 && new_port_number <= RSTP_MAX_PORTS) &&
> + (!is_port_number_taken__(rstp_port->rstp, new_port_number,
> rstp_port)))
> + {
> + rstp_port->port_number = new_port_number;
> + }
> + else if (new_port_number == 0 ||
> + is_port_number_taken__(rstp_port->rstp, new_port_number,
> + rstp_port)) {
> + rstp_port->port_number = rstp_first_free_number__(rstp,
> rstp_port);
> + }
> +
> + 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);
> + VLOG_DBG("%s: set new RSTP port number %d", rstp->name,
> + rstp_port->port_number);
> +}
> +
> +/* 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;
> +
> + value = speed >= 10000000 ? 2 /* 10 Tb/s. */
> + : speed >= 1000000 ? 20 /* 1 Tb/s. */
> + : speed >= 100000 ? 200 /* 100 Gb/s. */
> + : speed >= 10000 ? 2000 /* 10 Gb/s. */
> + : speed >= 1000 ? 20000 /* 1 Gb/s. */
> + : speed >= 100 ? 200000 /* 100 Mb/s. */
> + : speed >= 10 ? 2000000 /* 10 Mb/s. */
> + : speed >= 1 ? 20000000 /* 1 Mb/s. */
> + : RSTP_DEFAULT_PORT_PATH_COST; /* 100 Mb/s. */
> +
> + return value;
> +}
> +
> +/* Sets the port path cost. */
> +void
> +rstp_port_set_path_cost(struct rstp_port *rstp_port,
> + uint32_t new_port_path_cost)
> +{
> + struct rstp *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);
> + }
> +}
> +
> +/* Gets the root path cost. */
> +uint32_t
> +rstp_get_root_path_cost(const struct rstp *rstp)
> +{
> + uint32_t cost;
> +
> + ovs_mutex_lock(&mutex);
> + cost = 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;
> + struct rstp_port *p;
> +
> + needs_flush = false;
> +
> + ovs_mutex_lock(&mutex);
> + if (rstp->ports_count > 0){
> + LIST_FOR_EACH (p, node, &rstp->ports) {
> + if (p->fdb_flush) {
> + 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 *p;
> + bool changed;
> +
> + changed = false;
> +
> + ovs_mutex_lock(&mutex);
> + if (rstp->ports_count > 0){
> + LIST_FOR_EACH (p, node, &rstp->ports) {
> + if (p->state_changed) {
> + p->state_changed = false;
> + *portp = p;
> + changed = true;
> + ovs_mutex_unlock(&mutex);
> + return changed;
> + }
> + }
> + }
> + *portp = NULL;
> + ovs_mutex_unlock(&mutex);
> + return changed;
> +}
> +
> +/* Returns the port in 'rstp' with number 'port_number'. */
> +struct rstp_port *
> +rstp_get_port(struct rstp *rstp, int port_number)
> +{
> + struct rstp_port *port;
> +
> + ovs_mutex_lock(&mutex);
> + if (rstp->ports_count > 0){
> + LIST_FOR_EACH (port, node, &rstp->ports) {
> + if (port->port_number == port_number) {
> + ovs_mutex_unlock(&mutex);
> + return port;
> + }
> + }
> + }
> + ovs_mutex_unlock(&mutex);
> + return NULL;
> +}
> +
> +/* Updates the port_enabled parameter. */
> +static 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]. */
> +void
> +rstp_port_set_mac_operational(struct rstp_port *p, bool
> new_mac_operational)
> +{
> + struct rstp *rstp;
> +
> + 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);
> +}
> +
> +/* Gets the port MAC_Operational parameter [6.4.2]. */
> +bool
> +rstp_port_get_mac_operational(struct rstp_port *p)
> +{
> + bool value;
> +
> + ovs_mutex_lock(&mutex);
> + value = p->mac_operational;
> + ovs_mutex_unlock(&mutex);
> + return value;
> +}
> +
> +/* Sets the port Administrative Bridge Port parameter. */
> +void
> +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);
> + }
> +}
> +
> +/* Sets the port oper_point_to_point_mac parameter. */
> +void
> +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);
> + }
> +}
> +
> +/* Initializes a port with the defaults values for its parameters. */
> +static void
> +rstp_initialize_port(struct rstp_port *p)
> +OVS_REQUIRES(mutex)
> +{
> + struct rstp *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_priority(p, RSTP_DEFAULT_PORT_PRIORITY);
> + rstp_port_set_port_number(p, 0);
> + 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->aux = NULL;
> + p->uptime = 0;
> +
> + VLOG_DBG("%s: RSTP port "RSTP_PORT_ID_FMT" initialized.", rstp->name,
> + p->port_id);
> +}
> +
> +/* Reinitialization function used in tests. */
> +void
> +reinitialize_port(struct rstp_port *p)
> +{
> + struct rstp_port temp_port;
> + struct rstp *rstp;
> +
> + rstp = p->rstp;
> + temp_port = *p;
> + memset(p, 0, sizeof(struct rstp_port));
> + p->rstp = rstp;
> + p->node = temp_port.node;
> + p->aux = temp_port.aux;
> + p->port_number = temp_port.port_number;
> + p->port_priority = temp_port.port_priority;
> + p->port_id = temp_port.port_id;
> + p->rstp_state = RSTP_DISCARDING;
> +
> + 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_auto_edge(p, true);
> + /* Initialize state machines. */
> + 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: RSTP port "RSTP_PORT_ID_FMT" reinitialized.",
> rstp->name,
> + p->port_id);
> +}
> +
> +/* Sets the port state. */
> +void
> +rstp_port_set_state(struct rstp_port *p, enum rstp_state state)
> +OVS_REQUIRES(mutex)
> +{
> + struct rstp *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;
> + seq_change(connectivity_seq_get());
> + }
> + p->rstp_state = state;
> +}
> +
> +/* Adds a RSTP port. */
> +struct rstp_port *
> +rstp_add_port(struct rstp *rstp) {
> + struct rstp_port *p = xzalloc(sizeof *p);
> +
> + ovs_mutex_lock(&mutex);
> + p->rstp = rstp;
> + rstp_initialize_port(p);
> + rstp_port_set_state(p, RSTP_DISCARDING);
> + list_push_back(&rstp->ports, &p->node);
> + rstp->ports_count++;
> + rstp->changes = true;
> + move_rstp(rstp);
> + ovs_mutex_unlock(&mutex);
> + VLOG_DBG("%s: added port "RSTP_PORT_ID_FMT"", rstp->name, p->port_id);
> + return p;
> +}
> +
> +/* Deletes a RSTP port. */
> +void
> +rstp_delete_port(struct rstp_port *p) {
> + struct rstp *rstp;
> +
> + ovs_mutex_lock(&mutex);
> + rstp = p->rstp;
> + rstp_port_set_state(p, RSTP_DISABLED);
> + list_remove(&p->node);
> + rstp->ports_count--;
> + VLOG_DBG("%s: removed port "RSTP_PORT_ID_FMT"", rstp->name,
> p->port_id);
> + free(p);
> + ovs_mutex_unlock(&mutex);
> +}
> +
> +/* Sets the port Admin Edge parameter. */
> +void
> +rstp_port_set_admin_edge(struct rstp_port *rstp_port, bool new_admin_edge)
> +{
> + struct rstp *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);
> + }
> +}
> +
> +/* Sets the port Auto Edge parameter. */
> +void
> +rstp_port_set_auto_edge(struct rstp_port *rstp_port, bool new_auto_edge)
> +{
> + struct rstp *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);
> + }
> +}
> +
> +/* Sets the port mcheck parameter.
> + * [17.19.13] 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.
> + */
> +void
> +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;
> + }
> + ovs_mutex_unlock(&mutex);
> + VLOG_DBG("%s, port %u: set RSTP mcheck to %d", rstp->name,
> + rstp_port->port_number, new_mcheck);
> +}
> +
> +/* Returns the designated bridge id. */
> +rstp_identifier
> +rstp_get_designated_id(const struct rstp *rstp)
> +{
> + rstp_identifier designated_id;
> +
> + ovs_mutex_lock(&mutex);
> + designated_id = rstp->root_priority.designated_bridge_id;
> + ovs_mutex_unlock(&mutex);
> + return designated_id;
> +}
> +
> +/* Returns the root bridge id. */
> +rstp_identifier
> +rstp_get_root_id(const struct rstp *rstp)
> +{
> + rstp_identifier root_id;
> +
> + ovs_mutex_lock(&mutex);
> + root_id = rstp->root_priority.root_bridge_id;
> + ovs_mutex_unlock(&mutex);
> + return root_id;
> +}
> +
> +/* Returns the designated port id. */
> +uint16_t
> +rstp_get_designated_port_id(const struct rstp *rstp)
> +{
> + uint16_t designated_port_id;
> +
> + ovs_mutex_lock(&mutex);
> + designated_port_id = rstp->root_priority.designated_port_id;
> + ovs_mutex_unlock(&mutex);
> + return designated_port_id;
> +}
> +
> +/* Return the bridge port id. */
> +uint16_t
> +rstp_get_bridge_port_id(const struct rstp *rstp)
> +{
> + uint16_t bridge_port_id;
> +
> + ovs_mutex_lock(&mutex);
> + bridge_port_id = 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);
> + is_root = rstp->bridge_identifier ==
> + rstp->root_priority.designated_bridge_id;
> + ovs_mutex_unlock(&mutex);
> + return is_root;
> +}
> +
> +/* Returns the bridge ID of the bridge currently believed to be the root.
> */
> +rstp_identifier
> +rstp_get_designated_root(const struct rstp *rstp)
> +{
> + rstp_identifier designated_root;
> +
> + ovs_mutex_lock(&mutex);
> + designated_root = rstp->root_priority.designated_bridge_id;
> + 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;
> +
> + ovs_mutex_lock(&mutex);
> + if (rstp->ports_count > 0){
> + LIST_FOR_EACH (p, node, &rstp->ports) {
> + if (p->port_id == rstp->root_port_id) {
> + ovs_mutex_unlock(&mutex);
> + return p;
> + }
> + }
> + }
> + ovs_mutex_unlock(&mutex);
> + return NULL;
> +}
> +
> +/* Returns the port ID for 'p'. */
> +uint16_t
> +rstp_port_get_id(const struct rstp_port *p)
> +{
> + uint16_t port_id;
> +
> + ovs_mutex_lock(&mutex);
> + port_id = 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);
> +}
> +
> +void
> +rstp_port_set_aux(struct rstp_port *p, void *aux)
> +{
> + ovs_mutex_lock(&mutex);
> + p->aux = aux;
> + ovs_mutex_unlock(&mutex);
> +}
> +
> +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)
> + {
> + return (state == RSTP_DISCARDING || state == RSTP_LEARNING ||
> + state == RSTP_FORWARDING);
> + }
> +
> +/* 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)
> +{
> + return (state == RSTP_DISABLED || state == RSTP_FORWARDING);
> +}
> +
> +/* 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)
> +{
> + return (state == RSTP_DISABLED || state == RSTP_LEARNING ||
> + state == RSTP_FORWARDING);
> +}
> +
> +/* 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..916521a
> --- /dev/null
> +++ b/lib/rstp.h
> @@ -0,0 +1,199 @@
> +/*
> + * 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]
> + * These values are expressed in seconds.
> + */
> +#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
> +
> +/* RSTP Bridge identifier [9.2.5]. Top four most significant bits are a
> + * priority value. The next most significant twelve bits are a locally
> + * assigned system ID extension. Bottom 48 bits are MAC address of bridge.
> + */
> +typedef uint64_t rstp_identifier;
> +
> +#define RSTP_ID_FMT "%01"PRIx8".%03"PRIx16".%012"PRIx64
> +#define RSTP_ID_ARGS(rstp_id) \
> + (uint8_t)((rstp_id) >> 60), \
> + (uint16_t)(((rstp_id) & 0x0fff000000000000ULL) >> 48), \
> + (uint64_t)((rstp_id) & 0xffffffffffffULL)
> +
> +#define RSTP_PORT_ID_FMT "%04"PRIx16
> +
> +enum rstp_state {
> + RSTP_DISABLED,
> + RSTP_LEARNING,
> + RSTP_FORWARDING,
> + RSTP_DISCARDING
> +};
> +
> +/* 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;
> +
> +const 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);
> +const char *rstp_port_role_name(enum rstp_port_role);
> +
> +void rstp_init(void);
> +
> +struct rstp * rstp_create(const char *, rstp_identifier bridge_id,
> + void (*send_bpdu)(struct ofpbuf *, int port_no, void *),
> + void *);
> +struct rstp *rstp_ref(struct rstp *);
> +void rstp_unref(struct rstp *);
> +
> +/* Functions used outside RSTP, to call functions defined in
> + rstp-state-machines.h */
> +void rstp_tick_timers(struct rstp *);
> +void 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 **);
> +void rstp_port_set_mac_operational(struct rstp_port *,
> + bool new_mac_operational);
> +bool rstp_port_get_mac_operational(struct rstp_port *);
> +
> +/* Bridge setters */
> +void rstp_set_bridge_address(struct rstp *, rstp_identifier
> bridge_address);
> +void rstp_set_bridge_priority(struct rstp *, int new_priority);
> +void rstp_set_bridge_ageing_time(struct rstp *, int new_ageing_time);
> +void rstp_set_bridge_force_protocol_version(struct rstp *,
> + enum rstp_force_protocol_version
> new_force_protocol_version);
> +void rstp_set_bridge_hello_time(struct rstp *);
> +void rstp_set_bridge_max_age(struct rstp *, int new_max_age);
> +void rstp_set_bridge_forward_delay(struct rstp *, int new_forward_delay);
> +void rstp_set_bridge_transmit_hold_count(struct rstp *,
> + int new_transmit_hold_count);
> +void rstp_set_bridge_migrate_time(struct rstp *);
> +void rstp_set_bridge_times(struct rstp *, int new_forward_delay,
> + int new_hello_time, int new_max_age,
> + int new_message_age);
> +
> +struct rstp_port * rstp_add_port(struct rstp *);
> +void reinitialize_port(struct rstp_port *p);
> +void rstp_delete_port(struct rstp_port *);
> +/* Port setters */
> +void rstp_port_set_priority(struct rstp_port *, int new_port_priority);
> +void rstp_port_set_port_number(struct rstp_port *, uint16_t
> new_port_number);
> +uint32_t rstp_convert_speed_to_cost(unsigned int speed);
> +void rstp_port_set_path_cost(struct rstp_port *, uint32_t
> new_port_path_cost);
> +void rstp_port_set_admin_edge(struct rstp_port *, bool new_admin_edge);
> +void rstp_port_set_auto_edge(struct rstp_port *, bool new_auto_edge);
> +void rstp_port_set_state(struct rstp_port *, enum rstp_state new_state);
> +void rstp_port_set_aux(struct rstp_port *, void *aux);
> +void rstp_port_set_administrative_bridge_port(struct rstp_port *,
> uint8_t);
> +void rstp_port_set_oper_point_to_point_mac(struct rstp_port *, uint8_t);
> +void rstp_port_set_mcheck(struct rstp_port *, bool new_mcheck);
> +
> +/* Bridge getters */
> +const char * rstp_get_name(const struct rstp *);
> +rstp_identifier rstp_get_root_id(const struct rstp *);
> +rstp_identifier rstp_get_bridge_id(const struct rstp *);
> +rstp_identifier rstp_get_designated_id(const struct rstp *);
> +uint32_t rstp_get_root_path_cost(const struct rstp *);
> +uint16_t rstp_get_designated_port_id(const struct rstp *);
> +uint16_t rstp_get_bridge_port_id(const struct rstp *);
> +struct rstp_port * rstp_get_root_port(struct rstp *);
> +rstp_identifier rstp_get_designated_root(const struct rstp *);
> +bool rstp_is_root_bridge(const struct rstp *);
> +
> +/* Port getters */
> +int rstp_port_number(const struct rstp_port *);
> +struct rstp_port *rstp_get_port(struct rstp *, int port_no);
> +uint16_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/lib/stp.h b/lib/stp.h
> index cdc24d4..3d13bba 100644
> --- a/lib/stp.h
> +++ b/lib/stp.h
> @@ -27,11 +27,6 @@
>
> struct ofpbuf;
>
> -/* LLC field values used for STP frames. */
> -#define STP_LLC_SSAP 0x42
> -#define STP_LLC_DSAP 0x42
> -#define STP_LLC_CNTL 0x03
> -
> /* Bridge and port priorities that should be used by default. */
> #define STP_DEFAULT_BRIDGE_PRIORITY 32768
> #define STP_DEFAULT_PORT_PRIORITY 128
> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index 73bb22e..187a3a4 100644
> --- a/ofproto/ofproto-dpif-xlate.c
> +++ b/ofproto/ofproto-dpif-xlate.c
> @@ -86,6 +86,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;
> @@ -149,6 +150,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. */
>
> @@ -353,17 +355,15 @@ static struct xc_entry *xlate_cache_add_entry(struct
> xlate_cache *xc,
> static void xlate_xbridge_init(struct xlate_cfg *, struct xbridge *);
> static void xlate_xbundle_init(struct xlate_cfg *, struct xbundle *);
> static void xlate_xport_init(struct xlate_cfg *, struct xport *);
> -static void xlate_xbridge_set(struct xbridge *xbridge,
> - struct dpif *dpif,
> +static void xlate_xbridge_set(struct xbridge *, struct dpif *,
> struct rule_dpif *miss_rule,
> struct rule_dpif *no_packet_in_rule,
> - const struct mac_learning *ml, struct stp
> *stp,
> - const struct mcast_snooping *ms,
> - const struct mbridge *mbridge,
> - const struct dpif_sflow *sflow,
> - const struct dpif_ipfix *ipfix,
> - const struct netflow *netflow,
> - enum ofp_config_flags frag,
> + const struct mac_learning *, struct stp *,
> + struct rstp *, const struct mcast_snooping
> *,
> + const struct mbridge *,
> + const struct dpif_sflow *,
> + const struct dpif_ipfix *,
> + const struct netflow *, enum
> ofp_config_flags,
> bool forward_bpdu, bool has_in_band,
> bool enable_recirc,
> bool variable_length_userdata,
> @@ -376,6 +376,7 @@ static void xlate_xbundle_set(struct xbundle *xbundle,
> static void xlate_xport_set(struct xport *xport, odp_port_t odp_port,
> const struct netdev *netdev, const struct cfm
> *cfm,
> const struct bfd *bfd, int stp_port_no,
> + int rstp_port_no,
> enum ofputil_port_config config,
> enum ofputil_port_state state, bool is_tunnel,
> bool may_enable);
> @@ -423,7 +424,7 @@ xlate_xbridge_set(struct xbridge *xbridge,
> struct rule_dpif *miss_rule,
> struct rule_dpif *no_packet_in_rule,
> const struct mac_learning *ml, struct stp *stp,
> - const struct mcast_snooping *ms,
> + struct rstp *rstp, const struct mcast_snooping *ms,
> const struct mbridge *mbridge,
> const struct dpif_sflow *sflow,
> const struct dpif_ipfix *ipfix,
> @@ -463,6 +464,11 @@ xlate_xbridge_set(struct xbridge *xbridge,
> 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);
> @@ -508,13 +514,14 @@ xlate_xbundle_set(struct xbundle *xbundle,
> static void
> xlate_xport_set(struct xport *xport, odp_port_t odp_port,
> const struct netdev *netdev, const struct cfm *cfm,
> - const struct bfd *bfd, int stp_port_no,
> + const struct bfd *bfd, int stp_port_no, int rstp_port_no,
> enum ofputil_port_config config, enum ofputil_port_state
> state,
> bool is_tunnel, bool may_enable)
> {
> 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;
> @@ -548,10 +555,11 @@ xlate_xbridge_copy(struct xbridge *xbridge)
> xlate_xbridge_set(new_xbridge,
> xbridge->dpif, xbridge->miss_rule,
> xbridge->no_packet_in_rule, xbridge->ml,
> xbridge->stp,
> - xbridge->ms, xbridge->mbridge, xbridge->sflow,
> - xbridge->ipfix, xbridge->netflow, xbridge->frag,
> - xbridge->forward_bpdu, xbridge->has_in_band,
> - xbridge->enable_recirc,
> xbridge->variable_length_userdata,
> + xbridge->rstp, xbridge->ms, xbridge->mbridge,
> + xbridge->sflow, xbridge->ipfix, xbridge->netflow,
> + xbridge->frag, xbridge->forward_bpdu,
> + xbridge->has_in_band, xbridge->enable_recirc,
> + xbridge->variable_length_userdata,
> xbridge->max_mpls_depth);
> LIST_FOR_EACH (xbundle, list_node, &xbridge->xbundles) {
> xlate_xbundle_copy(new_xbridge, xbundle);
> @@ -596,8 +604,9 @@ xlate_xport_copy(struct xbridge *xbridge, struct
> xbundle *xbundle,
> xlate_xport_init(new_xcfg, new_xport);
>
> xlate_xport_set(new_xport, xport->odp_port, xport->netdev, xport->cfm,
> - xport->bfd, xport->stp_port_no, xport->config,
> xport->state,
> - xport->is_tunnel, xport->may_enable);
> + xport->bfd, xport->stp_port_no, xport->rstp_port_no,
> + xport->config, xport->state, xport->is_tunnel,
> + xport->may_enable);
>
> if (xport->peer) {
> struct xport *peer = xport_lookup(new_xcfg, xport->peer->ofport);
> @@ -698,15 +707,13 @@ 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,
> - const struct mcast_snooping *ms,
> + struct rstp *rstp, const struct mcast_snooping *ms,
> const struct mbridge *mbridge,
> const struct dpif_sflow *sflow,
> const struct dpif_ipfix *ipfix,
> const struct netflow *netflow, enum ofp_config_flags
> frag,
> - bool forward_bpdu, bool has_in_band,
> - bool enable_recirc,
> - bool variable_length_userdata,
> - size_t max_mpls_depth)
> + bool forward_bpdu, bool has_in_band, bool enable_recirc,
> + bool variable_length_userdata, size_t max_mpls_depth)
> {
> struct xbridge *xbridge;
>
> @@ -724,9 +731,9 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const
> char *name,
> xbridge->name = xstrdup(name);
>
> xlate_xbridge_set(xbridge, dpif, miss_rule, no_packet_in_rule, ml,
> stp,
> - ms, mbridge, sflow, ipfix, netflow, frag,
> forward_bpdu,
> - has_in_band, enable_recirc,
> variable_length_userdata,
> - max_mpls_depth);
> + rstp, ms, mbridge, sflow, ipfix, netflow, frag,
> + forward_bpdu, has_in_band, enable_recirc,
> + variable_length_userdata, max_mpls_depth);
> }
>
> static void
> @@ -754,6 +761,7 @@ xlate_xbridge_remove(struct xlate_cfg *xcfg, struct
> xbridge *xbridge)
> 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);
> @@ -835,7 +843,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,
> @@ -858,8 +866,8 @@ xlate_ofport_set(struct ofproto_dpif *ofproto, struct
> ofbundle *ofbundle,
>
> ovs_assert(xport->ofp_port == ofp_port);
>
> - xlate_xport_set(xport, odp_port, netdev, cfm, bfd, stp_port_no,
> config,
> - state, is_tunnel, may_enable);
> + xlate_xport_set(xport, odp_port, netdev, cfm, bfd, stp_port_no,
> + rstp_port_no, config, state, is_tunnel, may_enable);
>
> if (xport->peer) {
> xport->peer->peer = NULL;
> @@ -1113,6 +1121,58 @@ 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 !rp || rstp_learn_in_state(rstp_port_get_state(rp));
> +}
> +
> +static bool
> +xport_rstp_forward_state(const struct xport *xport)
> +{
> + struct rstp_port *rp = xport_get_rstp_port(xport);
> + return !rp || rstp_forward_in_state(rstp_port_get_state(rp));
> +}
> +
> +static bool
> +xport_rstp_should_manage_bpdu(const struct xport *xport)
> +{
> + struct rstp_port *rp = xport_get_rstp_port(xport);
> + return rp && rstp_should_manage_bpdu(rstp_port_get_state(rp));
> +}
> +
> +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 = ofpbuf_data(&payload);
> +
> + /* 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 (ofpbuf_size(&payload) > ntohs(eth->eth_type) + ETH_HEADER_LEN) {
> + ofpbuf_set_size(&payload, ntohs(eth->eth_type) + ETH_HEADER_LEN);
> + }
> +
> + if (ofpbuf_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) {
> + rstp_received_bpdu(rp, ofpbuf_data(&payload),
> ofpbuf_size(&payload));
> + }
> +}
> +
> static struct xport *
> get_ofp_port(const struct xbridge *xbridge, ofp_port_t ofp_port)
> {
> @@ -2347,9 +2407,11 @@ process_special(struct xlate_ctx *ctx, const struct
> flow *flow,
> lacp_process_packet(xport->xbundle->lacp, xport->ofport,
> packet);
> }
> return SLOW_LACP;
> - } else if (xbridge->stp && stp_should_process_flow(flow, wc)) {
> + } else if ((xbridge->stp || xbridge->rstp) &&
> + stp_should_process_flow(flow, wc)) {
> if (packet) {
> - stp_process_packet(xport, packet);
> + xbridge->stp ? stp_process_packet(xport, packet) :
> + rstp_process_packet(xport, packet);
> }
> return SLOW_STP;
> } else {
> @@ -2382,14 +2444,26 @@ compose_output_action__(struct xlate_ctx *ctx,
> ofp_port_t ofp_port,
> return;
> } else if (check_stp) {
> if (is_stp(&ctx->base_flow)) {
> - if (!xport_stp_should_forward_bpdu(xport)) {
> - xlate_report(ctx, "STP not in listening state, "
> - "skipping bpdu output");
> + if (!xport_stp_should_forward_bpdu(xport) &&
> + !xport_rstp_should_manage_bpdu(xport)) {
> + if (ctx->xbridge->stp != NULL) {
> + xlate_report(ctx, "STP not in listening state, "
> + "skipping bpdu output");
> + } else if (ctx->xbridge->rstp != NULL) {
> + xlate_report(ctx, "RSTP not managing BPDU in this
> state, "
> + "skipping bpdu output");
> + }
> return;
> }
> - } else if (!xport_stp_forward_state(xport)) {
> - xlate_report(ctx, "STP not in forwarding state, "
> - "skipping output");
> + } else if (!xport_stp_forward_state(xport) ||
> + !xport_rstp_forward_state(xport)) {
> + if (ctx->xbridge->stp != NULL) {
> + xlate_report(ctx, "STP not in forwarding state, "
> + "skipping output");
> + } else if (ctx->xbridge->rstp != NULL) {
> + xlate_report(ctx, "RSTP not in forwarding state, "
> + "skipping output");
> + }
> return;
> }
> }
> @@ -2415,11 +2489,11 @@ compose_output_action__(struct xlate_ctx *ctx,
> ofp_port_t ofp_port,
> if (special) {
> ctx->xout->slow |= special;
> } else if (may_receive(peer, ctx)) {
> - if (xport_stp_forward_state(peer)) {
> + if (xport_stp_forward_state(peer) &&
> 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 = ofpbuf_size(ctx->xout->odp_actions);
> mirror_mask_t old_mirrors = ctx->xout->mirrors;
> @@ -3384,7 +3458,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;
> }
>
> @@ -4106,7 +4181,8 @@ xlate_actions(struct xlate_in *xin, struct xlate_out
> *xout)
>
> /* We've let OFPP_NORMAL and the learning action look at the
> * packet, so drop it now if forwarding is disabled. */
> - if (in_port && !xport_stp_forward_state(in_port)) {
> + if (in_port && (!xport_stp_forward_state(in_port) ||
> + !xport_rstp_forward_state(in_port))) {
> ofpbuf_set_size(ctx.xout->odp_actions,
> sample_actions_len);
> }
> }
> diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h
> index 7394249..9962629 100644
> --- a/ofproto/ofproto-dpif-xlate.h
> +++ b/ofproto/ofproto-dpif-xlate.h
> @@ -147,7 +147,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 *,
> - const struct mcast_snooping *,
> + struct rstp *, const struct mcast_snooping *,
> const struct mbridge *, const struct dpif_sflow *,
> const struct dpif_ipfix *, const struct netflow *,
> enum ofp_config_flags, bool forward_bpdu,
> @@ -167,7 +167,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);
> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> index 4e987bc..ace96b1 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -145,6 +145,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 void 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;
> @@ -165,6 +169,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;
> @@ -224,6 +232,7 @@ static void ofport_update_peer(struct ofport_dpif *);
> 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);
> @@ -302,6 +312,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.
> */
> @@ -575,6 +589,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;
> @@ -595,8 +610,8 @@ 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->ms, ofproto->mbridge,
> - ofproto->sflow, ofproto->ipfix,
> + ofproto->stp, ofproto->rstp, ofproto->ms,
> + ofproto->mbridge, ofproto->sflow,
> ofproto->ipfix,
> ofproto->netflow, ofproto->up.frag_handling,
> ofproto->up.forward_bpdu,
> connmgr_has_in_band(ofproto->up.connmgr),
> @@ -616,11 +631,14 @@ 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_number(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,
> - ofport->qdscp, ofport->n_qdscp,
> + rstp_port, ofport->qdscp,
> ofport->n_qdscp,
> ofport->up.pp.config,
> ofport->up.pp.state,
> ofport->is_tunnel, ofport->may_enable);
> }
> @@ -1129,6 +1147,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);
> @@ -1393,6 +1412,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;
> @@ -1550,6 +1570,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;
> @@ -1660,6 +1682,9 @@ port_destruct(struct ofport *port_)
> if (port->stp_port) {
> stp_port_disable(port->stp_port);
> }
> + if (port->rstp_port) {
> + rstp_delete_port(port->rstp_port);
> + }
> if (ofproto->sflow) {
> dpif_sflow_del_port(ofproto->sflow, port->odp_port);
> }
> @@ -1883,6 +1908,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 = ofpbuf_l2(pkt);
> +
> + 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_;
> @@ -1907,6 +1957,138 @@ 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 void
> +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, s->address,
> + rstp_send_bpdu_cb, ofproto);
> + ofproto->rstp_last_tick = time_msec();
> + }
> + rstp_set_bridge_address(ofproto->rstp, 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;
> + }
> +}
> +
> +static void
> +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;
> + s->root_id = rstp_get_root_id(ofproto->rstp);
> + s->bridge_id = rstp_get_bridge_id(ofproto->rstp);
> + s->designated_id = rstp_get_designated_id(ofproto->rstp);
> + s->root_path_cost = rstp_get_root_path_cost(ofproto->rstp);
> + s->designated_port_id =
> rstp_get_designated_port_id(ofproto->rstp);
> + s->bridge_port_id = rstp_get_bridge_port_id(ofproto->rstp);
> + } else {
> + s->enabled = false;
> + }
> +}
> +
> +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);
> + 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)
> @@ -2125,6 +2307,94 @@ 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 void
> +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 stp_port;
> +
> + if (!s || !s->enable) {
> + if (rp) {
> + ofport->rstp_port = NULL;
> + rstp_delete_port(rp);
> + update_rstp_port_state(ofport);
> + }
> + return;
> + } else if (rp && rstp_port_number(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. */
> + if (s->port_num != 0) {
> + xlate_txn_start();
> + stp_port = ofport->stp_port ? stp_port_no(ofport->stp_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,
> + s->port_num,
> + ofport->qdscp, ofport->n_qdscp,
> + ofport->up.pp.config, ofport->up.pp.state,
> + ofport->is_tunnel, ofport->may_enable);
> + xlate_txn_commit();
> + }
> +
> + 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;
> + }
> + rp = ofport->rstp_port = rstp_get_port(ofproto->rstp, s->port_num);
> + /* Enable RSTP on port */
> + if (!rp) {
> + rp = ofport->rstp_port = rstp_add_port(ofproto->rstp);
> + }
> + /* 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);
> +}
> +
> +static void
> +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;
> + }
> +
> + s->enabled = true;
> + s->port_id = rstp_port_get_id(rp);
> + 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);
> +}
> +
>
> static int
> set_queues(struct ofport *ofport_, const struct ofproto_port_queue *qdscp,
> @@ -2862,6 +3132,12 @@ port_run(struct ofport_dpif *ofport)
> }
>
> ofport->may_enable = enable;
> +
> + if (ofport->rstp_port) {
> + if (rstp_port_get_mac_operational(ofport->rstp_port) != enable) {
> + rstp_port_set_mac_operational(ofport->rstp_port, enable);
> + }
> + }
> }
>
> static int
> @@ -5140,6 +5416,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 d490679..de354ec 100644
> --- a/ofproto/ofproto-provider.h
> +++ b/ofproto/ofproto-provider.h
> @@ -1417,6 +1417,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. */
> + void (*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. */
> + void (*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. */
> + void (*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. */
> + void (*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 4db6fec..5ced8b5 100644
> --- a/ofproto/ofproto.c
> +++ b/ofproto/ofproto.c
> @@ -911,6 +911,87 @@ 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)
> +{
> + if (!ofproto->ofproto_class->set_rstp) {
> + return EOPNOTSUPP;
> + }
> + ofproto->ofproto_class->set_rstp(ofproto, s);
> + return 0;
> +}
> +
> +/* 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)
> +{
> + if (!ofproto->ofproto_class->get_rstp_status) {
> + return EOPNOTSUPP;
> + }
> + ofproto->ofproto_class->get_rstp_status(ofproto, s);
> + return 0;
> +}
> +
> +/* 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;
> + }
> +
> + if (!ofproto->ofproto_class->set_rstp_port) {
> + return EOPNOTSUPP;
> + }
> + ofproto->ofproto_class->set_rstp_port(ofport, s);
> + return 0;
> +}
> +
> +/* 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;
> + }
> +
> + if (!ofproto->ofproto_class->get_rstp_port_status) {
> + return EOPNOTSUPP;
> + }
> + ofproto->ofproto_class->get_rstp_port_status(ofport, s);
> + return 0;
> +}
>
> /* Queue DSCP configuration. */
>
> @@ -2191,6 +2272,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 c51af16..1b8709a 100644
> --- a/ofproto/ofproto.h
> +++ b/ofproto/ofproto.h
> @@ -27,6 +27,7 @@
> #include "flow.h"
> #include "meta-flow.h"
> #include "netflow.h"
> +#include "rstp.h"
> #include "smap.h"
> #include "sset.h"
> #include "stp.h"
> @@ -83,6 +84,47 @@ struct ofproto_ipfix_flow_exporter_options {
> uint32_t cache_max_flows;
> };
>
> +struct ofproto_rstp_status {
> + bool enabled; /* If false, ignore other members. */
> + rstp_identifier root_id;
> + rstp_identifier bridge_id;
> + rstp_identifier designated_id;
> + uint32_t root_path_cost;
> + uint16_t designated_port_id;
> + uint16_t bridge_port_id;
> +};
> +
> +struct ofproto_rstp_settings {
> + rstp_identifier address;
> + uint16_t priority;
> + uint32_t ageing_time;
> + enum rstp_force_protocol_version force_protocol_version;
> + uint16_t bridge_forward_delay;
> + uint16_t bridge_max_age;
> + uint16_t transmit_hold_count;
> +};
> +
> +struct ofproto_port_rstp_status {
> + bool enabled; /* If false, ignore other members. */
> + uint16_t port_id;
> + 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;
> @@ -271,6 +313,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);
>
> @@ -292,6 +337,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/.gitignore b/tests/.gitignore
> index 5b55a7d..ddc46dd 100644
> --- a/tests/.gitignore
> +++ b/tests/.gitignore
> @@ -33,6 +33,7 @@
> /test-packets
> /test-random
> /test-reconnect
> +/test-rstp
> /test-sflow
> /test-sha1
> /test-stp
> diff --git a/tests/automake.mk b/tests/automake.mk
> index 2b88ad2..405e403 100644
> --- a/tests/automake.mk
> +++ b/tests/automake.mk
> @@ -65,6 +65,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
> @@ -123,6 +124,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 \
> @@ -239,6 +241,7 @@ tests_ovstest_SOURCES = \
> tests/test-packets.c \
> tests/test-random.c \
> tests/test-reconnect.c \
> + tests/test-rstp.c \
> tests/test-sflow.c \
> tests/test-sha1.c \
> tests/test-stp.c \
> diff --git a/tests/ovs-vsctl.at b/tests/ovs-vsctl.at
> index 135f768..8931d92 100644
> --- a/tests/ovs-vsctl.at
> +++ b/tests/ovs-vsctl.at
> @@ -658,6 +658,8 @@ netflow : []
> other_config : {}
> ports : []
> protocols : []
> +rstp_enable : false
> +rstp_status : {}
> sflow : []
> status : {}
> stp_enable : false
> @@ -1151,6 +1153,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..0818332
> --- /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([ovstest 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([ovstest 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([ovstest 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([ovstest 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([ovstest 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([ovstest 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..ad71f04
> --- /dev/null
> +++ b/tests/test-rstp.c
> @@ -0,0 +1,684 @@
> +#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 "ovstest.h"
> +#include "packets.h"
> +#include "vlog.h"
> +
> +#define MAX_PORTS 10
> +
> +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;
> + int n_active_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_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];
> + struct rstp_port *p;
> + int i;
> +
> + b->tc = tc;
> + b->id = id;
> + snprintf(name, sizeof name, "rstp%x", id);
> + b->rstp = rstp_create(name, id, send_bpdu, b);
> + for (i = 1; i < MAX_PORTS; i++) {
> + p = rstp_add_port(b->rstp);
> + rstp_port_set_aux(p, b);
> + rstp_port_set_state(p, RSTP_DISABLED);
> + rstp_port_set_mac_operational(p, true);
> + }
> +
> + assert(tc->n_bridges < ARRAY_SIZE(tc->bridges));
> + b->n_ports = 1;
> + b->n_active_ports = 1;
> + 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;
> + /* Enable port. */
> + reinitialize_port(p);
> + rstp_port_set_path_cost(p, path_cost);
> + rstp_port_set_state(p, RSTP_DISCARDING);
> + rstp_port_set_mac_operational(p, true);
> + 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=%u)",
> + rstp_get_root_path_cost(rstp));
> + }
> + 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);
> + }
> +}
> +
> +static void
> +test_rstp_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 = 1; port_no < MAX_PORTS; port_no++) {
> + struct rstp_port *p = rstp_get_port(bridge->rstp,
> port_no);
> + if (!token || match("X")) {
> + /* Disable port. */
> + reinitialize_port(p);
> + rstp_port_set_state(p, RSTP_DISABLED);
> + rstp_port_set_mac_operational(p, false);
> + } 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) {
> + /* Enable port. */
> + reinitialize_port(p);
> + rstp_port_set_path_cost(p, path_cost);
> + rstp_port_set_state(p, RSTP_DISCARDING);
> + rstp_port_set_mac_operational(p, true);
> + reconnect_port(bridge, port_no, lan);
> + } else if (port_no == bridge->n_ports) {
> + new_port(bridge, lan, path_cost);
> + bridge->n_active_ports++;
> + } 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;
> +
> + 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 (rstp_get_designated_root(rstp) != rootid) {
> + warn("%s: root "RSTP_ID_FMT", not %"PRIx64,
> + rstp_get_name(rstp),
> + RSTP_ID_ARGS(rstp_get_designated_root(rstp)),
> + rootid);
> + }
> + }
> + cost_value = rstp_get_root_path_cost(rstp);
> + 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 "RSTP_ID_FMT", not "RSTP_ID_FMT"",
> + rstp_get_name(rstp),
> + RSTP_ID_ARGS(rstp_get_designated_root(rstp)),
> + RSTP_ID_ARGS(rstp_get_bridge_id(rstp)));
> + }
> + for (port_no = 1; port_no < b->n_active_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 && state !=
> 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 = 1; port_no < b->n_active_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_number(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];
> + int j;
> + for (j = 1; j < MAX_PORTS; j++) {
> + rstp_delete_port(rstp_get_port(bridge->rstp, j));
> + }
> + rstp_unref(bridge->rstp);
> + free(bridge);
> + }
> + free(tc);
> +}
> +
> +OVSTEST_REGISTER("test-rstp", test_rstp_main);
> diff --git a/tests/testsuite.at b/tests/testsuite.at
> index 74e5c6d..8712277 100644
> --- a/tests/testsuite.at
> +++ b/tests/testsuite.at
> @@ -157,5 +157,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 a68b0bc..8cf13ae 100644
> --- a/utilities/ovs-vsctl.8.in
> +++ b/utilities/ovs-vsctl.8.in
> @@ -1002,6 +1002,85 @@ Deconfigure multicasting snooping from above:
> .IP
> .B "ovs\-vsctl set Bridge br0 mcast_snooping_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 7631cd4..ba2eb79 100644
> --- a/vswitchd/bridge.c
> +++ b/vswitchd/bridge.c
> @@ -229,6 +229,7 @@ static void bridge_configure_mcast_snooping(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 *,
> @@ -379,9 +380,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);
>
> @@ -444,6 +450,7 @@ bridge_init(const char *remote)
> cfm_init();
> ovs_numa_init();
> stp_init();
> + rstp_init();
> }
>
> void
> @@ -627,6 +634,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);
> }
> @@ -1302,12 +1310,107 @@ 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)
> +{
> + const char *config_str;
> + struct iface *iface;
> +
> + 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;
> + }
> + 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, use 0.
> rstp_port_set_port_number
> + * will look for the first free one.
> + */
> + port_s->port_num = 0;
> + }
> +
> + 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;
> @@ -1397,6 +1500,112 @@ 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;
> +
> + 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)) {
> + br_s.address = eth_addr_to_uint64(ea);
> + }
> + else {
> + br_s.address = eth_addr_to_uint64(br->ea);
> + VLOG_ERR("bridge %s: invalid rstp-address, defaulting "
> + "to "ETH_ADDR_FMT, br->name,
> ETH_ADDR_ARGS(br->ea));
> + }
> + }
> + else {
> + br_s.address = eth_addr_to_uint64(br->ea);
> + }
> +
> + 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;
> + 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);
> +
> + /* 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)
> {
> @@ -2208,6 +2417,90 @@ port_refresh_stp_stats(struct port *port)
> ARRAY_SIZE(int_values));
> }
>
> +static void
> +br_refresh_rstp_status(struct bridge *br)
> +{
> + 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;
> + }
> + smap_add_format(&smap, "rstp_bridge_id", RSTP_ID_FMT,
> + RSTP_ID_ARGS(status.bridge_id));
> + smap_add_format(&smap, "rstp_root_path_cost", "%d",
> + status.root_path_cost);
> + smap_add_format(&smap, "rstp_root_id", RSTP_ID_FMT,
> + RSTP_ID_ARGS(status.root_id));
> + smap_add_format(&smap, "rstp_designated_id", RSTP_ID_FMT,
> + RSTP_ID_ARGS(status.designated_id));
> + smap_add_format(&smap, "rstp_designated_port_id", RSTP_PORT_ID_FMT,
> + status.designated_port_id);
> + smap_add_format(&smap, "rstp_bridge_port_id", RSTP_PORT_ID_FMT,
> + status.bridge_port_id);
> + 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;
> +
> + 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);
> +
> + smap_add_format(&smap, "rstp_port_id", RSTP_PORT_ID_FMT,
> + status.port_id);
> + smap_add_format(&smap, "rstp_port_role", "%s",
> + rstp_port_role_name(status.role));
> + smap_add_format(&smap, "rstp_port_state", "%s",
> + rstp_state_name(status.state));
> +
> + 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)
> {
> @@ -2494,10 +2787,12 @@ bridge_run(void)
> struct port *port;
>
> br_refresh_stp_status(br);
> + br_refresh_rstp_status(br);
> HMAP_FOR_EACH (port, hmap_node, &br->ports) {
> struct iface *iface;
>
> port_refresh_stp_status(port);
> + port_refresh_rstp_status(port);
> LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
> iface_refresh_netdev_status(iface);
> iface_refresh_ofproto_status(iface);
> diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema
> index bf86f20..3d78f1d 100644
> --- a/vswitchd/vswitch.ovsschema
> +++ b/vswitchd/vswitch.ovsschema
> @@ -1,6 +1,6 @@
> {"name": "Open_vSwitch",
> - "version": "7.8.0",
> - "cksum": "4147598271 20869",
> + "version": "7.9.0",
> + "cksum": "2301439325 21345",
> "tables": {
> "Open_vSwitch": {
> "columns": {
> @@ -54,6 +54,8 @@
> "ephemeral": true},
> "stp_enable": {
> "type": "boolean"},
> + "rstp_enable": {
> + "type": "boolean"},
> "mcast_snooping_enable": {
> "type": "boolean"},
> "ports": {
> @@ -96,6 +98,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": {
> @@ -162,6 +167,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 6c49250..42dad8a 100644
> --- a/vswitchd/vswitch.xml
> +++ b/vswitchd/vswitch.xml
> @@ -692,6 +692,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.7.10.4
>
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev
>
More information about the dev
mailing list