2 * Copyright (c) 2011-2014 M3S, Srl - Italy
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * Rapid Spanning Tree Protocol (IEEE 802.1D-2004) state machines
22 * Martino Fornasa <mf@fornasa.it>
23 * Daniele Venturino <daniele.venturino@m3s.it>
25 * References to IEEE 802.1D-2004 standard are enclosed in square brackets.
26 * E.g. [17.3], [Table 17-1], etc.
32 #include "rstp-state-machines.h"
33 #include <arpa/inet.h>
35 #include <netinet/in.h>
37 #include <sys/types.h>
38 #include "byte-order.h"
39 #include "connectivity.h"
47 VLOG_DEFINE_THIS_MODULE(rstp_sm);
49 #define ROLE_FLAG_MASK 0xC
50 #define ROLE_FLAG_SHIFT 2
60 CONFIGURATION_BPDU_SIZE = 35,
61 TOPOLOGY_CHANGE_NOTIFICATION_BPDU_SIZE = 4,
62 RAPID_SPANNING_TREE_BPDU_SIZE = 36
65 enum vector_comparison {
71 static void decrement_timer(uint16_t *);
72 static void rstp_send_bpdu(struct rstp_port *, const void *, size_t)
73 OVS_REQUIRES(rstp_mutex);
74 static int validate_received_bpdu(struct rstp_port *, const void *, size_t)
75 OVS_REQUIRES(rstp_mutex);
76 static ovs_be16 time_encode(uint8_t);
77 static uint8_t time_decode(ovs_be16);
78 static enum vector_comparison
79 compare_rstp_priority_vector(struct rstp_priority_vector *,
80 struct rstp_priority_vector *);
81 static bool rstp_times_equal(struct rstp_times *, struct rstp_times *);
83 /* Per-Bridge State Machine */
84 static int port_role_selection_sm(struct rstp *)
85 OVS_REQUIRES(rstp_mutex);
86 /* Per-Port State Machines */
87 static int port_receive_sm(struct rstp_port *)
88 OVS_REQUIRES(rstp_mutex);
89 static int port_protocol_migration_sm(struct rstp_port *)
90 OVS_REQUIRES(rstp_mutex);
91 static int bridge_detection_sm(struct rstp_port *)
92 OVS_REQUIRES(rstp_mutex);
93 static int port_transmit_sm(struct rstp_port *)
94 OVS_REQUIRES(rstp_mutex);
95 static int port_information_sm(struct rstp_port *)
96 OVS_REQUIRES(rstp_mutex);
97 static int port_role_transition_sm(struct rstp_port *)
98 OVS_REQUIRES(rstp_mutex);
99 static int port_state_transition_sm(struct rstp_port *)
100 OVS_REQUIRES(rstp_mutex);
101 static int topology_change_sm(struct rstp_port *)
102 OVS_REQUIRES(rstp_mutex);
103 /* port_timers_sm() not defined as a state machine */
106 process_received_bpdu__(struct rstp_port *p, const void *bpdu,
108 OVS_REQUIRES(rstp_mutex)
110 struct rstp *rstp = p->rstp;
112 if (!p->port_enabled) {
118 if (validate_received_bpdu(p, bpdu, bpdu_size) == 0) {
120 p->rx_rstp_bpdu_cnt++;
122 memcpy(&p->received_bpdu_buffer, bpdu, sizeof(struct rstp_bpdu));
124 rstp->changes = true;
127 VLOG_DBG("%s, port %u: Bad STP or RSTP BPDU received", p->rstp->name,
133 /* Returns 0 on success. */
135 validate_received_bpdu(struct rstp_port *p, const void *bpdu, size_t bpdu_size)
136 OVS_REQUIRES(rstp_mutex)
138 /* Validation of received BPDU, see [9.3.4]. */
139 const struct rstp_bpdu *temp;
142 if (bpdu_size < TOPOLOGY_CHANGE_NOTIFICATION_BPDU_SIZE ||
143 ntohs(temp->protocol_identifier) != 0) {
146 if (temp->bpdu_type == CONFIGURATION_BPDU
147 && bpdu_size >= CONFIGURATION_BPDU_SIZE
148 && (time_decode(temp->message_age) < time_decode(temp->max_age))) {
149 if ((ntohll(temp->designated_bridge_id) !=
150 p->rstp->bridge_identifier)
151 || ((ntohll(temp->designated_bridge_id) ==
152 p->rstp->bridge_identifier)
153 && (ntohs(temp->designated_port_id) != p->port_id))) {
158 } else if (temp->bpdu_type == TOPOLOGY_CHANGE_NOTIFICATION_BPDU) {
160 } else if (temp->bpdu_type == RAPID_SPANNING_TREE_BPDU &&
161 bpdu_size >= RAPID_SPANNING_TREE_BPDU_SIZE) {
171 * This method is invoked to move the State Machines. The SMs move only if the
172 * boolean 'changes' is true, meaning that something changed and the SMs need
173 * to work to process this change.
174 * The boolean 'changes' is set every time a SM modifies its state, a BPDU is
175 * received, a timer expires or port down event is detected. If a parameter is
176 * set by management, then 'changes' is set.
178 #define MAX_RSTP_ITERATIONS 1000 /* safeguard */
180 move_rstp__(struct rstp *rstp)
181 OVS_REQUIRES(rstp_mutex)
186 while (rstp->changes == true && num_iterations < MAX_RSTP_ITERATIONS) {
189 VLOG_DBG("%s: move_rstp()", rstp->name);
191 rstp->changes = false;
192 port_role_selection_sm(rstp);
193 if (rstp->ports_count > 0) {
194 LIST_FOR_EACH (p, node, &rstp->ports) {
195 if (p->rstp_state != RSTP_DISABLED) {
197 bridge_detection_sm(p);
198 port_information_sm(p);
199 port_role_transition_sm(p);
200 port_state_transition_sm(p);
201 topology_change_sm(p);
203 port_protocol_migration_sm(p);
208 seq_change(connectivity_seq_get());
210 if (num_iterations >= MAX_RSTP_ITERATIONS) {
211 VLOG_ERR("%s: move_rstp() reached the iteration safeguard limit!",
217 void decrease_rstp_port_timers__(struct rstp *r)
218 OVS_REQUIRES(rstp_mutex)
222 if (r->ports_count > 0) {
223 LIST_FOR_EACH (p, node, &r->ports) {
224 decrement_timer(&p->hello_when);
225 decrement_timer(&p->tc_while);
226 decrement_timer(&p->fd_while);
227 decrement_timer(&p->rcvd_info_while);
228 decrement_timer(&p->rr_while);
229 decrement_timer(&p->rb_while);
230 decrement_timer(&p->mdelay_while);
231 decrement_timer(&p->edge_delay_while);
232 decrement_timer(&p->tx_count);
241 decrement_timer(uint16_t *timer)
248 /* Bridge State Machine. */
249 /* [17.28] Port Role Selection state machine. */
252 updt_role_disabled_tree(struct rstp *r)
253 OVS_REQUIRES(rstp_mutex)
257 if (r->ports_count > 0) {
258 LIST_FOR_EACH (p, node, &r->ports) {
259 p->selected_role = ROLE_DISABLED;
265 clear_reselect_tree(struct rstp *r)
266 OVS_REQUIRES(rstp_mutex)
270 if (r->ports_count > 0) {
271 LIST_FOR_EACH (p, node, &r->ports) {
278 updt_roles_tree__(struct rstp *r)
279 OVS_REQUIRES(rstp_mutex)
283 struct rstp_priority_vector best_vector, candidate_vector;
286 best_vector = r->bridge_priority;
288 r->root_times = r->bridge_times;
289 /* Letters a) b) c) */
290 if (r->ports_count > 0) {
291 LIST_FOR_EACH (p, node, &r->ports) {
292 uint32_t old_root_path_cost;
293 uint32_t root_path_cost;
295 if (p->info_is != INFO_IS_RECEIVED) {
299 candidate_vector = p->port_priority;
300 candidate_vector.bridge_port_id = p->port_id;
301 old_root_path_cost = candidate_vector.root_path_cost;
302 root_path_cost = old_root_path_cost + p->port_path_cost;
303 candidate_vector.root_path_cost = root_path_cost;
305 if ((candidate_vector.designated_bridge_id & 0xffffffffffffULL) ==
306 (r->bridge_priority.designated_bridge_id & 0xffffffffffffULL)) {
309 if (compare_rstp_priority_vector(&candidate_vector, &best_vector)
311 best_vector = candidate_vector;
312 r->root_times = p->port_times;
313 r->root_times.message_age++;
314 vsel = p->port_number;
318 r->root_priority = best_vector;
319 r->root_port_id = best_vector.bridge_port_id;
320 VLOG_DBG("%s: new Root is "RSTP_ID_FMT, r->name,
321 RSTP_ID_ARGS(r->root_priority.root_bridge_id));
323 if (r->ports_count > 0) {
324 LIST_FOR_EACH (p, node, &r->ports) {
325 p->designated_priority_vector.root_bridge_id =
326 r->root_priority.root_bridge_id;
327 p->designated_priority_vector.root_path_cost =
328 r->root_priority.root_path_cost;
329 p->designated_priority_vector.designated_bridge_id =
330 r->bridge_identifier;
331 p->designated_priority_vector.designated_port_id =
333 p->designated_times = r->root_times;
334 p->designated_times.hello_time = r->bridge_times.hello_time;
337 if (r->ports_count > 0) {
338 LIST_FOR_EACH (p, node, &r->ports) {
339 switch (p->info_is) {
340 case INFO_IS_DISABLED:
341 p->selected_role = ROLE_DISABLED;
345 p->selected_role = ROLE_DESIGNATED;
348 p->selected_role = ROLE_DESIGNATED;
349 if (compare_rstp_priority_vector(&p->port_priority,
350 &p->designated_priority_vector)
352 || !rstp_times_equal(&p->designated_times, &r->root_times)) {
356 case INFO_IS_RECEIVED:
357 if (vsel == p->port_number) { /* Letter i) */
358 p->selected_role = ROLE_ROOT;
359 p->updt_info = false;
360 } else if (compare_rstp_priority_vector(
361 &p->designated_priority_vector, &p->port_priority)
363 if (p->port_priority.designated_bridge_id !=
364 r->bridge_identifier) {
365 p->selected_role = ROLE_ALTERNATE;
366 p->updt_info = false;
368 p->selected_role = ROLE_BACKUP;
369 p->updt_info = false;
372 p->selected_role = ROLE_DESIGNATED;
382 seq_change(connectivity_seq_get());
386 set_selected_tree(struct rstp *r)
387 OVS_REQUIRES(rstp_mutex)
391 if (r->ports_count > 0) {
392 LIST_FOR_EACH (p, node, &r->ports) {
397 LIST_FOR_EACH (p, node, &r->ports) {
404 port_role_selection_sm(struct rstp *r)
405 OVS_REQUIRES(rstp_mutex)
407 enum port_role_selection_state_machine old_state;
410 old_state = r->port_role_selection_sm_state;
412 switch (r->port_role_selection_sm_state) {
413 case PORT_ROLE_SELECTION_SM_INIT:
415 r->port_role_selection_sm_state =
416 PORT_ROLE_SELECTION_SM_INIT_BRIDGE_EXEC;
419 case PORT_ROLE_SELECTION_SM_INIT_BRIDGE_EXEC:
420 updt_role_disabled_tree(r);
421 r->port_role_selection_sm_state = PORT_ROLE_SELECTION_SM_INIT_BRIDGE;
423 case PORT_ROLE_SELECTION_SM_INIT_BRIDGE:
424 r->port_role_selection_sm_state =
425 PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC;
427 case PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC:
428 clear_reselect_tree(r);
429 updt_roles_tree__(r);
430 set_selected_tree(r);
431 r->port_role_selection_sm_state =
432 PORT_ROLE_SELECTION_SM_ROLE_SELECTION;
434 case PORT_ROLE_SELECTION_SM_ROLE_SELECTION:
435 if (r->ports_count > 0) {
436 LIST_FOR_EACH (p, node, &r->ports) {
438 r->port_role_selection_sm_state =
439 PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC;
449 if (old_state != r->port_role_selection_sm_state) {
451 VLOG_DBG("%s: Port_role_selection_sm %d -> %d", r->name,
452 old_state, r->port_role_selection_sm_state);
457 /* Port State Machines */
459 /* [17.23 - Port receive state machine] */
462 updt_bpdu_version(struct rstp_port *p) /* [17.21.22] */
463 OVS_REQUIRES(rstp_mutex)
465 switch (p->received_bpdu_buffer.bpdu_type) {
466 case CONFIGURATION_BPDU:
467 case TOPOLOGY_CHANGE_NOTIFICATION_BPDU:
468 p->rcvd_rstp = false;
471 case RAPID_SPANNING_TREE_BPDU:
482 port_receive_sm(struct rstp_port *p)
483 OVS_REQUIRES(rstp_mutex)
485 enum port_receive_state_machine old_state;
488 old_state = p->port_receive_sm_state;
491 switch (p->port_receive_sm_state) {
492 case PORT_RECEIVE_SM_INIT:
493 if (r->begin || ((p->rcvd_bpdu || (p->edge_delay_while !=
494 r->migrate_time)) && !p->port_enabled)) {
495 p->port_receive_sm_state = PORT_RECEIVE_SM_DISCARD_EXEC;
498 case PORT_RECEIVE_SM_DISCARD_EXEC:
499 p->rcvd_bpdu = p->rcvd_rstp = p->rcvd_stp = false;
501 p->edge_delay_while = r->migrate_time;
502 p->port_receive_sm_state = PORT_RECEIVE_SM_DISCARD;
504 case PORT_RECEIVE_SM_DISCARD:
505 if (p->rcvd_bpdu && p->port_enabled) {
506 p->port_receive_sm_state = PORT_RECEIVE_SM_RECEIVE_EXEC;
509 case PORT_RECEIVE_SM_RECEIVE_EXEC:
510 updt_bpdu_version(p);
511 p->oper_edge = p->rcvd_bpdu = false;
513 p->edge_delay_while = r->migrate_time;
514 p->port_receive_sm_state = PORT_RECEIVE_SM_RECEIVE;
516 case PORT_RECEIVE_SM_RECEIVE:
517 if (p->rcvd_bpdu && p->port_enabled && !p->rcvd_msg) {
518 p->port_receive_sm_state = PORT_RECEIVE_SM_RECEIVE_EXEC;
525 if (old_state != p->port_receive_sm_state) {
527 VLOG_DBG("%s, port %u: Port_receive_sm %d -> %d", p->rstp->name,
528 p->port_number, old_state, p->port_receive_sm_state);
533 /* [17.24 - Port Protocol Migration state machine] */
535 port_protocol_migration_sm(struct rstp_port *p)
536 OVS_REQUIRES(rstp_mutex)
538 enum port_protocol_migration_state_machine old_state;
541 old_state = p->port_protocol_migration_sm_state;
544 switch (p->port_protocol_migration_sm_state) {
545 case PORT_PROTOCOL_MIGRATION_SM_INIT:
546 p->port_protocol_migration_sm_state =
547 PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC;
549 case PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC:
551 p->send_rstp = r->rstp_version;
552 p->mdelay_while = r->migrate_time;
553 p->port_protocol_migration_sm_state =
554 PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP;
556 case PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP:
557 if (p->mdelay_while == 0) {
558 p->port_protocol_migration_sm_state =
559 PORT_PROTOCOL_MIGRATION_SM_SENSING_EXEC;
560 } else if ((p->mdelay_while != r->migrate_time) && !p->port_enabled) {
561 p->port_protocol_migration_sm_state =
562 PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC;
565 case PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP_EXEC:
566 p->send_rstp = false;
567 p->mdelay_while = r->migrate_time;
568 p->port_protocol_migration_sm_state =
569 PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP;
571 case PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP:
572 if ((p->mdelay_while == 0) || (!p->port_enabled) || p->mcheck) {
573 p->port_protocol_migration_sm_state =
574 PORT_PROTOCOL_MIGRATION_SM_SENSING_EXEC;
577 case PORT_PROTOCOL_MIGRATION_SM_SENSING_EXEC:
578 p->rcvd_rstp = false;
580 p->port_protocol_migration_sm_state =
581 PORT_PROTOCOL_MIGRATION_SM_SENSING;
583 case PORT_PROTOCOL_MIGRATION_SM_SENSING:
584 if (!p->port_enabled || p->mcheck || ((r->rstp_version) &&
585 !p->send_rstp && p->rcvd_rstp)) {
586 p->port_protocol_migration_sm_state =
587 PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC;
588 } else if (p->send_rstp && p->rcvd_stp) {
589 p->port_protocol_migration_sm_state =
590 PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP_EXEC;
597 if (old_state != p->port_protocol_migration_sm_state) {
599 VLOG_DBG("%s, port %u: port_protocol_migration_sm %d -> %d",
600 p->rstp->name, p->port_number, old_state,
601 p->port_protocol_migration_sm_state);
607 /* [17.25 - Bridge Detection state machine] */
609 bridge_detection_sm(struct rstp_port *p)
610 OVS_REQUIRES(rstp_mutex)
612 enum bridge_detection_state_machine old_state;
615 old_state = p->bridge_detection_sm_state;
618 switch (p->bridge_detection_sm_state) {
619 case BRIDGE_DETECTION_SM_INIT:
620 if (r->begin && p->admin_edge) {
621 p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_EDGE_EXEC;
622 } else if (r->begin && !p->admin_edge) {
623 p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_NOT_EDGE_EXEC;
626 case BRIDGE_DETECTION_SM_EDGE_EXEC:
628 p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_EDGE;
630 case BRIDGE_DETECTION_SM_EDGE:
631 if ((!p->port_enabled && !p->admin_edge) || !p->oper_edge) {
632 p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_NOT_EDGE_EXEC;
635 case BRIDGE_DETECTION_SM_NOT_EDGE_EXEC:
636 p->oper_edge = false;
637 p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_NOT_EDGE;
639 case BRIDGE_DETECTION_SM_NOT_EDGE:
640 if ((!p->port_enabled && p->admin_edge)
641 || ((p->edge_delay_while == 0) && p->auto_edge && p->send_rstp
643 p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_EDGE_EXEC;
650 if (old_state != p->bridge_detection_sm_state) {
652 VLOG_DBG("%s, port %u: bridge_detection_sm %d -> %d", p->rstp->name,
653 p->port_number, old_state, p->bridge_detection_sm_state);
658 /* [17.26 - Port Transmit state machine] */
660 rstp_send_bpdu(struct rstp_port *p, const void *bpdu, size_t bpdu_size)
661 OVS_REQUIRES(rstp_mutex)
663 struct eth_header *eth;
664 struct llc_header *llc;
668 pkt = ofpbuf_new(ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size);
669 eth = ofpbuf_put_zeros(pkt, sizeof *eth);
670 llc = ofpbuf_put_zeros(pkt, sizeof *llc);
671 ofpbuf_set_frame(pkt, eth);
672 ofpbuf_set_l3(pkt, ofpbuf_put(pkt, bpdu, bpdu_size));
675 memcpy(eth->eth_dst, eth_addr_stp, ETH_ADDR_LEN);
676 /* p->rstp->send_bpdu() must fill in source address. */
677 eth->eth_type = htons(ofpbuf_size(pkt) - ETH_HEADER_LEN);
680 llc->llc_dsap = STP_LLC_DSAP;
681 llc->llc_ssap = STP_LLC_SSAP;
682 llc->llc_cntl = STP_LLC_CNTL;
683 p->rstp->send_bpdu(pkt, p->aux, p->rstp->aux);
687 record_agreement(struct rstp_port *p)
688 OVS_REQUIRES(rstp_mutex)
693 if (r->rstp_version && p->oper_point_to_point_mac &&
694 ((p->received_bpdu_buffer.flags & BPDU_FLAG_AGREEMENT))) {
696 p->proposing = false;
703 set_tc_flags(struct rstp_port *p)
704 OVS_REQUIRES(rstp_mutex)
706 /* Sets rcvd_tc and/or rcvd_tc_ack if the Topology Change and/or Topology
707 * Change Acknowledgment flags, respectively, are set in a ConfigBPDU or
710 if (p->received_bpdu_buffer.bpdu_type == CONFIGURATION_BPDU ||
711 p->received_bpdu_buffer.bpdu_type == RAPID_SPANNING_TREE_BPDU) {
712 if ((p->received_bpdu_buffer.flags & BPDU_FLAG_TOPCHANGE) != 0) {
715 if ((p->received_bpdu_buffer.flags & BPDU_FLAG_TOPCHANGEACK) != 0) {
716 p->rcvd_tc_ack = true;
719 /* Sets rcvd_tcn true if the BPDU is a TCN BPDU. */
720 if (p->received_bpdu_buffer.bpdu_type
721 == TOPOLOGY_CHANGE_NOTIFICATION_BPDU) {
727 record_dispute(struct rstp_port *p)
728 OVS_REQUIRES(rstp_mutex)
730 if ((p->received_bpdu_buffer.flags & BPDU_FLAG_LEARNING) != 0) {
732 p->proposing = false;
737 record_proposal(struct rstp_port *p)
738 OVS_REQUIRES(rstp_mutex)
740 enum port_flag role =
741 ((p->received_bpdu_buffer.flags) & ROLE_FLAG_MASK) >> ROLE_FLAG_SHIFT;
743 if ((role == PORT_DES)
744 && ((p->received_bpdu_buffer.flags & BPDU_FLAG_PROPOSAL) != 0)) {
750 record_priority(struct rstp_port *p)
751 OVS_REQUIRES(rstp_mutex)
753 p->port_priority.root_bridge_id = p->msg_priority.root_bridge_id;
754 p->port_priority.root_path_cost = p->msg_priority.root_path_cost;
755 p->port_priority.designated_bridge_id =
756 p->msg_priority.designated_bridge_id;
757 p->port_priority.designated_port_id = p->msg_priority.designated_port_id;
761 record_times(struct rstp_port *p)
762 OVS_REQUIRES(rstp_mutex)
764 p->port_times = p->msg_times;
765 if (p->msg_times.hello_time == 0) {
766 p->port_times.hello_time = 1;
771 updt_rcvd_info_while(struct rstp_port *p)
772 OVS_REQUIRES(rstp_mutex)
775 * The value assigned to rcvdInfoWhile is the three times the Hello Time,
776 * if Message Age, incremented by 1 second and rounded to the nearest whole
777 * second, does not exceed Max Age, and is zero otherwise.
779 if (p->port_times.message_age < p->port_times.max_age) {
780 p->rcvd_info_while = p->port_times.hello_time * 3;
782 p->rcvd_info_while = 0;
786 /* Times are internally held in seconds, while the protocol uses 1/256 seconds.
787 * time_encode() is used to convert time values sent in bpdus, while
788 * time_decode() is used to convert time values received in bpdus.
791 time_encode(uint8_t value)
793 return htons(value * 256);
797 time_decode(ovs_be16 encoded)
799 return ntohs(encoded) / 256;
804 tx_config(struct rstp_port *p)
805 OVS_REQUIRES(rstp_mutex)
807 struct rstp_bpdu bpdu;
809 bpdu.protocol_identifier = htons(0);
810 bpdu.protocol_version_identifier = 0;
811 bpdu.bpdu_type = CONFIGURATION_BPDU;
812 bpdu.root_bridge_id = htonll(p->designated_priority_vector.root_bridge_id);
813 bpdu.root_path_cost = htonl(p->designated_priority_vector.root_path_cost);
814 bpdu.designated_bridge_id =
815 htonll(p->designated_priority_vector.designated_bridge_id);
816 bpdu.designated_port_id =
817 htons(p->designated_priority_vector.designated_port_id);
818 bpdu.message_age = time_encode(p->designated_times.message_age);
819 bpdu.max_age = time_encode(p->designated_times.max_age);
820 bpdu.hello_time = time_encode(p->designated_times.hello_time);
821 bpdu.forward_delay = time_encode(p->designated_times.forward_delay);
823 if (p->tc_while != 0) {
824 bpdu.flags |= BPDU_FLAG_TOPCHANGE;
826 if (p->tc_ack != 0) {
827 bpdu.flags |= BPDU_FLAG_TOPCHANGEACK;
829 rstp_send_bpdu(p, &bpdu, sizeof(struct rstp_bpdu));
834 tx_rstp(struct rstp_port *p)
835 OVS_REQUIRES(rstp_mutex)
837 struct rstp_bpdu bpdu;
839 bpdu.protocol_identifier = htons(0);
840 bpdu.protocol_version_identifier = 2;
841 bpdu.bpdu_type = RAPID_SPANNING_TREE_BPDU;
842 bpdu.root_bridge_id = htonll(p->designated_priority_vector.root_bridge_id);
843 bpdu.root_path_cost = htonl(p->designated_priority_vector.root_path_cost);
844 bpdu.designated_bridge_id =
845 htonll(p->designated_priority_vector.designated_bridge_id);
846 bpdu.designated_port_id =
847 htons(p->designated_priority_vector.designated_port_id);
848 bpdu.message_age = time_encode(p->designated_times.message_age);
849 bpdu.max_age = time_encode(p->designated_times.max_age);
850 bpdu.hello_time = time_encode(p->designated_times.hello_time);
851 bpdu.forward_delay = time_encode(p->designated_times.forward_delay);
856 bpdu.flags = PORT_ROOT << ROLE_FLAG_SHIFT;
858 case ROLE_DESIGNATED:
859 bpdu.flags = PORT_DES << ROLE_FLAG_SHIFT;
863 bpdu.flags = PORT_ALT_BACK << ROLE_FLAG_SHIFT;
866 /* Should not happen! */
867 VLOG_ERR("%s transmitting bpdu in disabled role on port "
868 RSTP_PORT_ID_FMT, p->rstp->name, p->port_id);
872 bpdu.flags |= BPDU_FLAG_AGREEMENT;
875 bpdu.flags |= BPDU_FLAG_PROPOSAL;
877 if (p->tc_while != 0) {
878 bpdu.flags |= BPDU_FLAG_TOPCHANGE;
881 bpdu.flags |= BPDU_FLAG_LEARNING;
884 bpdu.flags |= BPDU_FLAG_FORWARDING;
886 bpdu.version1_length = 0;
887 rstp_send_bpdu(p, &bpdu, sizeof(struct rstp_bpdu));
892 tx_tcn(struct rstp_port *p)
893 OVS_REQUIRES(rstp_mutex)
895 struct rstp_bpdu bpdu;
897 memset(&bpdu, 0, sizeof(struct rstp_bpdu));
899 bpdu.protocol_identifier = htons(0);
900 bpdu.protocol_version_identifier = 0;
901 bpdu.bpdu_type = TOPOLOGY_CHANGE_NOTIFICATION_BPDU;
902 rstp_send_bpdu(p, &bpdu, sizeof(struct rstp_bpdu));
906 port_transmit_sm(struct rstp_port *p)
907 OVS_REQUIRES(rstp_mutex)
909 enum port_transmit_state_machine old_state;
912 old_state = p->port_transmit_sm_state;
915 switch (p->port_transmit_sm_state) {
916 case PORT_TRANSMIT_SM_INIT:
918 p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_INIT_EXEC;
921 case PORT_TRANSMIT_SM_TRANSMIT_INIT_EXEC:
924 p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_INIT;
926 case PORT_TRANSMIT_SM_TRANSMIT_INIT:
927 p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC;
929 case PORT_TRANSMIT_SM_TRANSMIT_PERIODIC_EXEC:
930 p->new_info = p->new_info || (p->role == ROLE_DESIGNATED ||
931 (p->role == ROLE_ROOT && p->tc_while != 0));
932 p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_PERIODIC;
934 case PORT_TRANSMIT_SM_TRANSMIT_PERIODIC:
935 p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC;
937 case PORT_TRANSMIT_SM_IDLE_EXEC:
938 p->hello_when = r->bridge_hello_time;
939 p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE;
941 case PORT_TRANSMIT_SM_IDLE:
942 if (p->role == ROLE_DISABLED) {
943 VLOG_DBG("%s, port %u: port_transmit_sm ROLE == DISABLED.",
944 p->rstp->name, p->port_number);
946 } else if (p->send_rstp && p->new_info
947 && p->tx_count < r->transmit_hold_count
948 && p->hello_when != 0 && p->selected && !p->updt_info) {
949 p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_RSTP_EXEC;
950 } else if (p->hello_when == 0 && p->selected && !p->updt_info) {
951 p->port_transmit_sm_state =
952 PORT_TRANSMIT_SM_TRANSMIT_PERIODIC_EXEC;
953 } else if (!p->send_rstp && p->new_info && p->role == ROLE_ROOT
954 && p->tx_count < r->transmit_hold_count
955 && p->hello_when != 0 && p->selected && !p->updt_info) {
956 p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_TCN_EXEC;
957 } else if (!p->send_rstp && p->new_info && p->role == ROLE_DESIGNATED
958 && p->tx_count < r->transmit_hold_count
959 && p->hello_when != 0 && p->selected && !p->updt_info) {
960 p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_CONFIG_EXEC;
963 case PORT_TRANSMIT_SM_TRANSMIT_CONFIG_EXEC:
968 p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_CONFIG;
970 case PORT_TRANSMIT_SM_TRANSMIT_CONFIG:
971 p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC;
973 case PORT_TRANSMIT_SM_TRANSMIT_TCN_EXEC:
977 p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_TCN;
979 case PORT_TRANSMIT_SM_TRANSMIT_TCN:
980 p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC;
982 case PORT_TRANSMIT_SM_TRANSMIT_RSTP_EXEC:
987 p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_RSTP;
989 case PORT_TRANSMIT_SM_TRANSMIT_RSTP:
990 p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC;
996 if (old_state != p->port_transmit_sm_state) {
998 VLOG_DBG("%s, port %u: port_transmit_sm %d -> %d", p->rstp->name,
999 p->port_number, old_state, p->port_transmit_sm_state);
1004 /* [17.27 Port Information state machine] */
1009 rcv_info(struct rstp_port *p)
1010 OVS_REQUIRES(rstp_mutex)
1012 enum vector_comparison cp;
1014 enum port_flag role;
1016 p->msg_priority.root_bridge_id =
1017 ntohll(p->received_bpdu_buffer.root_bridge_id);
1018 p->msg_priority.root_path_cost =
1019 ntohl(p->received_bpdu_buffer.root_path_cost);
1020 p->msg_priority.designated_bridge_id =
1021 ntohll(p->received_bpdu_buffer.designated_bridge_id);
1022 p->msg_priority.designated_port_id =
1023 ntohs(p->received_bpdu_buffer.designated_port_id);
1025 p->msg_times.forward_delay =
1026 time_decode(p->received_bpdu_buffer.forward_delay);
1027 p->msg_times.hello_time = time_decode(p->received_bpdu_buffer.hello_time);
1028 p->msg_times.max_age = time_decode(p->received_bpdu_buffer.max_age);
1029 p->msg_times.message_age =
1030 time_decode(p->received_bpdu_buffer.message_age);
1032 cp = compare_rstp_priority_vector(&p->msg_priority, &p->port_priority);
1033 ct = rstp_times_equal(&p->port_times, &p->msg_times);
1035 (p->received_bpdu_buffer.flags & ROLE_FLAG_MASK) >> ROLE_FLAG_SHIFT;
1037 /* Returns SuperiorDesignatedInfo if:
1038 * a) The received message conveys a Designated Port Role, and
1039 * 1) The message priority is superior (17.6) to the Port.s port priority
1041 * 2) The message priority vector is the same as the Port.s port priority
1042 * vector, and any of the received timer parameter values (msg_times.
1043 * 17.19.15) differ from those already held for the Port (port_times
1045 * NOTE: Configuration BPDU explicitly conveys a Designated Port Role.
1047 if ((role == PORT_DES
1048 || p->received_bpdu_buffer.bpdu_type == CONFIGURATION_BPDU)
1049 && (cp == SUPERIOR || (cp == SAME && ct == false))) {
1050 return SUPERIOR_DESIGNATED_INFO;
1052 /* Returns RepeatedDesignatedInfo if:
1053 * b) The received message conveys Designated Port Role, and a message
1054 * priority vector and timer parameters that are the same as the
1055 * Port's port priority vector or timer values. */
1056 } else if (role == PORT_DES && cp == SAME && ct == true) {
1057 return REPEATED_DESIGNATED_INFO;
1059 /* Returns InferiorDesignatedInfo if:
1060 * c) The received message conveys a Designated Port Role, and a
1061 * message priority vector that is worse than the Port's port
1062 * priority vector. */
1063 } else if (role == PORT_DES && cp == INFERIOR) {
1064 return INFERIOR_DESIGNATED_INFO;
1066 /* Returns InferiorRootAlternateInfo if:
1067 * d) The received message conveys a Root Port, Alternate Port, or
1068 * Backup Port Role and a message priority that is the same as or
1069 * worse than the port priority vector. */
1070 } else if ((role == PORT_ROOT || role == PORT_ALT_BACK) &&
1071 (cp == INFERIOR || cp == SAME)) {
1072 return INFERIOR_ROOT_ALTERNATE_INFO;
1074 /* Otherwise, returns OtherInfo. */
1081 better_or_same_info(struct rstp_port *p, int new_info_is)
1082 OVS_REQUIRES(rstp_mutex)
1084 /* >= SUPERIOR means that the vector is better or the same. */
1086 (new_info_is == RECEIVED && p->info_is == INFO_IS_RECEIVED
1087 && compare_rstp_priority_vector(&p->msg_priority,
1088 &p->port_priority) >= SUPERIOR)
1089 || (new_info_is == MINE && p->info_is == INFO_IS_MINE
1090 && compare_rstp_priority_vector(&p->designated_priority_vector,
1091 &p->port_priority) >= SUPERIOR);
1095 port_information_sm(struct rstp_port *p)
1096 OVS_REQUIRES(rstp_mutex)
1098 enum port_information_state_machine old_state;
1101 old_state = p->port_information_sm_state;
1104 if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) {
1105 p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
1107 switch (p->port_information_sm_state) {
1108 case PORT_INFORMATION_SM_INIT:
1110 p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
1113 case PORT_INFORMATION_SM_DISABLED_EXEC:
1114 p->rcvd_msg = false;
1115 p->proposing = p->proposed = p->agree = p->agreed = false;
1116 p->rcvd_info_while = 0;
1117 p->info_is = INFO_IS_DISABLED;
1119 p->selected = false;
1120 p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED;
1122 case PORT_INFORMATION_SM_DISABLED:
1123 if (p->port_enabled) {
1124 p->port_information_sm_state = PORT_INFORMATION_SM_AGED_EXEC;
1125 } else if (p->rcvd_msg) {
1126 p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC;
1129 case PORT_INFORMATION_SM_AGED_EXEC:
1130 p->info_is = INFO_IS_AGED;
1132 p->selected = false;
1133 p->port_information_sm_state = PORT_INFORMATION_SM_AGED;
1135 case PORT_INFORMATION_SM_AGED:
1136 if (p->selected && p->updt_info) {
1137 p->port_information_sm_state = PORT_INFORMATION_SM_UPDATE_EXEC;
1140 case PORT_INFORMATION_SM_UPDATE_EXEC:
1141 p->proposing = p->proposed = false;
1142 /* MINE is not specified in Standard 802.1D-2004. */
1143 p->agreed = p->agreed && better_or_same_info(p, MINE);
1144 p->synced = p->synced && p->agreed;
1145 p->port_priority.root_bridge_id =
1146 p->designated_priority_vector.root_bridge_id;
1147 p->port_priority.root_path_cost =
1148 p->designated_priority_vector.root_path_cost;
1149 p->port_priority.designated_bridge_id =
1150 p->designated_priority_vector.designated_bridge_id;
1151 p->port_priority.designated_port_id =
1152 p->designated_priority_vector.designated_port_id;
1153 p->port_times = p->designated_times;
1154 p->updt_info = false;
1155 p->info_is = INFO_IS_MINE;
1157 p->port_information_sm_state = PORT_INFORMATION_SM_UPDATE;
1159 case PORT_INFORMATION_SM_UPDATE:
1160 p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
1162 case PORT_INFORMATION_SM_CURRENT_EXEC:
1163 p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT;
1165 case PORT_INFORMATION_SM_CURRENT:
1166 if (p->rcvd_msg && !p->updt_info) {
1167 p->port_information_sm_state = PORT_INFORMATION_SM_RECEIVE_EXEC;
1168 } else if (p->selected && p->updt_info) {
1169 p->port_information_sm_state = PORT_INFORMATION_SM_UPDATE_EXEC;
1170 } else if ((p->info_is == INFO_IS_RECEIVED) &&
1171 (p->rcvd_info_while == 0) && !p->updt_info &&
1173 p->port_information_sm_state = PORT_INFORMATION_SM_AGED_EXEC;
1176 case PORT_INFORMATION_SM_RECEIVE_EXEC:
1177 p->rcvd_info = rcv_info(p);
1178 p->port_information_sm_state = PORT_INFORMATION_SM_RECEIVE;
1180 case PORT_INFORMATION_SM_RECEIVE:
1181 switch (p->rcvd_info) {
1182 case SUPERIOR_DESIGNATED_INFO:
1183 p->port_information_sm_state =
1184 PORT_INFORMATION_SM_SUPERIOR_DESIGNATED_EXEC;
1186 case REPEATED_DESIGNATED_INFO:
1187 p->port_information_sm_state =
1188 PORT_INFORMATION_SM_REPEATED_DESIGNATED_EXEC;
1190 case INFERIOR_DESIGNATED_INFO:
1191 p->port_information_sm_state =
1192 PORT_INFORMATION_SM_INFERIOR_DESIGNATED_EXEC;
1194 case INFERIOR_ROOT_ALTERNATE_INFO:
1195 p->port_information_sm_state =
1196 PORT_INFORMATION_SM_NOT_DESIGNATED_EXEC;
1199 p->port_information_sm_state = PORT_INFORMATION_SM_OTHER_EXEC;
1206 case PORT_INFORMATION_SM_OTHER_EXEC:
1207 p->rcvd_msg = false;
1208 p->port_information_sm_state = PORT_INFORMATION_SM_OTHER;
1210 case PORT_INFORMATION_SM_OTHER:
1211 p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
1213 case PORT_INFORMATION_SM_NOT_DESIGNATED_EXEC:
1214 record_agreement(p);
1216 p->rcvd_msg = false;
1217 p->port_information_sm_state = PORT_INFORMATION_SM_NOT_DESIGNATED;
1219 case PORT_INFORMATION_SM_NOT_DESIGNATED:
1220 p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
1222 case PORT_INFORMATION_SM_INFERIOR_DESIGNATED_EXEC:
1224 p->rcvd_msg = false;
1225 p->port_information_sm_state = PORT_INFORMATION_SM_INFERIOR_DESIGNATED;
1227 case PORT_INFORMATION_SM_INFERIOR_DESIGNATED:
1228 p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
1230 case PORT_INFORMATION_SM_REPEATED_DESIGNATED_EXEC:
1233 updt_rcvd_info_while(p);
1234 p->rcvd_msg = false;
1235 p->port_information_sm_state = PORT_INFORMATION_SM_REPEATED_DESIGNATED;
1237 case PORT_INFORMATION_SM_REPEATED_DESIGNATED:
1238 p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
1240 case PORT_INFORMATION_SM_SUPERIOR_DESIGNATED_EXEC:
1241 p->agreed = p->proposing = false;
1244 /* RECEIVED is not specified in Standard 802.1D-2004. */
1245 p->agree = p->agree && better_or_same_info(p, RECEIVED);
1248 updt_rcvd_info_while(p);
1249 p->info_is = INFO_IS_RECEIVED;
1251 p->selected = false;
1252 p->rcvd_msg = false;
1253 p->port_information_sm_state = PORT_INFORMATION_SM_SUPERIOR_DESIGNATED;
1255 case PORT_INFORMATION_SM_SUPERIOR_DESIGNATED:
1256 p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC;
1262 if (old_state != p->port_information_sm_state) {
1264 VLOG_DBG("%s, port %u: Port_information_sm %d -> %d", p->rstp->name,
1265 p->port_number, old_state, p->port_information_sm_state);
1270 /* [17.29 Port Role Transitions state machine] */
1273 set_re_root_tree(struct rstp_port *p)
1274 OVS_REQUIRES(rstp_mutex)
1277 struct rstp_port *p1;
1280 if (r->ports_count > 0) {
1281 LIST_FOR_EACH (p1, node, &r->ports) {
1288 set_sync_tree(struct rstp_port *p)
1289 OVS_REQUIRES(rstp_mutex)
1292 struct rstp_port *p1;
1295 if (r->ports_count > 0) {
1296 LIST_FOR_EACH (p1, node, &r->ports) {
1303 hello_time(struct rstp_port *p)
1304 OVS_REQUIRES(rstp_mutex)
1306 return p->designated_times.hello_time;
1310 fwd_delay(struct rstp_port *p)
1311 OVS_REQUIRES(rstp_mutex)
1313 return p->designated_times.forward_delay;
1317 forward_delay(struct rstp_port *p)
1318 OVS_REQUIRES(rstp_mutex)
1321 return hello_time(p);
1323 return fwd_delay(p);
1328 edge_delay(struct rstp_port *p)
1329 OVS_REQUIRES(rstp_mutex)
1334 if (p->oper_point_to_point_mac == 1) {
1335 return r->migrate_time;
1337 return p->designated_times.max_age;
1342 check_selected_role_change(struct rstp_port *p, int current_role_state)
1343 OVS_REQUIRES(rstp_mutex)
1345 if (p->selected && !p->updt_info && p->role != p->selected_role
1346 && p->selected_role != current_role_state) {
1347 VLOG_DBG("%s, port %u: case: current = %s role = %s selected = %d",
1348 p->rstp->name, p->port_number,
1349 rstp_port_role_name(current_role_state),
1350 rstp_port_role_name(p->role), p->selected_role);
1351 switch (p->selected_role) {
1353 p->port_role_transition_sm_state =
1354 PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
1356 case ROLE_DESIGNATED:
1357 p->port_role_transition_sm_state =
1358 PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
1360 case ROLE_ALTERNATE:
1361 p->port_role_transition_sm_state =
1362 PORT_ROLE_TRANSITION_SM_BLOCK_PORT_EXEC;
1365 p->port_role_transition_sm_state =
1366 PORT_ROLE_TRANSITION_SM_BLOCK_PORT_EXEC;
1369 p->port_role_transition_sm_state =
1370 PORT_ROLE_TRANSITION_SM_DISABLE_PORT_EXEC;
1378 re_rooted(struct rstp_port *p)
1379 OVS_REQUIRES(rstp_mutex)
1382 struct rstp_port *p1;
1385 if (r->ports_count > 0) {
1386 LIST_FOR_EACH (p1, node, &r->ports) {
1387 if ((p1 != p) && (p1->rr_while != 0)) {
1396 all_synced(struct rstp *r)
1397 OVS_REQUIRES(rstp_mutex)
1399 struct rstp_port *p;
1401 if (r->ports_count > 0) {
1402 LIST_FOR_EACH (p, node, &r->ports) {
1403 if (!(p->selected && p->role == p->selected_role &&
1404 (p->role == ROLE_ROOT || p->synced == true))) {
1413 port_role_transition_sm(struct rstp_port *p)
1414 OVS_REQUIRES(rstp_mutex)
1416 enum port_role_transition_state_machine old_state;
1418 enum rstp_port_role last_role;
1420 old_state = p->port_role_transition_sm_state;
1422 last_role = p->role;
1424 switch (p->port_role_transition_sm_state) {
1425 case PORT_ROLE_TRANSITION_SM_INIT:
1427 p->port_role_transition_sm_state =
1428 PORT_ROLE_TRANSITION_SM_INIT_PORT_EXEC;
1431 case PORT_ROLE_TRANSITION_SM_INIT_PORT_EXEC:
1432 p->role = ROLE_DISABLED;
1433 p->learn = p->forward = false;
1435 p->sync = p->re_root = true;
1436 p->rr_while = p->designated_times.forward_delay;
1437 p->fd_while = p->designated_times.max_age;
1439 p->port_role_transition_sm_state =
1440 PORT_ROLE_TRANSITION_SM_DISABLE_PORT_EXEC;
1442 case PORT_ROLE_TRANSITION_SM_DISABLE_PORT_EXEC:
1443 p->role = p->selected_role;
1444 p->learn = p->forward = false;
1445 p->port_role_transition_sm_state =
1446 PORT_ROLE_TRANSITION_SM_DISABLE_PORT;
1448 case PORT_ROLE_TRANSITION_SM_DISABLE_PORT:
1449 if (check_selected_role_change(p, ROLE_DISABLED)) {
1451 } else if (p->selected && !p->updt_info && !p->learning
1452 && !p->forwarding) {
1453 p->port_role_transition_sm_state =
1454 PORT_ROLE_TRANSITION_SM_DISABLED_PORT_EXEC;
1457 case PORT_ROLE_TRANSITION_SM_DISABLED_PORT_EXEC:
1458 p->fd_while = p->designated_times.max_age;
1461 p->sync = p->re_root = false;
1462 p->port_role_transition_sm_state =
1463 PORT_ROLE_TRANSITION_SM_DISABLED_PORT;
1465 case PORT_ROLE_TRANSITION_SM_DISABLED_PORT:
1466 if (check_selected_role_change(p, ROLE_DISABLED)) {
1468 } else if (p->selected && !p->updt_info
1469 && (p->fd_while != p->designated_times.max_age || p->sync
1470 || p->re_root || !p->synced)) {
1471 p->port_role_transition_sm_state =
1472 PORT_ROLE_TRANSITION_SM_DISABLED_PORT_EXEC;
1475 case PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC:
1476 p->role = ROLE_ROOT;
1477 p->rr_while = p->designated_times.forward_delay;
1478 p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT;
1480 case PORT_ROLE_TRANSITION_SM_ROOT_PORT:
1481 if (check_selected_role_change(p, ROLE_ROOT)) {
1483 } else if (p->selected && !p->updt_info) {
1484 if (p->rr_while != p->designated_times.forward_delay) {
1485 p->port_role_transition_sm_state =
1486 PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
1488 } else if (p->re_root && p->forward) {
1489 p->port_role_transition_sm_state =
1490 PORT_ROLE_TRANSITION_SM_REROOTED_EXEC;
1492 } else if ((p->fd_while == 0
1493 || ((re_rooted(p) && p->rb_while == 0)
1494 && r->rstp_version)) && !p->learn) {
1495 p->port_role_transition_sm_state =
1496 PORT_ROLE_TRANSITION_SM_ROOT_LEARN_EXEC;
1498 } else if ((p->fd_while == 0
1499 || ((re_rooted(p) && p->rb_while == 0)
1500 && r->rstp_version)) && p->learn && !p->forward) {
1501 p->port_role_transition_sm_state =
1502 PORT_ROLE_TRANSITION_SM_ROOT_FORWARD_EXEC;
1504 } else if (p->proposed && !p->agree) {
1505 p->port_role_transition_sm_state =
1506 PORT_ROLE_TRANSITION_SM_ROOT_PROPOSED_EXEC;
1508 } else if ((all_synced(r) && !p->agree) ||
1509 (p->proposed && p->agree)) {
1510 p->port_role_transition_sm_state =
1511 PORT_ROLE_TRANSITION_SM_ROOT_AGREED_EXEC;
1513 } else if (!p->forward && !p->re_root) {
1514 p->port_role_transition_sm_state =
1515 PORT_ROLE_TRANSITION_SM_REROOT_EXEC;
1520 case PORT_ROLE_TRANSITION_SM_REROOT_EXEC:
1521 set_re_root_tree(p);
1522 p->port_role_transition_sm_state =
1523 PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
1525 case PORT_ROLE_TRANSITION_SM_ROOT_AGREED_EXEC:
1526 p->proposed = p->sync = false;
1527 p->agree = p->new_info = true;
1528 p->port_role_transition_sm_state =
1529 PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
1531 case PORT_ROLE_TRANSITION_SM_ROOT_PROPOSED_EXEC:
1533 p->proposed = false;
1534 p->port_role_transition_sm_state =
1535 PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
1537 case PORT_ROLE_TRANSITION_SM_ROOT_FORWARD_EXEC:
1540 p->port_role_transition_sm_state =
1541 PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
1543 case PORT_ROLE_TRANSITION_SM_ROOT_LEARN_EXEC:
1544 p->fd_while = forward_delay(p);
1546 p->port_role_transition_sm_state =
1547 PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
1549 case PORT_ROLE_TRANSITION_SM_REROOTED_EXEC:
1551 p->port_role_transition_sm_state =
1552 PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC;
1554 case PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC:
1555 p->role = ROLE_DESIGNATED;
1556 p->port_role_transition_sm_state =
1557 PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT;
1559 case PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT:
1560 if (check_selected_role_change(p, ROLE_DESIGNATED)) {
1562 } else if (p->selected && !p->updt_info) {
1563 if (((p->sync && !p->synced)
1564 || (p->re_root && p->rr_while != 0) || p->disputed)
1565 && !p->oper_edge && (p->learn || p->forward)) {
1566 p->port_role_transition_sm_state =
1567 PORT_ROLE_TRANSITION_SM_DESIGNATED_DISCARD_EXEC;
1568 } else if ((p->fd_while == 0 || p->agreed || p->oper_edge)
1569 && (p->rr_while == 0 || !p->re_root)
1570 && !p->sync && !p->learn) {
1571 p->port_role_transition_sm_state =
1572 PORT_ROLE_TRANSITION_SM_DESIGNATED_LEARN_EXEC;
1573 } else if ((p->fd_while == 0 || p->agreed || p->oper_edge)
1574 && (p->rr_while == 0 || !p->re_root)
1575 && !p->sync && (p->learn && !p->forward)) {
1576 p->port_role_transition_sm_state =
1577 PORT_ROLE_TRANSITION_SM_DESIGNATED_FORWARD_EXEC;
1578 } else if (!p->forward && !p->agreed && !p->proposing &&
1580 p->port_role_transition_sm_state =
1581 PORT_ROLE_TRANSITION_SM_DESIGNATED_PROPOSE_EXEC;
1582 } else if ((!p->learning && !p->forwarding && !p->synced)
1583 || (p->agreed && !p->synced)
1584 || (p->oper_edge && !p->synced)
1585 || (p->sync && p->synced)) {
1586 p->port_role_transition_sm_state =
1587 PORT_ROLE_TRANSITION_SM_DESIGNATED_SYNCED_EXEC;
1588 } else if (p->rr_while == 0 && p->re_root) {
1589 p->port_role_transition_sm_state =
1590 PORT_ROLE_TRANSITION_SM_DESIGNATED_RETIRED_EXEC;
1594 case PORT_ROLE_TRANSITION_SM_DESIGNATED_RETIRED_EXEC:
1596 p->port_role_transition_sm_state =
1597 PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
1599 case PORT_ROLE_TRANSITION_SM_DESIGNATED_SYNCED_EXEC:
1603 p->port_role_transition_sm_state =
1604 PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
1606 case PORT_ROLE_TRANSITION_SM_DESIGNATED_PROPOSE_EXEC:
1607 p->proposing = true;
1608 p->edge_delay_while = edge_delay(p);
1610 p->port_role_transition_sm_state =
1611 PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
1613 case PORT_ROLE_TRANSITION_SM_DESIGNATED_FORWARD_EXEC:
1616 p->agreed = p->send_rstp;
1617 p->port_role_transition_sm_state =
1618 PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
1620 case PORT_ROLE_TRANSITION_SM_DESIGNATED_LEARN_EXEC:
1622 p->fd_while = forward_delay(p);
1623 p->port_role_transition_sm_state =
1624 PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
1626 case PORT_ROLE_TRANSITION_SM_DESIGNATED_DISCARD_EXEC:
1627 p->learn = p->forward = p->disputed = false;
1628 p->fd_while = forward_delay(p);
1629 p->port_role_transition_sm_state =
1630 PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC;
1632 case PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC:
1633 p->fd_while = p->designated_times.forward_delay;
1636 p->sync = p->re_root = false;
1637 p->port_role_transition_sm_state =
1638 PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT;
1640 case PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT:
1641 if (check_selected_role_change(p, ROLE_ALTERNATE)) {
1643 } else if (p->selected && !p->updt_info) {
1644 if (p->rb_while != 2 * p->designated_times.hello_time
1645 && p->role == ROLE_BACKUP) {
1646 p->port_role_transition_sm_state =
1647 PORT_ROLE_TRANSITION_SM_BACKUP_PORT_EXEC;
1648 } else if ((p->fd_while != forward_delay(p)) || p->sync
1649 || p->re_root || !p->synced) {
1650 p->port_role_transition_sm_state =
1651 PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
1652 } else if (p->proposed && !p->agree) {
1653 p->port_role_transition_sm_state =
1654 PORT_ROLE_TRANSITION_SM_ALTERNATE_PROPOSED_EXEC;
1655 } else if ((all_synced(r) && !p->agree)
1656 || (p->proposed && p->agree)) {
1657 p->port_role_transition_sm_state =
1658 PORT_ROLE_TRANSITION_SM_ALTERNATE_AGREED;
1662 case PORT_ROLE_TRANSITION_SM_ALTERNATE_AGREED:
1663 p->proposed = false;
1666 p->port_role_transition_sm_state =
1667 PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
1669 case PORT_ROLE_TRANSITION_SM_ALTERNATE_PROPOSED_EXEC:
1671 p->proposed = false;
1672 p->port_role_transition_sm_state =
1673 PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
1675 case PORT_ROLE_TRANSITION_SM_BLOCK_PORT_EXEC:
1676 p->role = p->selected_role;
1677 p->learn = p->forward = false;
1678 p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_BLOCK_PORT;
1680 case PORT_ROLE_TRANSITION_SM_BLOCK_PORT:
1681 if (check_selected_role_change(p, ROLE_ALTERNATE)) {
1683 } else if (p->selected && !p->updt_info && !p->learning &&
1685 p->port_role_transition_sm_state =
1686 PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
1689 case PORT_ROLE_TRANSITION_SM_BACKUP_PORT_EXEC:
1690 p->rb_while = 2 * p->designated_times.hello_time;
1691 p->port_role_transition_sm_state =
1692 PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC;
1698 if (old_state != p->port_role_transition_sm_state) {
1700 VLOG_DBG("%s, port %u: Port_role_transition_sm %d -> %d",
1701 p->rstp->name, p->port_number, old_state,
1702 p->port_role_transition_sm_state);
1704 if (last_role != p->role) {
1705 last_role = p->role;
1706 VLOG_DBG("%s, port %u, port role ["RSTP_PORT_ID_FMT"] = %s",
1707 p->rstp->name, p->port_number, p->port_id,
1708 rstp_port_role_name(p->role));
1713 /* [17.30 - Port state transition state machine] */
1716 enable_learning(struct rstp_port *p)
1717 OVS_REQUIRES(rstp_mutex)
1719 /* [17.21.6 enableLearning()] An implementation dependent procedure that
1720 * causes the Learning Process (7.8) to start learning from frames received
1721 * on the Port. The procedure does not complete until learning has been
1724 rstp_port_set_state__(p, RSTP_LEARNING);
1728 enable_forwarding(struct rstp_port *p)
1729 OVS_REQUIRES(rstp_mutex)
1731 /* [17.21.5 enableForwarding()] An implementation dependent procedure that
1732 * causes the Forwarding Process (7.7) to start forwarding frames through
1733 * the Port. The procedure does not complete until forwarding has been
1736 rstp_port_set_state__(p, RSTP_FORWARDING);
1740 disable_learning(struct rstp_port *p)
1741 OVS_REQUIRES(rstp_mutex)
1743 /* [17.21.4 - disableLearning()] An implementation dependent procedure that
1744 * causes the Learning Process (7.8) to stop learning from the source
1745 * address of frames received on the Port. The procedure does not complete
1746 * until learning has stopped.
1748 rstp_port_set_state__(p, RSTP_DISCARDING);
1752 disable_forwarding(struct rstp_port *p)
1753 OVS_REQUIRES(rstp_mutex)
1755 /* [17.21.3 - disableForwarding()] An implementation dependent procedure
1756 * that causes the Forwarding Process (7.7) to stop forwarding frames
1757 * through the Port. The procedure does not complete until forwarding has
1760 rstp_port_set_state__(p, RSTP_DISCARDING);
1764 port_state_transition_sm(struct rstp_port *p)
1765 OVS_REQUIRES(rstp_mutex)
1767 enum port_state_transition_state_machine old_state;
1770 old_state = p->port_state_transition_sm_state;
1773 switch (p->port_state_transition_sm_state) {
1774 case PORT_STATE_TRANSITION_SM_INIT:
1776 p->port_state_transition_sm_state =
1777 PORT_STATE_TRANSITION_SM_DISCARDING_EXEC;
1780 case PORT_STATE_TRANSITION_SM_DISCARDING_EXEC:
1781 disable_learning(p);
1782 p->learning = false;
1783 disable_forwarding(p);
1784 p->forwarding = false;
1785 p->port_state_transition_sm_state =
1786 PORT_STATE_TRANSITION_SM_DISCARDING;
1788 case PORT_STATE_TRANSITION_SM_DISCARDING:
1790 p->port_state_transition_sm_state =
1791 PORT_STATE_TRANSITION_SM_LEARNING_EXEC;
1794 case PORT_STATE_TRANSITION_SM_LEARNING_EXEC:
1797 p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_LEARNING;
1799 case PORT_STATE_TRANSITION_SM_LEARNING:
1801 p->port_state_transition_sm_state =
1802 PORT_STATE_TRANSITION_SM_DISCARDING_EXEC;
1803 } else if (p->forward) {
1804 p->port_state_transition_sm_state =
1805 PORT_STATE_TRANSITION_SM_FORWARDING_EXEC;
1808 case PORT_STATE_TRANSITION_SM_FORWARDING_EXEC:
1809 enable_forwarding(p);
1810 p->forwarding = true;
1811 p->port_state_transition_sm_state =
1812 PORT_STATE_TRANSITION_SM_FORWARDING;
1814 case PORT_STATE_TRANSITION_SM_FORWARDING:
1816 p->port_state_transition_sm_state =
1817 PORT_STATE_TRANSITION_SM_DISCARDING_EXEC;
1824 if (old_state != p->port_state_transition_sm_state) {
1826 VLOG_DBG("%s, port %u: Port_state_transition_sm %d -> %d",
1827 p->rstp->name, p->port_number, old_state,
1828 p->port_state_transition_sm_state);
1833 /* [17.31 - Topology Change state machine] */
1836 new_tc_while(struct rstp_port *p)
1837 OVS_REQUIRES(rstp_mutex)
1842 if (p->tc_while == 0 && p->send_rstp == true) {
1843 p->tc_while = r->bridge_hello_time + 1;
1845 } else if (p->tc_while == 0 && p->send_rstp == false) {
1846 p->tc_while = r->bridge_max_age + r->bridge_forward_delay;
1850 /* [17.21.18 setTcPropTree()]
1851 * Sets tcprop for all Ports except the Port that called the procedure.
1854 set_tc_prop_tree(struct rstp_port *p)
1855 OVS_REQUIRES(rstp_mutex)
1858 struct rstp_port *p1;
1861 if (r->ports_count > 0) {
1862 LIST_FOR_EACH (p1, node, &r->ports) {
1863 /* Set tc_prop on every port, except the one calling this
1866 if (p1->port_number != p->port_number) {
1874 set_tc_prop_bridge(struct rstp_port *p) /* not specified in 802.1D-2004. */
1875 OVS_REQUIRES(rstp_mutex)
1877 set_tc_prop_tree(p); /* see 802.1w-2001. */
1881 topology_change_sm(struct rstp_port *p)
1882 OVS_REQUIRES(rstp_mutex)
1884 enum topology_change_state_machine old_state;
1887 old_state = p->topology_change_sm_state;
1890 switch (p->topology_change_sm_state) {
1891 case TOPOLOGY_CHANGE_SM_INIT:
1893 p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_INACTIVE_EXEC;
1896 case TOPOLOGY_CHANGE_SM_INACTIVE_EXEC:
1897 p->fdb_flush = true;
1900 p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_INACTIVE;
1902 case TOPOLOGY_CHANGE_SM_INACTIVE:
1903 if (p->learn && !p->fdb_flush) {
1904 p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING_EXEC;
1907 case TOPOLOGY_CHANGE_SM_LEARNING_EXEC:
1908 p->rcvd_tc = p->rcvd_tcn = p->rcvd_tc_ack = false;
1909 p->tc_prop = p->rcvd_tc_ack = false;
1910 p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING;
1912 case TOPOLOGY_CHANGE_SM_LEARNING:
1913 if (p->role != ROLE_ROOT && p->role != ROLE_DESIGNATED &&
1914 !(p->learn || p->learning) && !(p->rcvd_tc || p->rcvd_tcn ||
1915 p->rcvd_tc_ack || p->tc_prop)) {
1916 p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_INACTIVE_EXEC;
1917 } else if (p->rcvd_tc || p->rcvd_tcn || p->rcvd_tc_ack || p->tc_prop) {
1918 p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING_EXEC;
1919 } else if ((p->role == ROLE_ROOT || p->role == ROLE_DESIGNATED)
1920 && p->forward && !p->oper_edge) {
1921 p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_DETECTED_EXEC;
1924 case TOPOLOGY_CHANGE_SM_DETECTED_EXEC:
1926 set_tc_prop_tree(p);
1928 p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE_EXEC;
1930 case TOPOLOGY_CHANGE_SM_ACTIVE_EXEC:
1931 p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE;
1933 case TOPOLOGY_CHANGE_SM_ACTIVE:
1934 if ((p->role != ROLE_ROOT && p->role != ROLE_DESIGNATED)
1936 p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING_EXEC;
1937 } else if (p->rcvd_tcn) {
1938 p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_NOTIFIED_TCN_EXEC;
1939 } else if (p->rcvd_tc) {
1940 p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_NOTIFIED_TC_EXEC;
1941 } else if (p->tc_prop && !p->oper_edge) {
1942 p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_PROPAGATING_EXEC;
1943 } else if (p->rcvd_tc_ack) {
1944 p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACKNOWLEDGED_EXEC;
1947 case TOPOLOGY_CHANGE_SM_ACKNOWLEDGED_EXEC:
1949 p->rcvd_tc_ack = false;
1950 p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE;
1952 case TOPOLOGY_CHANGE_SM_PROPAGATING_EXEC:
1954 p->fdb_flush = true;
1956 p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE;
1958 case TOPOLOGY_CHANGE_SM_NOTIFIED_TC_EXEC:
1959 p->rcvd_tcn = p->rcvd_tc = false;
1960 if (p->role == ROLE_DESIGNATED) {
1963 set_tc_prop_bridge(p);
1964 p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE;
1966 case TOPOLOGY_CHANGE_SM_NOTIFIED_TCN_EXEC:
1968 p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE;
1974 if (old_state != p->topology_change_sm_state) {
1976 VLOG_DBG("%s, port %u: Topology_change_sm %d -> %d", p->rstp->name,
1977 p->port_number, old_state, p->topology_change_sm_state);
1982 /****************************************************************************
1983 * [17.6] Priority vector calculation helper functions
1984 ****************************************************************************/
1987 * This message priority vector is superior to the port priority vector and
1988 * will replace it if, and only if, the message priority vector is better
1989 * than the port priority vector, or the message has been transmitted from the
1990 * same Designated Bridge and Designated Port as the port priority vector,
1991 * i.e.,if the following is true:
1992 * ((RD < RootBridgeID)) ||
1993 * ((RD == RootBridgeID) && (RPCD < RootPathCost)) ||
1994 * ((RD == RootBridgeID) && (RPCD == RootPathCost) &&
1995 * (D < designated_bridge_id)) ||
1996 * ((RD == RootBridgeID) && (RPCD == RootPathCost) &&
1997 * (D == designated_bridge_id) && (PD < designated_port_id)) ||
1998 * ((D == designated_bridge_id.BridgeAddress) &&
1999 * (PD == designated_port_id.PortNumber))
2002 /* compare_rstp_priority_vector() compares two struct rstp_priority_vector and
2003 * returns a value indicating if the first rstp_priority_vector is superior,
2004 * same or inferior to the second one.
2006 static enum vector_comparison
2007 compare_rstp_priority_vector(struct rstp_priority_vector *v1,
2008 struct rstp_priority_vector *v2)
2010 VLOG_DBG("v1: "RSTP_ID_FMT", %u, "RSTP_ID_FMT", %d",
2011 RSTP_ID_ARGS(v1->root_bridge_id), v1->root_path_cost,
2012 RSTP_ID_ARGS(v1->designated_bridge_id), v1->designated_port_id);
2013 VLOG_DBG("v2: "RSTP_ID_FMT", %u, "RSTP_ID_FMT", %d",
2014 RSTP_ID_ARGS(v2->root_bridge_id), v2->root_path_cost,
2015 RSTP_ID_ARGS(v2->designated_bridge_id), v2->designated_port_id);
2017 if ((v1->root_bridge_id < v2->root_bridge_id) ||
2018 ((v1->root_bridge_id == v2->root_bridge_id) &&
2019 (v1->root_path_cost < v2->root_path_cost)) ||
2020 ((v1->root_bridge_id == v2->root_bridge_id) &&
2021 (v1->root_path_cost == v2->root_path_cost) &&
2022 (v1->designated_bridge_id < v2->designated_bridge_id)) ||
2023 ((v1->root_bridge_id == v2->root_bridge_id) &&
2024 (v1->root_path_cost == v2->root_path_cost) &&
2025 (v1->designated_bridge_id == v2->designated_bridge_id) &&
2026 (v1->designated_port_id < v2->designated_port_id))) {
2027 VLOG_DBG("superior_absolute");
2030 else if (((v1->root_bridge_id > v2->root_bridge_id) ||
2031 ((v1->root_bridge_id == v2->root_bridge_id) &&
2032 (v1->root_path_cost > v2->root_path_cost)) ||
2033 ((v1->root_bridge_id == v2->root_bridge_id) &&
2034 (v1->root_path_cost == v2->root_path_cost) &&
2035 (v1->designated_bridge_id > v2->designated_bridge_id)) ||
2036 ((v1->root_bridge_id == v2->root_bridge_id) &&
2037 (v1->root_path_cost == v2->root_path_cost) &&
2038 (v1->designated_bridge_id == v2->designated_bridge_id) &&
2039 (v1->designated_port_id > v2->designated_port_id))) &&
2040 (v1->designated_bridge_id == v2->designated_bridge_id) &&
2041 (v1->designated_port_id == v2->designated_port_id)) {
2042 VLOG_DBG("superior_same_des");
2045 else if ((v1->root_bridge_id == v2->root_bridge_id) &&
2046 (v1->root_path_cost == v2->root_path_cost) &&
2047 (v1->designated_bridge_id == v2->designated_bridge_id) &&
2048 (v1->designated_port_id == v2->designated_port_id)) {
2053 VLOG_DBG("inferior");
2059 rstp_times_equal(struct rstp_times *t1, struct rstp_times *t2)
2061 return t1->forward_delay == t2->forward_delay
2062 && t1->hello_time == t2->hello_time
2063 && t1->max_age == t2->max_age
2064 && t1->message_age == t2->message_age;