mirror of
https://github.com/openvswitch/ovs
synced 2025-08-31 22:35:15 +00:00
Rapid Spanning Tree Protocol (IEEE 802.1D).
This is the v5 from June 12th, 2014, rebased to OVS master, further changes in following patches. Signed-off by: Daniele Venturino <daniele.venturino@m3s.it> Signed-off by: Martino Fornasa <mf@fornasa.it> Signed-off-by: Jarno Rajahalme <jrajahalme@nicira.com> Acked-by: Daniele Venturino <daniele.venturino@m3s.it>
This commit is contained in:
committed by
Jarno Rajahalme
parent
3c33f0ffe7
commit
9efd308e95
1
AUTHORS
1
AUTHORS
@@ -95,6 +95,7 @@ Madhu Challa challa@noironetworks.com
|
||||
Mark Hamilton mhamilton@nicira.com
|
||||
Mark Maglana mmaglana@gmail.com
|
||||
Martin Casado casado@nicira.com
|
||||
Martino Fornasa mf@fornasa.it
|
||||
Maryam Tahhan maryam.tahhan@intel.com
|
||||
Mehak Mahajan mmahajan@nicira.com
|
||||
Murphy McCauley murphy.mccauley@gmail.com
|
||||
|
3
NOTICE
3
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
|
||||
|
@@ -189,6 +189,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 \
|
||||
|
@@ -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
|
||||
|
884
lib/rstp-common.h
Normal file
884
lib/rstp-common.h
Normal file
@@ -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@fornasa.it>
|
||||
* Daniele Venturino <daniele.venturino@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 */
|
2036
lib/rstp-state-machines.c
Normal file
2036
lib/rstp-state-machines.c
Normal file
File diff suppressed because it is too large
Load Diff
42
lib/rstp-state-machines.h
Normal file
42
lib/rstp-state-machines.h
Normal file
@@ -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@fornasa.it>
|
||||
* Daniele Venturino <daniele.venturino@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 */
|
1236
lib/rstp.c
Normal file
1236
lib/rstp.c
Normal file
File diff suppressed because it is too large
Load Diff
199
lib/rstp.h
Normal file
199
lib/rstp.h
Normal file
@@ -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@fornasa.it>
|
||||
* Daniele Venturino <daniele.venturino@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 */
|
@@ -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
|
||||
|
@@ -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;
|
||||
@@ -1148,6 +1156,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)
|
||||
{
|
||||
@@ -2382,9 +2442,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 {
|
||||
@@ -2417,14 +2479,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;
|
||||
}
|
||||
}
|
||||
@@ -2450,11 +2524,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;
|
||||
@@ -3428,7 +3502,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;
|
||||
}
|
||||
|
||||
@@ -4150,7 +4225,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);
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
@@ -1551,6 +1571,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;
|
||||
@@ -1661,6 +1683,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_)
|
||||
{
|
||||
@@ -1908,6 +1958,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)
|
||||
@@ -2126,6 +2308,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,
|
||||
@@ -2863,6 +3133,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
|
||||
@@ -5128,6 +5404,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,
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
1
tests/.gitignore
vendored
1
tests/.gitignore
vendored
@@ -31,6 +31,7 @@
|
||||
/test-packets
|
||||
/test-random
|
||||
/test-reconnect
|
||||
/test-rstp
|
||||
/test-sflow
|
||||
/test-sha1
|
||||
/test-stp
|
||||
|
@@ -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 \
|
||||
@@ -232,6 +234,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 \
|
||||
|
@@ -658,6 +658,8 @@ netflow : []
|
||||
other_config : {}
|
||||
ports : []
|
||||
protocols : []
|
||||
rstp_enable : false
|
||||
rstp_status : {}
|
||||
sflow : []
|
||||
status : {}
|
||||
stp_enable : false
|
||||
@@ -1154,6 +1156,8 @@ netflow : []
|
||||
other_config : {}
|
||||
ports : []
|
||||
protocols : []
|
||||
rstp_enable : false
|
||||
rstp_status : {}
|
||||
sflow : []
|
||||
status : {}
|
||||
stp_enable : false
|
||||
|
149
tests/rstp.at
Normal file
149
tests/rstp.at
Normal file
@@ -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
|
684
tests/test-rstp.c
Normal file
684
tests/test-rstp.c
Normal file
@@ -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);
|
@@ -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])
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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},
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user