2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-31 14:25:26 +00:00
Files
ovs/lib/rstp-state-machines.c
Jarno Rajahalme f025bcb7ea lib/rstp: More robust thread safety.
Current code expects there to be a single thread that is responsible
for creating rstp and creating and deleting rstp_port objects.  rstp
objects are also deleted from other threads, as managed by reference
counting.

rstp port objects are not reference counted, which means that
references to rstp ports may only be held while holding the rstp
mutex, or by the thread that creates and deletes them.

This patch adds reference counting to RSTP ports, which allows ports
to be passed from ofproto-dpif to ofproto-dpif-xlate without using the
RSTP port number.  This simplifies RSTP port reconfiguration, as the
port need not be resynchronized with xlate if just the port number
changes.  This also avoids lookups on the processing of RSTP BPDUs.

This patch also:

1. Exposes the rstp mutex so that related thread safety annotations
   can be used also within rstp-state-machines.c.

2. Internal variants of most setter an getter functions are defined,
   suffixed with two underscores.  These are annotated to be callable
   only when the mutex is held.

3. Port setters were only called in a specific pattern.  The new external
   port setter combines them in a single rspt_port_set() function.

Signed-off-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Daniele Venturino <daniele.venturino@m3s.it>
2014-09-09 11:45:44 -07:00

2097 lines
70 KiB
C

/*
* Copyright (c) 2011-2014 M3S, Srl - Italy
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Rapid Spanning Tree Protocol (IEEE 802.1D-2004) state machines
* implementation.
*
* Authors:
* Martino Fornasa <mf@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.
*
*/
#include <config.h>
#include "rstp.h"
#include "rstp-state-machines.h"
#include <arpa/inet.h>
#include <inttypes.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <sys/types.h>
#include "byte-order.h"
#include "connectivity.h"
#include "ofpbuf.h"
#include "packets.h"
#include "seq.h"
#include "unixctl.h"
#include "util.h"
#include "vlog.h"
VLOG_DEFINE_THIS_MODULE(rstp_sm);
#define ROLE_FLAG_MASK 0xC
#define ROLE_FLAG_SHIFT 2
enum port_flag {
PORT_UNKN = 0,
PORT_ALT_BACK = 1,
PORT_ROOT = 2,
PORT_DES = 3
};
enum bpdu_size {
CONFIGURATION_BPDU_SIZE = 35,
TOPOLOGY_CHANGE_NOTIFICATION_BPDU_SIZE = 4,
RAPID_SPANNING_TREE_BPDU_SIZE = 36
};
enum vector_comparison {
INFERIOR = 0,
SUPERIOR = 1,
SAME = 2
};
static void decrement_timer(uint16_t *);
static void rstp_send_bpdu(struct rstp_port *, const void *, size_t)
OVS_REQUIRES(rstp_mutex);
static int validate_received_bpdu(struct rstp_port *, const void *, size_t)
OVS_REQUIRES(rstp_mutex);
static ovs_be16 time_encode(uint8_t);
static uint8_t time_decode(ovs_be16);
static enum vector_comparison
compare_rstp_priority_vector(struct rstp_priority_vector *,
struct rstp_priority_vector *);
static bool rstp_times_equal(struct rstp_times *, struct rstp_times *);
/* Per-Bridge State Machine */
static int port_role_selection_sm(struct rstp *)
OVS_REQUIRES(rstp_mutex);
/* Per-Port State Machines */
static int port_receive_sm(struct rstp_port *)
OVS_REQUIRES(rstp_mutex);
static int port_protocol_migration_sm(struct rstp_port *)
OVS_REQUIRES(rstp_mutex);
static int bridge_detection_sm(struct rstp_port *)
OVS_REQUIRES(rstp_mutex);
static int port_transmit_sm(struct rstp_port *)
OVS_REQUIRES(rstp_mutex);
static int port_information_sm(struct rstp_port *)
OVS_REQUIRES(rstp_mutex);
static int port_role_transition_sm(struct rstp_port *)
OVS_REQUIRES(rstp_mutex);
static int port_state_transition_sm(struct rstp_port *)
OVS_REQUIRES(rstp_mutex);
static int topology_change_sm(struct rstp_port *)
OVS_REQUIRES(rstp_mutex);
/* port_timers_sm() not defined as a state machine */
void
process_received_bpdu__(struct rstp_port *p, const void *bpdu,
size_t bpdu_size)
OVS_REQUIRES(rstp_mutex)
{
struct rstp *rstp = p->rstp;
if (!p->port_enabled)
return;
if (p->rcvd_bpdu)
return;
if (validate_received_bpdu(p, bpdu, bpdu_size) == 0) {
p->rcvd_bpdu = true;
p->rx_rstp_bpdu_cnt++;
memcpy(&p->received_bpdu_buffer, bpdu, sizeof(struct rstp_bpdu));
rstp->changes = true;
move_rstp__(rstp);
} else {
VLOG_DBG("%s, port %u: Bad STP or RSTP BPDU received", p->rstp->name,
p->port_number);
p->error_count++;
}
}
static int
validate_received_bpdu(struct rstp_port *p, const void *bpdu, size_t bpdu_size)
OVS_REQUIRES(rstp_mutex)
{
/* Validation of received BPDU, see [9.3.4]. */
const struct rstp_bpdu *temp;
temp = bpdu;
if (bpdu_size < TOPOLOGY_CHANGE_NOTIFICATION_BPDU_SIZE ||
ntohs(temp->protocol_identifier) != 0) {
return -1;
} else {
if (temp->bpdu_type == CONFIGURATION_BPDU &&
bpdu_size >= CONFIGURATION_BPDU_SIZE &&
(time_decode(temp->message_age) < time_decode(temp->max_age)))
{
if ((ntohll(temp->designated_bridge_id) !=
p->rstp->bridge_identifier) ||
((ntohll(temp->designated_bridge_id) ==
p->rstp->bridge_identifier) &&
(ntohs(temp->designated_port_id) != p->port_id))) {
return 0;
}
else {
return -1;
}
} else if (temp->bpdu_type == TOPOLOGY_CHANGE_NOTIFICATION_BPDU) {
return 0;
} else if (temp->bpdu_type == RAPID_SPANNING_TREE_BPDU &&
bpdu_size >= RAPID_SPANNING_TREE_BPDU_SIZE) {
return 0;
}
else {
return -1;
}
}
}
/*
* move_rstp__()
* This method is invoked to move the State Machines. The SMs move only if the
* boolean 'changes' is true, meaning that something changed and the SMs need
* to work to process this change.
* The boolean 'changes' is set every time a SM modifies its state, a BPDU is
* received, a timer expires or port down event is detected. If a parameter is
* set by management, then 'changes' is set.
*/
#define MAX_RSTP_ITERATIONS 1000 /* safeguard */
int
move_rstp__(struct rstp *rstp)
OVS_REQUIRES(rstp_mutex)
{
struct rstp_port *p;
int num_iterations;
num_iterations = 0;
while (rstp->changes == true && num_iterations < MAX_RSTP_ITERATIONS) {
VLOG_DBG("%s: move_rstp()", rstp->name);
rstp->changes = false;
port_role_selection_sm(rstp);
if (rstp->ports_count > 0) {
LIST_FOR_EACH (p, node, &rstp->ports) {
if (p->rstp_state != RSTP_DISABLED) {
port_receive_sm(p);
bridge_detection_sm(p);
port_information_sm(p);
port_role_transition_sm(p);
port_state_transition_sm(p);
topology_change_sm(p);
port_transmit_sm(p);
port_protocol_migration_sm(p);
}
}
}
num_iterations++;
seq_change(connectivity_seq_get());
}
if (num_iterations >= MAX_RSTP_ITERATIONS) {
VLOG_ERR("%s: move_rstp() reached the iteration safeguard limit!",
rstp->name);
}
return 0;
}
void decrease_rstp_port_timers__(struct rstp *r)
OVS_REQUIRES(rstp_mutex)
{
struct rstp_port *p;
if (r->ports_count > 0) {
LIST_FOR_EACH (p, node, &r->ports) {
decrement_timer(&p->hello_when);
decrement_timer(&p->tc_while);
decrement_timer(&p->fd_while);
decrement_timer(&p->rcvd_info_while);
decrement_timer(&p->rr_while);
decrement_timer(&p->rb_while);
decrement_timer(&p->mdelay_while);
decrement_timer(&p->edge_delay_while);
decrement_timer(&p->tx_count);
p->uptime+=1;
}
}
r->changes = true;
move_rstp__(r);
}
static void
decrement_timer(uint16_t *timer)
{
if (*timer != 0) {
*timer -= 1;
}
}
/* Bridge State Machine. */
/* [17.28] Port Role Selection state machine. */
static void
updt_role_disabled_tree(struct rstp *r)
OVS_REQUIRES(rstp_mutex)
{
struct rstp_port *p;
if (r->ports_count > 0) {
LIST_FOR_EACH (p, node, &r->ports) {
p->selected_role = ROLE_DISABLED;
}
}
}
static void
clear_reselect_tree(struct rstp *r)
OVS_REQUIRES(rstp_mutex)
{
struct rstp_port *p;
if (r->ports_count > 0) {
LIST_FOR_EACH (p, node, &r->ports) {
p->reselect = false;
}
}
}
void
updt_roles_tree__(struct rstp *r)
OVS_REQUIRES(rstp_mutex)
{
struct rstp_port *p;
int vsel;
struct rstp_priority_vector best_vector, candidate_vector;
vsel = -1;
best_vector = r->bridge_priority;
/* Letter c1) */
r->root_times = r->bridge_times;
/* Letters a) b) c) */
if (r->ports_count > 0) {
LIST_FOR_EACH (p, node, &r->ports) {
uint32_t old_root_path_cost;
uint32_t root_path_cost;
if (p->info_is != INFO_IS_RECEIVED) {
continue;
}
/* [17.6] */
candidate_vector = p->port_priority;
candidate_vector.bridge_port_id = p->port_id;
old_root_path_cost = candidate_vector.root_path_cost;
root_path_cost = old_root_path_cost + p->port_path_cost;
candidate_vector.root_path_cost = root_path_cost;
if ((candidate_vector.designated_bridge_id & 0xffffffffffffULL) ==
(r->bridge_priority.designated_bridge_id & 0xffffffffffffULL))
{
break;
}
if (compare_rstp_priority_vector(&candidate_vector, &best_vector)
== SUPERIOR) {
best_vector = candidate_vector;
r->root_times = p->port_times;
r->root_times.message_age++;
vsel = p->port_number;
}
}
}
r->root_priority = best_vector;
r->root_port_id = best_vector.bridge_port_id;
VLOG_DBG("%s: new Root is "RSTP_ID_FMT, r->name,
RSTP_ID_ARGS(r->root_priority.root_bridge_id));
/* Letters d) e) */
if (r->ports_count > 0) {
LIST_FOR_EACH (p, node, &r->ports) {
p->designated_priority_vector.root_bridge_id =
r->root_priority.root_bridge_id;
p->designated_priority_vector.root_path_cost =
r->root_priority.root_path_cost;
p->designated_priority_vector.designated_bridge_id =
r->bridge_identifier;
p->designated_priority_vector.designated_port_id =
p->port_id;
p->designated_times = r->root_times;
p->designated_times.hello_time = r->bridge_times.hello_time;
}
}
if (r->ports_count > 0) {
LIST_FOR_EACH (p, node, &r->ports) {
switch (p->info_is) {
case INFO_IS_DISABLED:
p->selected_role = ROLE_DISABLED;
break;
case INFO_IS_AGED:
p->updt_info = true;
p->selected_role = ROLE_DESIGNATED;
break;
case INFO_IS_MINE:
p->selected_role = ROLE_DESIGNATED;
if ((compare_rstp_priority_vector(&p->port_priority,
&p->designated_priority_vector) != SAME) ||
!rstp_times_equal(&p->designated_times, &r->root_times)) {
p->updt_info = true;
}
break;
case INFO_IS_RECEIVED:
if (vsel == p->port_number) { /* Letter i) */
p->selected_role = ROLE_ROOT;
p->updt_info = false;
} else if (compare_rstp_priority_vector(
&p->designated_priority_vector, &p->port_priority)
== INFERIOR) {
if (p->port_priority.designated_bridge_id !=
r->bridge_identifier) {
p->selected_role = ROLE_ALTERNATE;
p->updt_info = false;
} else {
p->selected_role = ROLE_BACKUP;
p->updt_info = false;
}
} else {
p->selected_role = ROLE_DESIGNATED;
p->updt_info = true;
}
break;
default:
OVS_NOT_REACHED();
/* no break */
}
}
}
seq_change(connectivity_seq_get());
}
static void
set_selected_tree(struct rstp *r)
OVS_REQUIRES(rstp_mutex)
{
struct rstp_port *p;
if (r->ports_count > 0) {
LIST_FOR_EACH (p, node, &r->ports) {
if (p->reselect) {
return;
}
}
LIST_FOR_EACH (p, node, &r->ports) {
p->selected = true;
}
}
}
static int
port_role_selection_sm(struct rstp *r)
OVS_REQUIRES(rstp_mutex)
{
enum port_role_selection_state_machine old_state;
struct rstp_port *p;
old_state = r->port_role_selection_sm_state;
switch (r->port_role_selection_sm_state) {
case PORT_ROLE_SELECTION_SM_INIT:
if (r->begin)
r->port_role_selection_sm_state =
PORT_ROLE_SELECTION_SM_INIT_BRIDGE_EXEC;
break;
case PORT_ROLE_SELECTION_SM_INIT_BRIDGE_EXEC:
updt_role_disabled_tree(r);
r->port_role_selection_sm_state = PORT_ROLE_SELECTION_SM_INIT_BRIDGE;
/* no break */
case PORT_ROLE_SELECTION_SM_INIT_BRIDGE:
r->port_role_selection_sm_state =
PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC;
break;
case PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC:
clear_reselect_tree(r);
updt_roles_tree__(r);
set_selected_tree(r);
r->port_role_selection_sm_state =
PORT_ROLE_SELECTION_SM_ROLE_SELECTION;
/* no break */
case PORT_ROLE_SELECTION_SM_ROLE_SELECTION:
if (r->ports_count > 0) {
LIST_FOR_EACH (p, node, &r->ports) {
if (p->reselect) {
r->port_role_selection_sm_state =
PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC;
break;
}
}
}
break;
default:
OVS_NOT_REACHED();
/* no break */
}
if (old_state != r->port_role_selection_sm_state) {
r->changes = true;
VLOG_DBG("%s: Port_role_selection_sm %d -> %d", r->name,
old_state, r->port_role_selection_sm_state);
}
return 0;
}
/* Port State Machines */
/* [17.23 - Port receive state machine] */
static void
updt_bpdu_version(struct rstp_port *p) /* [17.21.22] */
OVS_REQUIRES(rstp_mutex)
{
switch (p->received_bpdu_buffer.bpdu_type) {
case CONFIGURATION_BPDU:
case TOPOLOGY_CHANGE_NOTIFICATION_BPDU:
p->rcvd_rstp = false;
p->rcvd_stp = true;
break;
case RAPID_SPANNING_TREE_BPDU:
p->rcvd_rstp = true;
p->rcvd_stp = false;
break;
default:
OVS_NOT_REACHED();
/* no break */
}
}
static int
port_receive_sm(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
enum port_receive_state_machine old_state;
struct rstp *r;
old_state = p->port_receive_sm_state;
r = p->rstp;
switch (p->port_receive_sm_state) {
case PORT_RECEIVE_SM_INIT:
if (r->begin || ((p->rcvd_bpdu || (p->edge_delay_while !=
r->migrate_time)) && !p->port_enabled)) {
p->port_receive_sm_state = PORT_RECEIVE_SM_DISCARD_EXEC;
}
break;
case PORT_RECEIVE_SM_DISCARD_EXEC:
p->rcvd_bpdu = p->rcvd_rstp = p->rcvd_stp = false;
p->rcvd_msg = false;
p->edge_delay_while = r->migrate_time;
p->port_receive_sm_state = PORT_RECEIVE_SM_DISCARD;
/* no break */
case PORT_RECEIVE_SM_DISCARD:
if (p->rcvd_bpdu && p->port_enabled) {
p->port_receive_sm_state = PORT_RECEIVE_SM_RECEIVE_EXEC;
}
break;
case PORT_RECEIVE_SM_RECEIVE_EXEC:
updt_bpdu_version(p);
p->oper_edge = p->rcvd_bpdu = false;
p->rcvd_msg = true;
p->edge_delay_while = r->migrate_time;
p->port_receive_sm_state = PORT_RECEIVE_SM_RECEIVE;
/* no break */
case PORT_RECEIVE_SM_RECEIVE:
if (p->rcvd_bpdu && p->port_enabled && !p->rcvd_msg) {
p->port_receive_sm_state = PORT_RECEIVE_SM_RECEIVE_EXEC;
}
break;
default:
OVS_NOT_REACHED();
/* no break */
}
if (old_state != p->port_receive_sm_state) {
r->changes = true;
VLOG_DBG("%s, port %u: Port_receive_sm %d -> %d", p->rstp->name,
p->port_number, old_state, p->port_receive_sm_state);
}
return 0;
}
/* [17.24 - Port Protocol Migration state machine] */
static int
port_protocol_migration_sm(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
enum port_protocol_migration_state_machine old_state;
struct rstp *r;
old_state = p->port_protocol_migration_sm_state;
r = p->rstp;
switch (p->port_protocol_migration_sm_state) {
case PORT_PROTOCOL_MIGRATION_SM_INIT:
p->port_protocol_migration_sm_state =
PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC;
/* no break */
case PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC:
p->mcheck = false;
p->send_rstp = r->rstp_version;
p->mdelay_while = r->migrate_time;
p->port_protocol_migration_sm_state =
PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP;
/* no break */
case PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP:
if (p->mdelay_while == 0) {
p->port_protocol_migration_sm_state =
PORT_PROTOCOL_MIGRATION_SM_SENSING_EXEC;
}
else if ((p->mdelay_while != r->migrate_time) && !p->port_enabled) {
p->port_protocol_migration_sm_state =
PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC;
}
break;
case PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP_EXEC:
p->send_rstp = false;
p->mdelay_while = r->migrate_time;
p->port_protocol_migration_sm_state =
PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP;
/* no break */
case PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP:
if ((p->mdelay_while == 0) || (!p->port_enabled) || p->mcheck) {
p->port_protocol_migration_sm_state =
PORT_PROTOCOL_MIGRATION_SM_SENSING_EXEC;
}
break;
case PORT_PROTOCOL_MIGRATION_SM_SENSING_EXEC:
p->rcvd_rstp = false;
p->rcvd_stp = false;
p->port_protocol_migration_sm_state =
PORT_PROTOCOL_MIGRATION_SM_SENSING;
/* no break */
case PORT_PROTOCOL_MIGRATION_SM_SENSING:
if (!p->port_enabled || p->mcheck || ((r->rstp_version) &&
!p->send_rstp && p->rcvd_rstp)) {
p->port_protocol_migration_sm_state =
PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC;
}
else if (p->send_rstp && p->rcvd_stp) {
p->port_protocol_migration_sm_state =
PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP_EXEC;
}
break;
default:
OVS_NOT_REACHED();
/* no break */
}
if (old_state != p->port_protocol_migration_sm_state) {
r->changes = true;
VLOG_DBG("%s, port %u: port_protocol_migration_sm %d -> %d",
p->rstp->name, p->port_number, old_state,
p->port_protocol_migration_sm_state);
}
return 0;
}
/* [17.25 - Bridge Detection state machine] */
static int
bridge_detection_sm(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
enum bridge_detection_state_machine old_state;
struct rstp *r;
old_state = p->bridge_detection_sm_state;
r = p->rstp;
switch (p->bridge_detection_sm_state) {
case BRIDGE_DETECTION_SM_INIT:
if (r->begin && p->admin_edge) {
p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_EDGE_EXEC;
} else if (r->begin && !p->admin_edge) {
p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_NOT_EDGE_EXEC;
}
break;
case BRIDGE_DETECTION_SM_EDGE_EXEC:
p->oper_edge = true;
p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_EDGE;
/* no break */
case BRIDGE_DETECTION_SM_EDGE:
if ((!p->port_enabled && !p->admin_edge) || !p->oper_edge) {
p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_NOT_EDGE_EXEC;
}
break;
case BRIDGE_DETECTION_SM_NOT_EDGE_EXEC:
p->oper_edge = false;
p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_NOT_EDGE;
/* no break */
case BRIDGE_DETECTION_SM_NOT_EDGE:
if ((!p->port_enabled && p->admin_edge) || ((p->edge_delay_while == 0)
&& p->auto_edge && p->send_rstp && p->proposing)) {
p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_EDGE_EXEC;
}
break;
default:
OVS_NOT_REACHED();
/* no break */
}
if (old_state != p->bridge_detection_sm_state) {
r->changes = true;
VLOG_DBG("%s, port %u: bridge_detection_sm %d -> %d", p->rstp->name,
p->port_number, old_state, p->bridge_detection_sm_state);
}
return 0;
}
/* [17.26 - Port Transmit state machine] */
static void
rstp_send_bpdu(struct rstp_port *p, const void *bpdu, size_t bpdu_size)
OVS_REQUIRES(rstp_mutex)
{
struct eth_header *eth;
struct llc_header *llc;
struct ofpbuf *pkt;
/* Skeleton. */
pkt = ofpbuf_new(ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size);
eth = ofpbuf_put_zeros(pkt, sizeof *eth);
llc = ofpbuf_put_zeros(pkt, sizeof *llc);
ofpbuf_set_frame(pkt, eth);
ofpbuf_set_l3(pkt, ofpbuf_put(pkt, bpdu, bpdu_size));
/* 802.2 header. */
memcpy(eth->eth_dst, eth_addr_stp, ETH_ADDR_LEN);
/* p->rstp->send_bpdu() must fill in source address. */
eth->eth_type = htons(ofpbuf_size(pkt) - ETH_HEADER_LEN);
/* LLC header. */
llc->llc_dsap = STP_LLC_DSAP;
llc->llc_ssap = STP_LLC_SSAP;
llc->llc_cntl = STP_LLC_CNTL;
p->rstp->send_bpdu(pkt, p->port_number, p->rstp->aux);
}
static void
record_agreement(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
struct rstp *r;
r = p->rstp;
if (r->rstp_version && p->oper_point_to_point_mac &&
((p->received_bpdu_buffer.flags & BPDU_FLAG_AGREEMENT))) {
p->agreed = true;
p->proposing = false;
} else {
p->agreed = false;
}
}
static void
set_tc_flags(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
/* Sets rcvd_tc and/or rcvd_tc_ack if the Topology Change and/or Topology
* Change Acknowledgment flags, respectively, are set in a ConfigBPDU or
* RST BPDU.
*/
if (p->received_bpdu_buffer.bpdu_type == CONFIGURATION_BPDU ||
p->received_bpdu_buffer.bpdu_type == RAPID_SPANNING_TREE_BPDU) {
if ((p->received_bpdu_buffer.flags & BPDU_FLAG_TOPCHANGE) != 0) {
p->rcvd_tc = true;
}
if ((p->received_bpdu_buffer.flags & BPDU_FLAG_TOPCHANGEACK) != 0) {
p->rcvd_tc_ack = true;
}
}
/* Sets rcvd_tcn true if the BPDU is a TCN BPDU. */
if (p->received_bpdu_buffer.bpdu_type ==
TOPOLOGY_CHANGE_NOTIFICATION_BPDU) {
p->rcvd_tcn = true;
}
}
static void
record_dispute(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
if ((p->received_bpdu_buffer.flags & BPDU_FLAG_LEARNING) != 0) {
p->agreed = true;
p->proposing = false;
}
}
static void
record_proposal(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
enum port_flag role =
((p->received_bpdu_buffer.flags) & ROLE_FLAG_MASK) >> ROLE_FLAG_SHIFT;
if ((role == PORT_DES) &&
((p->received_bpdu_buffer.flags & BPDU_FLAG_PROPOSAL) != 0)) {
p->proposed = true;
}
}
static void
record_priority(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
p->port_priority.root_bridge_id = p->msg_priority.root_bridge_id;
p->port_priority.root_path_cost = p->msg_priority.root_path_cost;
p->port_priority.designated_bridge_id =
p->msg_priority.designated_bridge_id;
p->port_priority.designated_port_id = p->msg_priority.designated_port_id;
}
static void
record_times(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
p->port_times = p->msg_times;
if (p->msg_times.hello_time == 0) {
p->port_times.hello_time = 1;
}
}
static void
updt_rcvd_info_while(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
/* [17.21.23]
* The value assigned to rcvdInfoWhile is the three times the Hello Time,
* if Message Age, incremented by 1 second and rounded to the nearest whole
* second, does not exceed Max Age, and is zero otherwise.
*/
if (p->port_times.message_age < p->port_times.max_age) {
p->rcvd_info_while = p->port_times.hello_time * 3;
} else {
p->rcvd_info_while = 0;
}
}
/* Times are internally held in seconds, while the protocol uses 1/256 seconds.
* time_encode() is used to convert time values sent in bpdus, while
* time_decode() is used to convert time values received in bpdus.
*/
static ovs_be16
time_encode(uint8_t value)
{
return htons(value * 256);
}
static uint8_t
time_decode(ovs_be16 encoded)
{
return ntohs(encoded) / 256;
}
/* [17.21.19] */
static void
tx_config(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
struct rstp_bpdu bpdu;
bpdu.protocol_identifier = htons(0);
bpdu.protocol_version_identifier = 0;
bpdu.bpdu_type = CONFIGURATION_BPDU;
bpdu.root_bridge_id = htonll(p->designated_priority_vector.root_bridge_id);
bpdu.root_path_cost = htonl(p->designated_priority_vector.root_path_cost);
bpdu.designated_bridge_id =
htonll(p->designated_priority_vector.designated_bridge_id);
bpdu.designated_port_id =
htons(p->designated_priority_vector.designated_port_id);
bpdu.message_age = time_encode(p->designated_times.message_age);
bpdu.max_age = time_encode(p->designated_times.max_age);
bpdu.hello_time = time_encode(p->designated_times.hello_time);
bpdu.forward_delay = time_encode(p->designated_times.forward_delay);
bpdu.flags = 0;
if (p->tc_while != 0) {
bpdu.flags |= BPDU_FLAG_TOPCHANGE;
}
if (p->tc_ack != 0) {
bpdu.flags |= BPDU_FLAG_TOPCHANGEACK;
}
rstp_send_bpdu(p, &bpdu, sizeof(struct rstp_bpdu));
}
/* [17.21.20] */
static void
tx_rstp(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
struct rstp_bpdu bpdu;
bpdu.protocol_identifier = htons(0);
bpdu.protocol_version_identifier = 2;
bpdu.bpdu_type = RAPID_SPANNING_TREE_BPDU;
bpdu.root_bridge_id = htonll(p->designated_priority_vector.root_bridge_id);
bpdu.root_path_cost = htonl(p->designated_priority_vector.root_path_cost);
bpdu.designated_bridge_id =
htonll(p->designated_priority_vector.designated_bridge_id);
bpdu.designated_port_id =
htons(p->designated_priority_vector.designated_port_id);
bpdu.message_age = time_encode(p->designated_times.message_age);
bpdu.max_age = time_encode(p->designated_times.max_age);
bpdu.hello_time = time_encode(p->designated_times.hello_time);
bpdu.forward_delay = time_encode(p->designated_times.forward_delay);
bpdu.flags = 0;
switch (p->role) {
case ROLE_ROOT:
bpdu.flags = PORT_ROOT << ROLE_FLAG_SHIFT;
break;
case ROLE_DESIGNATED:
bpdu.flags = PORT_DES << ROLE_FLAG_SHIFT;
break;
case ROLE_ALTERNATE:
case ROLE_BACKUP:
bpdu.flags = PORT_ALT_BACK << ROLE_FLAG_SHIFT;
break;
case ROLE_DISABLED:
/* Should not happen! */
VLOG_ERR("%s transmitting bpdu in disabled role on port "
RSTP_PORT_ID_FMT, p->rstp->name, p->port_id);
break;
}
if (p->agree) {
bpdu.flags |= BPDU_FLAG_AGREEMENT;
}
if (p->proposing) {
bpdu.flags |= BPDU_FLAG_PROPOSAL;
}
if (p->tc_while != 0) {
bpdu.flags |= BPDU_FLAG_TOPCHANGE;
}
if (p->learning) {
bpdu.flags |= BPDU_FLAG_LEARNING;
}
if (p->forwarding) {
bpdu.flags |= BPDU_FLAG_FORWARDING;
}
bpdu.version1_length = 0;
rstp_send_bpdu(p, &bpdu, sizeof(struct rstp_bpdu));
}
/* [17.21.21] */
static void
tx_tcn(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
struct rstp_bpdu bpdu;
memset(&bpdu, 0, sizeof(struct rstp_bpdu));
bpdu.protocol_identifier = htons(0);
bpdu.protocol_version_identifier = 0;
bpdu.bpdu_type = TOPOLOGY_CHANGE_NOTIFICATION_BPDU;
rstp_send_bpdu(p, &bpdu, sizeof(struct rstp_bpdu));
}
static int
port_transmit_sm(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
enum port_transmit_state_machine old_state;
struct rstp *r;
old_state = p->port_transmit_sm_state;
r = p->rstp;
switch (p->port_transmit_sm_state) {
case PORT_TRANSMIT_SM_INIT:
if (r->begin) {
p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_INIT_EXEC;
}
break;
case PORT_TRANSMIT_SM_TRANSMIT_INIT_EXEC:
p->new_info = true;
p->tx_count = 0;
p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_INIT;
/* no break */
case PORT_TRANSMIT_SM_TRANSMIT_INIT:
p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC;
break;
case PORT_TRANSMIT_SM_TRANSMIT_PERIODIC_EXEC:
p->new_info = p->new_info || (p->role == ROLE_DESIGNATED ||
(p->role == ROLE_ROOT && p->tc_while != 0));
p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_PERIODIC;
/* no break */
case PORT_TRANSMIT_SM_TRANSMIT_PERIODIC:
p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC;
break;
case PORT_TRANSMIT_SM_IDLE_EXEC:
p->hello_when = r->bridge_hello_time;
p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE;
/* no break */
case PORT_TRANSMIT_SM_IDLE:
if (p->role == ROLE_DISABLED) {
VLOG_DBG("%s, port %u: port_transmit_sm ROLE == DISABLED.",
p->rstp->name, p->port_number);
break;
}
else if (p->send_rstp && p->new_info &&
(p->tx_count < r->transmit_hold_count) && (p->hello_when != 0)
&& p->selected && !p->updt_info) {
p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_RSTP_EXEC;
}
else if (p->hello_when == 0 && p->selected && !p->updt_info) {
p->port_transmit_sm_state =
PORT_TRANSMIT_SM_TRANSMIT_PERIODIC_EXEC;
}
else if (!p->send_rstp && p->new_info && (p->role == ROLE_ROOT) &&
(p->tx_count < r->transmit_hold_count) && (p->hello_when != 0)
&& p->selected && !p->updt_info) {
p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_TCN_EXEC;
}
else if (!p->send_rstp && p->new_info && (p->role == ROLE_DESIGNATED)
&& (p->tx_count < r->transmit_hold_count) &&
(p->hello_when != 0) && p->selected && !p->updt_info) {
p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_CONFIG_EXEC;
}
break;
case PORT_TRANSMIT_SM_TRANSMIT_CONFIG_EXEC:
p->new_info = false;
tx_config(p);
p->tx_count += 1;
p->tc_ack = false;
p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_CONFIG;
/* no break */
case PORT_TRANSMIT_SM_TRANSMIT_CONFIG:
p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC;
break;
case PORT_TRANSMIT_SM_TRANSMIT_TCN_EXEC:
p->new_info = false;
tx_tcn(p);
p->tx_count += 1;
p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_TCN;
/* no break */
case PORT_TRANSMIT_SM_TRANSMIT_TCN:
p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC;
break;
case PORT_TRANSMIT_SM_TRANSMIT_RSTP_EXEC:
p->new_info = false;
tx_rstp(p);
p->tx_count += 1;
p->tc_ack = false;
p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_RSTP;
/* no break */
case PORT_TRANSMIT_SM_TRANSMIT_RSTP:
p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC;
break;
default:
OVS_NOT_REACHED();
/* no break */
}
if (old_state != p->port_transmit_sm_state) {
r->changes = true;
VLOG_DBG("%s, port %u: port_transmit_sm %d -> %d", p->rstp->name,
p->port_number, old_state, p->port_transmit_sm_state);
}
return 0;
}
/* [17.27 Port Information state machine] */
#define RECEIVED 0
#define MINE 1
static int
rcv_info(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
enum vector_comparison cp;
bool ct;
enum port_flag role;
p->msg_priority.root_bridge_id =
ntohll(p->received_bpdu_buffer.root_bridge_id);
p->msg_priority.root_path_cost =
ntohl(p->received_bpdu_buffer.root_path_cost);
p->msg_priority.designated_bridge_id =
ntohll(p->received_bpdu_buffer.designated_bridge_id);
p->msg_priority.designated_port_id =
ntohs(p->received_bpdu_buffer.designated_port_id);
p->msg_times.forward_delay =
time_decode(p->received_bpdu_buffer.forward_delay);
p->msg_times.hello_time = time_decode(p->received_bpdu_buffer.hello_time);
p->msg_times.max_age = time_decode(p->received_bpdu_buffer.max_age);
p->msg_times.message_age =
time_decode(p->received_bpdu_buffer.message_age);
cp = compare_rstp_priority_vector(&p->msg_priority, &p->port_priority);
ct = rstp_times_equal(&p->port_times, &p->msg_times);
role =
((p->received_bpdu_buffer.flags) & ROLE_FLAG_MASK) >> ROLE_FLAG_SHIFT;
/* Returns SuperiorDesignatedInfo if:
* a) The received message conveys a Designated Port Role, and
* 1) The message priority is superior (17.6) to the Port.s port priority
* vector, or
* 2) The message priority vector is the same as the Port.s port priority
* vector, and any of the received timer parameter values (msg_times.
* 17.19.15) differ from those already held for the Port (port_times
* 17.19.22).
* NOTE: Configuration BPDU explicitly conveys a Designated Port Role.
*/
if ((role == PORT_DES ||
p->received_bpdu_buffer.bpdu_type == CONFIGURATION_BPDU) &&
((cp == SUPERIOR) || ((cp == SAME) && ct == false))) {
return SUPERIOR_DESIGNATED_INFO;
}
/* Returns RepeatedDesignatedInfo if:
* b) The received message conveys Designated Port Role, and a message
* priority vector and timer parameters that are the same as the Port's
* port priority vector or timer values.
*/
else if ((role == PORT_DES) && (cp == SAME) && (ct == true)) {
return REPEATED_DESIGNATED_INFO;
}
/* Returns InferiorDesignatedInfo if:
* c) The received message conveys a Designated Port Role, and a message
* priority vector that is worse than the Port's port priority vector.
*/
else if ((role == PORT_DES) && (cp == INFERIOR)) {
return INFERIOR_DESIGNATED_INFO;
}
/* Returns InferiorRootAlternateInfo if:
* d) The received message conveys a Root Port, Alternate Port, or Backup
* Port Role and a message priority that is the same as or worse than
* the port priority vector.
*/
else if ((role == PORT_ROOT || role == PORT_ALT_BACK) &&
(cp == INFERIOR || cp == SAME)) {
return INFERIOR_ROOT_ALTERNATE_INFO;
}
/* Otherwise, returns OtherInfo. */
else {
return OTHER_INFO;
}
}
static int
better_or_same_info(struct rstp_port *p, int new_info_is)
OVS_REQUIRES(rstp_mutex)
{
/* >= SUPERIOR means that the vector is better or the same. */
return ((new_info_is == RECEIVED && p->info_is == INFO_IS_RECEIVED &&
compare_rstp_priority_vector(&p->msg_priority,
&p->port_priority) >= SUPERIOR) ||
(new_info_is == MINE && p->info_is == INFO_IS_MINE &&
compare_rstp_priority_vector(&p->designated_priority_vector,
&p->port_priority) >= SUPERIOR));
}
static int
port_information_sm(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
enum port_information_state_machine old_state;
struct rstp *r;
old_state = p->port_information_sm_state;
r = p->rstp;
if (!p->port_enabled && (p->info_is != INFO_IS_DISABLED)) {
p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
}
switch (p->port_information_sm_state) {
case PORT_INFORMATION_SM_INIT:
if (r->begin) {
p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
}
break;
case PORT_INFORMATION_SM_DISABLED_EXEC:
p->rcvd_msg = false;
p->proposing = p->proposed = p->agree = p->agreed = false;
p->rcvd_info_while = 0;
p->info_is = INFO_IS_DISABLED;
p->reselect = true;
p->selected = false;
p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED;
/* no break */
case PORT_INFORMATION_SM_DISABLED:
if (p->port_enabled) {
p->port_information_sm_state = PORT_INFORMATION_SM_AGED_EXEC;
} else if (p->rcvd_msg) {
p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
}
break;
case PORT_INFORMATION_SM_AGED_EXEC:
p->info_is = INFO_IS_AGED;
p->reselect = true;
p->selected = false;
p->port_information_sm_state = PORT_INFORMATION_SM_AGED;
/* no break */
case PORT_INFORMATION_SM_AGED:
if (p->selected && p->updt_info) {
p->port_information_sm_state = PORT_INFORMATION_SM_UPDATE_EXEC;
}
break;
case PORT_INFORMATION_SM_UPDATE_EXEC:
p->proposing = p->proposed = false;
/* MINE is not specified in Standard 802.1D-2004. */
p->agreed = p->agreed && better_or_same_info(p, MINE);
p->synced = p->synced && p->agreed;
p->port_priority.root_bridge_id =
p->designated_priority_vector.root_bridge_id;
p->port_priority.root_path_cost =
p->designated_priority_vector.root_path_cost;
p->port_priority.designated_bridge_id =
p->designated_priority_vector.designated_bridge_id;
p->port_priority.designated_port_id =
p->designated_priority_vector.designated_port_id;
p->port_times = p->designated_times;
p->updt_info = false;
p->info_is = INFO_IS_MINE;
p->new_info = true;
p->port_information_sm_state = PORT_INFORMATION_SM_UPDATE;
/* no break */
case PORT_INFORMATION_SM_UPDATE:
p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
break;
case PORT_INFORMATION_SM_CURRENT_EXEC:
p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT;
/* no break */
case PORT_INFORMATION_SM_CURRENT:
if (p->rcvd_msg && !p->updt_info) {
p->port_information_sm_state = PORT_INFORMATION_SM_RECEIVE_EXEC;
} else if (p->selected && p->updt_info) {
p->port_information_sm_state = PORT_INFORMATION_SM_UPDATE_EXEC;
} else if ((p->info_is == INFO_IS_RECEIVED) &&
(p->rcvd_info_while == 0) && !p->updt_info &&
!p->rcvd_msg) {
p->port_information_sm_state = PORT_INFORMATION_SM_AGED_EXEC;
}
break;
case PORT_INFORMATION_SM_RECEIVE_EXEC:
p->rcvd_info = rcv_info(p);
p->port_information_sm_state = PORT_INFORMATION_SM_RECEIVE;
/* no break */
case PORT_INFORMATION_SM_RECEIVE:
switch (p->rcvd_info) {
case SUPERIOR_DESIGNATED_INFO:
p->port_information_sm_state =
PORT_INFORMATION_SM_SUPERIOR_DESIGNATED_EXEC;
break;
case REPEATED_DESIGNATED_INFO:
p->port_information_sm_state =
PORT_INFORMATION_SM_REPEATED_DESIGNATED_EXEC;
break;
case INFERIOR_DESIGNATED_INFO:
p->port_information_sm_state =
PORT_INFORMATION_SM_INFERIOR_DESIGNATED_EXEC;
break;
case INFERIOR_ROOT_ALTERNATE_INFO:
p->port_information_sm_state =
PORT_INFORMATION_SM_NOT_DESIGNATED_EXEC;
break;
case OTHER_INFO:
p->port_information_sm_state = PORT_INFORMATION_SM_OTHER_EXEC;
break;
default:
OVS_NOT_REACHED();
/* no break */
}
break;
case PORT_INFORMATION_SM_OTHER_EXEC:
p->rcvd_msg = false;
p->port_information_sm_state = PORT_INFORMATION_SM_OTHER;
/* no break */
case PORT_INFORMATION_SM_OTHER:
p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
break;
case PORT_INFORMATION_SM_NOT_DESIGNATED_EXEC:
record_agreement(p);
set_tc_flags(p);
p->rcvd_msg = false;
p->port_information_sm_state = PORT_INFORMATION_SM_NOT_DESIGNATED;
/* no break */
case PORT_INFORMATION_SM_NOT_DESIGNATED:
p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
break;
case PORT_INFORMATION_SM_INFERIOR_DESIGNATED_EXEC:
record_dispute(p);
p->rcvd_msg = false;
p->port_information_sm_state = PORT_INFORMATION_SM_INFERIOR_DESIGNATED;
/* no break */
case PORT_INFORMATION_SM_INFERIOR_DESIGNATED:
p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
break;
case PORT_INFORMATION_SM_REPEATED_DESIGNATED_EXEC:
record_proposal(p);
set_tc_flags(p);
updt_rcvd_info_while(p);
p->rcvd_msg = false;
p->port_information_sm_state = PORT_INFORMATION_SM_REPEATED_DESIGNATED;
/* no break */
case PORT_INFORMATION_SM_REPEATED_DESIGNATED:
p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
break;
case PORT_INFORMATION_SM_SUPERIOR_DESIGNATED_EXEC:
p->agreed = p->proposing = false;
record_proposal(p);
set_tc_flags(p);
/* RECEIVED is not specified in Standard 802.1D-2004. */
p->agree = p->agree && better_or_same_info(p, RECEIVED);
record_priority(p);
record_times(p);
updt_rcvd_info_while(p);
p->info_is = INFO_IS_RECEIVED;
p->reselect = true;
p->selected = false;
p->rcvd_msg = false;
p->port_information_sm_state = PORT_INFORMATION_SM_SUPERIOR_DESIGNATED;
/* no break */
case PORT_INFORMATION_SM_SUPERIOR_DESIGNATED:
p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
break;
default:
OVS_NOT_REACHED();
/* no break */
}
if (old_state != p->port_information_sm_state) {
r->changes = true;
VLOG_DBG("%s, port %u: Port_information_sm %d -> %d", p->rstp->name,
p->port_number, old_state, p->port_information_sm_state);
}
return 0;
}
/* [17.29 Port Role Transitions state machine] */
static void
set_re_root_tree(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
struct rstp *r;
struct rstp_port *p1;
r = p->rstp;
if (r->ports_count > 0) {
LIST_FOR_EACH (p1, node, &r->ports) {
p1->re_root = true;
}
}
}
static void
set_sync_tree(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
struct rstp *r;
struct rstp_port *p1;
r = p->rstp;
if (r->ports_count > 0) {
LIST_FOR_EACH (p1, node, &r->ports) {
p1->sync = true;
}
}
}
static int
hello_time(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
return p->designated_times.hello_time;
}
static int
fwd_delay(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
return p->designated_times.forward_delay;
}
static int
forward_delay(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
if (p->send_rstp) {
return hello_time(p);
} else {
return fwd_delay(p);
}
}
static int
edge_delay(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
struct rstp *r;
r = p->rstp;
if (p->oper_point_to_point_mac == 1) {
return r->migrate_time;
} else {
return p->designated_times.max_age;
}
}
static int
check_selected_role_change(struct rstp_port *p, int current_role_state)
OVS_REQUIRES(rstp_mutex)
{
if (p->selected && !p->updt_info && (p->role != p->selected_role) &&
(p->selected_role != current_role_state)) {
VLOG_DBG("%s, port %u: case: current = %s role = %s selected = %d",
p->rstp->name, p->port_number,
rstp_port_role_name(current_role_state),
rstp_port_role_name(p->role), p->selected_role);
switch (p->selected_role) {
case ROLE_ROOT:
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
return true;
case ROLE_DESIGNATED:
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
return true;
case ROLE_ALTERNATE:
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_BLOCK_PORT_EXEC;
return true;
case ROLE_BACKUP:
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_BLOCK_PORT_EXEC;
return true;
case ROLE_DISABLED:
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_DISABLE_PORT_EXEC;
return true;
}
}
return false;
}
static int
re_rooted(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
struct rstp *r;
struct rstp_port *p1;
r = p->rstp;
if (r->ports_count > 0) {
LIST_FOR_EACH (p1, node, &r->ports) {
if ((p1 != p) && (p1->rr_while != 0)) {
return false;
}
}
}
return true;
}
static int
all_synced(struct rstp *r)
OVS_REQUIRES(rstp_mutex)
{
struct rstp_port *p;
if (r->ports_count > 0) {
LIST_FOR_EACH (p, node, &r->ports) {
if (!(p->selected && p->role == p->selected_role &&
(p->role == ROLE_ROOT || p->synced == true))) {
return false;
}
}
}
return true;
}
static int
port_role_transition_sm(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
enum port_role_transition_state_machine old_state;
struct rstp *r;
enum rstp_port_role last_role;
old_state = p->port_role_transition_sm_state;
r = p->rstp;
last_role = p->role;
switch (p->port_role_transition_sm_state) {
case PORT_ROLE_TRANSITION_SM_INIT:
if (r->begin) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_INIT_PORT_EXEC;
}
break;
case PORT_ROLE_TRANSITION_SM_INIT_PORT_EXEC:
p->role = ROLE_DISABLED;
p->learn = p->forward = false;
p->synced = false;
p->sync = p->re_root = true;
p->rr_while = p->designated_times.forward_delay;
p->fd_while = p->designated_times.max_age;
p->rb_while = 0;
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_DISABLE_PORT_EXEC;
break;
case PORT_ROLE_TRANSITION_SM_DISABLE_PORT_EXEC:
p->role = p->selected_role;
p->learn = p->forward = false;
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_DISABLE_PORT;
/* no break */
case PORT_ROLE_TRANSITION_SM_DISABLE_PORT:
if (check_selected_role_change(p, ROLE_DISABLED)) {
break;
}
else if (p->selected && !p->updt_info && !p->learning &&
!p->forwarding) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_DISABLED_PORT_EXEC;
}
break;
case PORT_ROLE_TRANSITION_SM_DISABLED_PORT_EXEC:
p->fd_while = p->designated_times.max_age;
p->synced = true;
p->rr_while = 0;
p->sync = p->re_root = false;
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_DISABLED_PORT;
/* no break */
case PORT_ROLE_TRANSITION_SM_DISABLED_PORT:
if (check_selected_role_change(p, ROLE_DISABLED)) {
break;
}
else if (p->selected && !p->updt_info &&
((p->fd_while != p->designated_times.max_age) || p->sync ||
p->re_root || !p->synced)) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_DISABLED_PORT_EXEC;
}
break;
case PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC:
p->role = ROLE_ROOT;
p->rr_while = p->designated_times.forward_delay;
p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT;
/* no break */
case PORT_ROLE_TRANSITION_SM_ROOT_PORT:
if (check_selected_role_change(p, ROLE_ROOT)) {
break;
}
else if (p->selected && !p->updt_info) {
if (p->rr_while != p->designated_times.forward_delay) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
break;
}
else if (p->re_root && p->forward) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_REROOTED_EXEC;
break;
}
else if (((p->fd_while == 0) || ((re_rooted(p) &&
(p->rb_while == 0)) &&
(r->rstp_version))) && !p->learn) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_ROOT_LEARN_EXEC;
break;
}
else if (((p->fd_while == 0) || ((re_rooted(p) &&
(p->rb_while == 0)) &&
(r->rstp_version))) && p->learn && !p->forward) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_ROOT_FORWARD_EXEC;
break;
}
else if (p->proposed && !p->agree) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_ROOT_PROPOSED_EXEC;
break;
}
else if ((all_synced(r) && !p->agree) ||
(p->proposed && p->agree)) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_ROOT_AGREED_EXEC;
break;
}
else if (!p->forward && !p->re_root) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_REROOT_EXEC;
break;
}
}
break;
case PORT_ROLE_TRANSITION_SM_REROOT_EXEC:
set_re_root_tree(p);
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
break;
case PORT_ROLE_TRANSITION_SM_ROOT_AGREED_EXEC:
p->proposed = p->sync = false;
p->agree = p->new_info = true;
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
break;
case PORT_ROLE_TRANSITION_SM_ROOT_PROPOSED_EXEC:
set_sync_tree(p);
p->proposed = false;
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
break;
case PORT_ROLE_TRANSITION_SM_ROOT_FORWARD_EXEC:
p->fd_while = 0;
p->forward = true;
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
break;
case PORT_ROLE_TRANSITION_SM_ROOT_LEARN_EXEC:
p->fd_while = forward_delay(p);
p->learn = true;
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
break;
case PORT_ROLE_TRANSITION_SM_REROOTED_EXEC:
p->re_root = false;
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
break;
case PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC:
p->role = ROLE_DESIGNATED;
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT;
/* no break */
case PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT:
if (check_selected_role_change(p, ROLE_DESIGNATED)) {
break;
}
else if (p->selected && !p->updt_info) {
if (((p->sync && !p->synced) || (p->re_root &&
(p->rr_while != 0)) || p->disputed) &&
!p->oper_edge && (p->learn || p->forward)) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_DESIGNATED_DISCARD_EXEC;
}
else if (((p->fd_while == 0)|| p->agreed || p->oper_edge) &&
((p->rr_while == 0) || !p->re_root) &&
!p->sync && !p->learn) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_DESIGNATED_LEARN_EXEC;
}
else if (((p->fd_while == 0) || p->agreed || p->oper_edge) &&
((p->rr_while == 0) || !p->re_root) &&
!p->sync && (p->learn && !p->forward)) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_DESIGNATED_FORWARD_EXEC;
}
else if (!p->forward && !p->agreed && !p->proposing &&
!p->oper_edge) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_DESIGNATED_PROPOSE_EXEC;
}
else if ((!p->learning && !p->forwarding && !p->synced) ||
(p->agreed && !p->synced) || (p->oper_edge && !p->synced) ||
(p->sync && p->synced)) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_DESIGNATED_SYNCED_EXEC;
}
else if ((p->rr_while == 0) && p->re_root) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_DESIGNATED_RETIRED_EXEC;
}
}
break;
case PORT_ROLE_TRANSITION_SM_DESIGNATED_RETIRED_EXEC:
p->re_root = false;
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
break;
case PORT_ROLE_TRANSITION_SM_DESIGNATED_SYNCED_EXEC:
p->rr_while = 0;
p->synced = true;
p->sync = false;
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
break;
case PORT_ROLE_TRANSITION_SM_DESIGNATED_PROPOSE_EXEC:
p->proposing = true;
p->edge_delay_while = edge_delay(p);
p->new_info = true;
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
break;
case PORT_ROLE_TRANSITION_SM_DESIGNATED_FORWARD_EXEC:
p->forward = true;
p->fd_while = 0;
p->agreed = p->send_rstp;
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
break;
case PORT_ROLE_TRANSITION_SM_DESIGNATED_LEARN_EXEC:
p->learn = true;
p->fd_while = forward_delay(p);
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
break;
case PORT_ROLE_TRANSITION_SM_DESIGNATED_DISCARD_EXEC:
p->learn = p->forward = p->disputed = false;
p->fd_while = forward_delay(p);
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
break;
case PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC:
p->fd_while = p->designated_times.forward_delay;
p->synced = true;
p->rr_while = 0;
p->sync = p->re_root = false;
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT;
/* no break */
case PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT:
if (check_selected_role_change(p, ROLE_ALTERNATE)) {
break;
}
else if (p->selected && !p->updt_info) {
if ((p->rb_while != (2 * p->designated_times.hello_time)) &&
(p->role == ROLE_BACKUP)) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_BACKUP_PORT_EXEC;
}
else if ((p->fd_while != forward_delay(p)) || p->sync ||
p->re_root || !p->synced) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
}
else if (p->proposed && !p->agree) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_ALTERNATE_PROPOSED_EXEC;
}
else if (( all_synced(r) && !p->agree) ||
(p->proposed && p->agree)) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_ALTERNATE_AGREED;
}
}
break;
case PORT_ROLE_TRANSITION_SM_ALTERNATE_AGREED:
p->proposed = false;
p->agree = true;
p->new_info = true;
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
break;
case PORT_ROLE_TRANSITION_SM_ALTERNATE_PROPOSED_EXEC:
set_sync_tree(p);
p->proposed = false;
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
break;
case PORT_ROLE_TRANSITION_SM_BLOCK_PORT_EXEC:
p->role = p->selected_role;
p->learn = p->forward = false;
p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_BLOCK_PORT;
/* no break */
case PORT_ROLE_TRANSITION_SM_BLOCK_PORT:
if (check_selected_role_change(p, ROLE_ALTERNATE)) {
break;
}
else if (p->selected && !p->updt_info && !p->learning &&
!p->forwarding) {
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
}
break;
case PORT_ROLE_TRANSITION_SM_BACKUP_PORT_EXEC:
p->rb_while = 2 * p->designated_times.hello_time;
p->port_role_transition_sm_state =
PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
break;
default:
OVS_NOT_REACHED();
/* no break */
}
if (old_state != p->port_role_transition_sm_state) {
r->changes = true;
VLOG_DBG("%s, port %u: Port_role_transition_sm %d -> %d",
p->rstp->name, p->port_number, old_state,
p->port_role_transition_sm_state);
}
if (last_role != p->role) {
last_role = p->role;
VLOG_DBG("%s, port %u, port role ["RSTP_PORT_ID_FMT"] = %s",
p->rstp->name, p->port_number, p->port_id,
rstp_port_role_name(p->role));
}
return 0;
}
/* [17.30 - Port state transition state machine] */
static void
enable_learning(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
/* [17.21.6 enableLearning()] An implementation dependent procedure that
* causes the Learning Process (7.8) to start learning from frames received
* on the Port. The procedure does not complete until learning has been
* enabled.
*/
rstp_port_set_state__(p, RSTP_LEARNING);
}
static void
enable_forwarding(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
/* [17.21.5 enableForwarding()] An implementation dependent procedure that
* causes the Forwarding Process (7.7) to start forwarding frames through
* the Port. The procedure does not complete until forwarding has been
* enabled.
*/
rstp_port_set_state__(p, RSTP_FORWARDING);
}
static void
disable_learning(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
/* [17.21.4 - disableLearning()] An implementation dependent procedure that
* causes the Learning Process (7.8) to stop learning from the source
* address of frames received on the Port. The procedure does not complete
* until learning has stopped.
*/
rstp_port_set_state__(p, RSTP_DISCARDING);
}
static void
disable_forwarding(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
/* [17.21.3 - disableForwarding()] An implementation dependent procedure
* that causes the Forwarding Process (7.7) to stop forwarding frames
* through the Port. The procedure does not complete until forwarding has
* stopped.
*/
rstp_port_set_state__(p, RSTP_DISCARDING);
}
static int
port_state_transition_sm(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
enum port_state_transition_state_machine old_state;
struct rstp *r;
old_state = p->port_state_transition_sm_state;
r = p->rstp;
switch (p->port_state_transition_sm_state) {
case PORT_STATE_TRANSITION_SM_INIT:
if (r->begin) {
p->port_state_transition_sm_state =
PORT_STATE_TRANSITION_SM_DISCARDING_EXEC;
}
break;
case PORT_STATE_TRANSITION_SM_DISCARDING_EXEC:
disable_learning(p);
p->learning = false;
disable_forwarding(p);
p->forwarding = false;
p->port_state_transition_sm_state =
PORT_STATE_TRANSITION_SM_DISCARDING;
/* no break */
case PORT_STATE_TRANSITION_SM_DISCARDING:
if (p->learn) {
p->port_state_transition_sm_state =
PORT_STATE_TRANSITION_SM_LEARNING_EXEC;
}
break;
case PORT_STATE_TRANSITION_SM_LEARNING_EXEC:
enable_learning(p);
p->learning = true;
p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_LEARNING;
/* no break */
case PORT_STATE_TRANSITION_SM_LEARNING:
if (!p->learn) {
p->port_state_transition_sm_state =
PORT_STATE_TRANSITION_SM_DISCARDING_EXEC;
}
else if (p->forward) {
p->port_state_transition_sm_state =
PORT_STATE_TRANSITION_SM_FORWARDING_EXEC;
}
break;
case PORT_STATE_TRANSITION_SM_FORWARDING_EXEC:
enable_forwarding(p);
p->forwarding = true;
p->port_state_transition_sm_state =
PORT_STATE_TRANSITION_SM_FORWARDING;
/* no break */
case PORT_STATE_TRANSITION_SM_FORWARDING:
if (!p->forward) {
p->port_state_transition_sm_state =
PORT_STATE_TRANSITION_SM_DISCARDING_EXEC;
}
break;
default:
OVS_NOT_REACHED();
/* no break */
}
if (old_state != p->port_state_transition_sm_state) {
r->changes = true;
VLOG_DBG("%s, port %u: Port_state_transition_sm %d -> %d",
p->rstp->name, p->port_number, old_state,
p->port_state_transition_sm_state);
}
return 0;
}
/* [17.31 - Topology Change state machine] */
static void
new_tc_while(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
struct rstp *r;
r = p->rstp;
if (p->tc_while == 0 && p->send_rstp == true) {
p->tc_while = r->bridge_hello_time + 1;
p->new_info = true;
}
else if (p->tc_while == 0 && p->send_rstp == false) {
p->tc_while = r->bridge_max_age + r->bridge_forward_delay;
}
}
/* [17.21.18 setTcPropTree()]
* Sets tcprop for all Ports except the Port that called the procedure.
*/
static void
set_tc_prop_tree(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
struct rstp *r;
struct rstp_port *p1;
r = p->rstp;
if (r->ports_count > 0) {
LIST_FOR_EACH (p1, node, &r->ports) {
/* Set tc_prop on every port, except the one calling this
* function.
*/
if (p1->port_number != p->port_number) {
p1->tc_prop = true;
}
}
}
}
static void
set_tc_prop_bridge(struct rstp_port *p) /* not specified in 802.1D-2004. */
OVS_REQUIRES(rstp_mutex)
{
set_tc_prop_tree(p); /* see 802.1w-2001. */
}
static int
topology_change_sm(struct rstp_port *p)
OVS_REQUIRES(rstp_mutex)
{
enum topology_change_state_machine old_state;
struct rstp *r;
old_state = p->topology_change_sm_state;
r = p->rstp;
switch (p->topology_change_sm_state) {
case TOPOLOGY_CHANGE_SM_INIT:
if (r->begin) {
p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_INACTIVE_EXEC;
}
break;
case TOPOLOGY_CHANGE_SM_INACTIVE_EXEC:
p->fdb_flush = true;
p->tc_while = 0;
p->tc_ack = false;
p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_INACTIVE;
/* no break */
case TOPOLOGY_CHANGE_SM_INACTIVE:
if (p->learn && !p->fdb_flush) {
p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING_EXEC;
}
break;
case TOPOLOGY_CHANGE_SM_LEARNING_EXEC:
p->rcvd_tc = p->rcvd_tcn = p->rcvd_tc_ack = false;
p->tc_prop = p->rcvd_tc_ack = false;
p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING;
/* no break */
case TOPOLOGY_CHANGE_SM_LEARNING:
if ((p->role != ROLE_ROOT) && (p->role != ROLE_DESIGNATED) &&
!(p->learn || p->learning) && !(p->rcvd_tc || p->rcvd_tcn ||
p->rcvd_tc_ack || p->tc_prop)) {
p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_INACTIVE_EXEC;
}
else if (p->rcvd_tc || p->rcvd_tcn || p->rcvd_tc_ack || p->tc_prop) {
p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING_EXEC;
}
else if (((p->role == ROLE_ROOT) || (p->role == ROLE_DESIGNATED)) &&
p->forward && !p->oper_edge) {
p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_DETECTED_EXEC;
}
break;
case TOPOLOGY_CHANGE_SM_DETECTED_EXEC:
new_tc_while(p);
set_tc_prop_tree(p);
p->new_info = true;
p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE_EXEC;
/* no break */
case TOPOLOGY_CHANGE_SM_ACTIVE_EXEC:
p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE;
/* no break */
case TOPOLOGY_CHANGE_SM_ACTIVE:
if (((p->role != ROLE_ROOT) && (p->role != ROLE_DESIGNATED)) ||
p->oper_edge) {
p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING_EXEC;
}
else if (p->rcvd_tcn) {
p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_NOTIFIED_TCN_EXEC;
}
else if (p->rcvd_tc) {
p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_NOTIFIED_TC_EXEC;
}
else if (p->tc_prop && !p->oper_edge) {
p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_PROPAGATING_EXEC;
}
else if (p->rcvd_tc_ack) {
p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACKNOWLEDGED_EXEC;
}
break;
case TOPOLOGY_CHANGE_SM_ACKNOWLEDGED_EXEC:
p->tc_while = 0;
p->rcvd_tc_ack = false;
p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE;
break;
case TOPOLOGY_CHANGE_SM_PROPAGATING_EXEC:
new_tc_while(p);
p->fdb_flush = true;
p->tc_prop = false;
p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE;
break;
case TOPOLOGY_CHANGE_SM_NOTIFIED_TC_EXEC:
p->rcvd_tcn = p->rcvd_tc = false;
if (p->role == ROLE_DESIGNATED) {
p->tc_ack = true;
}
set_tc_prop_bridge(p);
p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE;
break;
case TOPOLOGY_CHANGE_SM_NOTIFIED_TCN_EXEC:
new_tc_while(p);
p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE;
break;
default:
OVS_NOT_REACHED();
/* no break */
}
if (old_state != p->topology_change_sm_state) {
r->changes = true;
VLOG_DBG("%s, port %u: Topology_change_sm %d -> %d", p->rstp->name,
p->port_number, old_state, p->topology_change_sm_state);
}
return 0;
}
/****************************************************************************
* [17.6] Priority vector calculation helper functions
****************************************************************************/
/* [17.6]
* This message priority vector is superior to the port priority vector and
* will replace it if, and only if, the message priority vector is better
* than the port priority vector, or the message has been transmitted from the
* same Designated Bridge and Designated Port as the port priority vector,
* i.e.,if the following is true:
* ((RD < RootBridgeID)) ||
* ((RD == RootBridgeID) && (RPCD < RootPathCost)) ||
* ((RD == RootBridgeID) && (RPCD == RootPathCost) &&
* (D < designated_bridge_id)) ||
* ((RD == RootBridgeID) && (RPCD == RootPathCost) &&
* (D == designated_bridge_id) && (PD < designated_port_id)) ||
* ((D == designated_bridge_id.BridgeAddress) &&
* (PD == designated_port_id.PortNumber))
*/
/* compare_rstp_priority_vector() compares two struct rstp_priority_vector and
* returns a value indicating if the first rstp_priority_vector is superior,
* same or inferior to the second one.
*/
static enum vector_comparison
compare_rstp_priority_vector(struct rstp_priority_vector *v1,
struct rstp_priority_vector *v2)
{
VLOG_DBG("v1: "RSTP_ID_FMT", %u, "RSTP_ID_FMT", %d",
RSTP_ID_ARGS(v1->root_bridge_id), v1->root_path_cost,
RSTP_ID_ARGS(v1->designated_bridge_id), v1->designated_port_id);
VLOG_DBG("v2: "RSTP_ID_FMT", %u, "RSTP_ID_FMT", %d",
RSTP_ID_ARGS(v2->root_bridge_id), v2->root_path_cost,
RSTP_ID_ARGS(v2->designated_bridge_id), v2->designated_port_id);
if ((v1->root_bridge_id < v2->root_bridge_id) ||
((v1->root_bridge_id == v2->root_bridge_id) &&
(v1->root_path_cost < v2->root_path_cost)) ||
((v1->root_bridge_id == v2->root_bridge_id) &&
(v1->root_path_cost == v2->root_path_cost) &&
(v1->designated_bridge_id < v2->designated_bridge_id)) ||
((v1->root_bridge_id == v2->root_bridge_id) &&
(v1->root_path_cost == v2->root_path_cost) &&
(v1->designated_bridge_id == v2->designated_bridge_id) &&
(v1->designated_port_id < v2->designated_port_id))) {
VLOG_DBG("superior_absolute");
return SUPERIOR;
}
else if (((v1->root_bridge_id > v2->root_bridge_id) ||
((v1->root_bridge_id == v2->root_bridge_id) &&
(v1->root_path_cost > v2->root_path_cost)) ||
((v1->root_bridge_id == v2->root_bridge_id) &&
(v1->root_path_cost == v2->root_path_cost) &&
(v1->designated_bridge_id > v2->designated_bridge_id)) ||
((v1->root_bridge_id == v2->root_bridge_id) &&
(v1->root_path_cost == v2->root_path_cost) &&
(v1->designated_bridge_id == v2->designated_bridge_id) &&
(v1->designated_port_id > v2->designated_port_id))) &&
(v1->designated_bridge_id == v2->designated_bridge_id) &&
(v1->designated_port_id == v2->designated_port_id)) {
VLOG_DBG("superior_same_des");
return SUPERIOR;
}
else if ((v1->root_bridge_id == v2->root_bridge_id) &&
(v1->root_path_cost == v2->root_path_cost) &&
(v1->designated_bridge_id == v2->designated_bridge_id) &&
(v1->designated_port_id == v2->designated_port_id)) {
VLOG_DBG("same");
return SAME;
}
else {
VLOG_DBG("inferior");
return INFERIOR;
}
}
static bool
rstp_times_equal(struct rstp_times *t1, struct rstp_times *t2) {
return ((t1->forward_delay == t2->forward_delay) &&
(t1->hello_time == t2->hello_time) &&
(t1->max_age == t2->max_age) &&
(t1->message_age == t2->message_age));
}