]> git.proxmox.com Git - ovs.git/blame - lib/rstp.c
lockfile: Support \-delimited file names in lockfile_name().
[ovs.git] / lib / rstp.c
CommitLineData
9efd308e
DV
1/*
2 * Copyright (c) 2011-2014 M3S, Srl - Italy
3 *
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:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17/*
18 * Rapid Spanning Tree Protocol (IEEE 802.1D-2004) public interface.
19 *
20 * Authors:
21 * Martino Fornasa <mf@fornasa.it>
22 * Daniele Venturino <daniele.venturino@m3s.it>
23 *
24 * References to IEEE 802.1D-2004 standard are enclosed in square brackets.
25 * E.g. [17.3], [Table 17-1], etc.
26 *
27 */
28
29#include <config.h>
4b30ba05 30
9efd308e
DV
31#include "rstp.h"
32#include "rstp-common.h"
33#include "rstp-state-machines.h"
34#include <arpa/inet.h>
35#include <inttypes.h>
36#include <netinet/in.h>
37#include <stdlib.h>
38#include <sys/types.h>
39#include "byte-order.h"
40#include "connectivity.h"
41#include "ofpbuf.h"
42#include "ofproto/ofproto.h"
cf62fa4c 43#include "dp-packet.h"
9efd308e
DV
44#include "packets.h"
45#include "seq.h"
46#include "unixctl.h"
47#include "util.h"
e6211adc 48#include "openvswitch/vlog.h"
9efd308e
DV
49
50VLOG_DEFINE_THIS_MODULE(rstp);
51
6b90bc57 52struct ovs_mutex rstp_mutex = OVS_MUTEX_INITIALIZER;
f025bcb7 53
55951e15 54static struct ovs_list all_rstps__ = OVS_LIST_INITIALIZER(&all_rstps__);
ca6ba700 55static struct ovs_list *const all_rstps OVS_GUARDED_BY(rstp_mutex) = &all_rstps__;
f025bcb7
JR
56
57/* Internal use only. */
58static void rstp_set_bridge_address__(struct rstp *, rstp_identifier)
59 OVS_REQUIRES(rstp_mutex);
60static void rstp_set_bridge_priority__(struct rstp *, int new_priority)
61 OVS_REQUIRES(rstp_mutex);
62static void rstp_set_bridge_ageing_time__(struct rstp *, int new_ageing_time)
63 OVS_REQUIRES(rstp_mutex);
64static void rstp_set_bridge_force_protocol_version__(struct rstp *,
65 enum rstp_force_protocol_version)
66 OVS_REQUIRES(rstp_mutex);
67static void rstp_set_bridge_hello_time__(struct rstp *)
68 OVS_REQUIRES(rstp_mutex);
69static void rstp_set_bridge_max_age__(struct rstp *, int new_max_age)
70 OVS_REQUIRES(rstp_mutex);
71static void rstp_set_bridge_forward_delay__(struct rstp *, int new_forward_delay)
72 OVS_REQUIRES(rstp_mutex);
73static void rstp_set_bridge_transmit_hold_count__(struct rstp *,
74 int new_transmit_hold_count)
75 OVS_REQUIRES(rstp_mutex);
76static void rstp_set_bridge_migrate_time__(struct rstp *)
77 OVS_REQUIRES(rstp_mutex);
78static void rstp_set_bridge_times__(struct rstp *, int new_forward_delay,
79 int new_hello_time, int new_max_age,
80 int new_message_age)
81 OVS_REQUIRES(rstp_mutex);
82
83static struct rstp_port *rstp_get_port__(struct rstp *rstp,
84 uint16_t port_number)
85 OVS_REQUIRES(rstp_mutex);
86static void set_port_id__(struct rstp_port *)
87 OVS_REQUIRES(rstp_mutex);
88static void update_port_enabled__(struct rstp_port *)
89 OVS_REQUIRES(rstp_mutex);
90static void set_bridge_priority__(struct rstp *)
91 OVS_REQUIRES(rstp_mutex);
92static void reinitialize_rstp__(struct rstp *)
93 OVS_REQUIRES(rstp_mutex);
94static bool is_port_number_available__(struct rstp *, int, struct rstp_port *)
95 OVS_REQUIRES(rstp_mutex);
96static uint16_t rstp_first_free_number__(struct rstp *, struct rstp_port *)
97 OVS_REQUIRES(rstp_mutex);
98static void rstp_initialize_port_defaults__(struct rstp_port *)
99 OVS_REQUIRES(rstp_mutex);
100static void rstp_port_set_priority__(struct rstp_port *, int priority)
101 OVS_REQUIRES(rstp_mutex);
102static void rstp_port_set_port_number__(struct rstp_port *,
103 uint16_t port_number)
104 OVS_REQUIRES(rstp_mutex);
105static void rstp_port_set_path_cost__(struct rstp_port *, uint32_t path_cost)
106 OVS_REQUIRES(rstp_mutex);
107static void rstp_port_set_administrative_bridge_port__(struct rstp_port *,
37a4efd1
JR
108 uint8_t admin_port_state,
109 bool initializing)
f025bcb7
JR
110 OVS_REQUIRES(rstp_mutex);
111static void rstp_port_set_admin_edge__(struct rstp_port *, bool admin_edge)
112 OVS_REQUIRES(rstp_mutex);
113static void rstp_port_set_auto_edge__(struct rstp_port *, bool auto_edge)
114 OVS_REQUIRES(rstp_mutex);
67e8c1ac
JR
115static void rstp_port_set_admin_point_to_point_mac__(struct rstp_port *,
116 enum rstp_admin_point_to_point_mac_state admin_p2p_mac_state)
117 OVS_REQUIRES(rstp_mutex);
f025bcb7
JR
118static void rstp_port_set_mcheck__(struct rstp_port *, bool mcheck)
119 OVS_REQUIRES(rstp_mutex);
120static void reinitialize_port__(struct rstp_port *p)
121 OVS_REQUIRES(rstp_mutex);
9efd308e
DV
122
123const char *
124rstp_state_name(enum rstp_state state)
125{
126 switch (state) {
127 case RSTP_DISABLED:
128 return "Disabled";
129 case RSTP_LEARNING:
130 return "Learning";
131 case RSTP_FORWARDING:
132 return "Forwarding";
133 case RSTP_DISCARDING:
134 return "Discarding";
135 default:
136 return "Unknown";
137 }
138}
139
140const char *
141rstp_port_role_name(enum rstp_port_role role)
142{
143 switch (role) {
144 case ROLE_ROOT:
145 return "Root";
146 case ROLE_DESIGNATED:
147 return "Designated";
148 case ROLE_ALTERNATE:
149 return "Alternate";
150 case ROLE_BACKUP:
151 return "Backup";
152 case ROLE_DISABLED:
153 return "Disabled";
154 default:
155 return "Unknown";
156 }
157}
158
4b30ba05 159/* Caller has to hold a reference to prevent 'rstp' from being deleted
f025bcb7 160 * while taking a new reference. */
9efd308e 161struct rstp *
4b30ba05 162rstp_ref(struct rstp *rstp)
f025bcb7 163 OVS_EXCLUDED(rstp_mutex)
9efd308e 164{
9efd308e
DV
165 if (rstp) {
166 ovs_refcount_ref(&rstp->ref_cnt);
167 }
168 return rstp;
169}
170
f025bcb7 171/* Frees RSTP struct when reference count reaches zero. */
9efd308e
DV
172void
173rstp_unref(struct rstp *rstp)
f025bcb7 174 OVS_EXCLUDED(rstp_mutex)
9efd308e 175{
f5920067 176 if (rstp && ovs_refcount_unref_relaxed(&rstp->ref_cnt) == 1) {
f025bcb7
JR
177 ovs_mutex_lock(&rstp_mutex);
178
179 /* Each RSTP port points back to struct rstp without holding a
180 * reference for that pointer. This is OK as we never move
181 * ports from one bridge to another, and holders always
182 * release their ports before releasing the bridge. This
183 * means that there should be not ports at this time. */
d5f31dc8 184 ovs_assert(hmap_is_empty(&rstp->ports));
4b30ba05 185
9efd308e 186 list_remove(&rstp->node);
f025bcb7 187 ovs_mutex_unlock(&rstp_mutex);
9efd308e
DV
188 free(rstp->name);
189 free(rstp);
190 }
191}
192
4b30ba05
JR
193/* Returns the port number. Mutex is needed to guard against
194 * concurrent reinitialization (which can temporarily clear the
195 * port_number). */
9efd308e 196int
f025bcb7
JR
197rstp_port_get_number(const struct rstp_port *p)
198 OVS_EXCLUDED(rstp_mutex)
9efd308e
DV
199{
200 int number;
201
f025bcb7 202 ovs_mutex_lock(&rstp_mutex);
9efd308e 203 number = p->port_number;
f025bcb7
JR
204 ovs_mutex_unlock(&rstp_mutex);
205
9efd308e
DV
206 return number;
207}
208
209static void rstp_unixctl_tcn(struct unixctl_conn *, int argc,
210 const char *argv[], void *aux);
211
212/* Decrements the State Machines' timers. */
213void
214rstp_tick_timers(struct rstp *rstp)
f025bcb7 215 OVS_EXCLUDED(rstp_mutex)
9efd308e 216{
f025bcb7
JR
217 ovs_mutex_lock(&rstp_mutex);
218 decrease_rstp_port_timers__(rstp);
219 ovs_mutex_unlock(&rstp_mutex);
9efd308e
DV
220}
221
222/* Processes an incoming BPDU. */
223void
f025bcb7
JR
224rstp_port_received_bpdu(struct rstp_port *rp, const void *bpdu,
225 size_t bpdu_size)
226 OVS_EXCLUDED(rstp_mutex)
9efd308e 227{
f025bcb7
JR
228 ovs_mutex_lock(&rstp_mutex);
229 /* Only process packets on ports that have RSTP enabled. */
230 if (rp && rp->rstp_state != RSTP_DISABLED) {
231 process_received_bpdu__(rp, bpdu, bpdu_size);
232 }
233 ovs_mutex_unlock(&rstp_mutex);
9efd308e
DV
234}
235
236void
237rstp_init(void)
f025bcb7 238 OVS_EXCLUDED(rstp_mutex)
9efd308e 239{
4b30ba05
JR
240 unixctl_command_register("rstp/tcn", "[bridge]", 0, 1, rstp_unixctl_tcn,
241 NULL);
9efd308e
DV
242}
243
244/* Creates and returns a new RSTP instance that initially has no ports. */
245struct rstp *
246rstp_create(const char *name, rstp_identifier bridge_address,
cf62fa4c 247 void (*send_bpdu)(struct dp_packet *bpdu, void *port_aux,
6b90bc57 248 void *rstp_aux),
4b30ba05 249 void *aux)
f025bcb7 250 OVS_EXCLUDED(rstp_mutex)
9efd308e 251{
9efd308e
DV
252 struct rstp *rstp;
253
254 VLOG_DBG("Creating RSTP instance");
9efd308e
DV
255
256 rstp = xzalloc(sizeof *rstp);
257 rstp->name = xstrdup(name);
f025bcb7 258
d5f31dc8
JR
259 /* Initialize the ports map before calling any setters,
260 * so that the state machines will see an empty ports map. */
261 hmap_init(&rstp->ports);
37db915e 262
f025bcb7 263 ovs_mutex_lock(&rstp_mutex);
9efd308e 264 /* Set bridge address. */
f025bcb7 265 rstp_set_bridge_address__(rstp, bridge_address);
9efd308e 266 /* Set default parameters values. */
f025bcb7
JR
267 rstp_set_bridge_priority__(rstp, RSTP_DEFAULT_PRIORITY);
268 rstp_set_bridge_ageing_time__(rstp, RSTP_DEFAULT_AGEING_TIME);
269 rstp_set_bridge_force_protocol_version__(rstp, FPV_DEFAULT);
270 rstp_set_bridge_forward_delay__(rstp, RSTP_DEFAULT_BRIDGE_FORWARD_DELAY);
271 rstp_set_bridge_hello_time__(rstp);
272 rstp_set_bridge_max_age__(rstp, RSTP_DEFAULT_BRIDGE_MAX_AGE);
273 rstp_set_bridge_migrate_time__(rstp);
274 rstp_set_bridge_transmit_hold_count__(rstp,
275 RSTP_DEFAULT_TRANSMIT_HOLD_COUNT);
276 rstp_set_bridge_times__(rstp, RSTP_DEFAULT_BRIDGE_FORWARD_DELAY,
277 RSTP_BRIDGE_HELLO_TIME,
278 RSTP_DEFAULT_BRIDGE_MAX_AGE, 0);
9efd308e
DV
279 rstp->send_bpdu = send_bpdu;
280 rstp->aux = aux;
281 rstp->changes = false;
282 rstp->begin = true;
283
9efd308e 284 ovs_refcount_init(&rstp->ref_cnt);
4b30ba05 285
9efd308e 286 list_push_back(all_rstps, &rstp->node);
f025bcb7 287 ovs_mutex_unlock(&rstp_mutex);
4b30ba05 288
9efd308e
DV
289 VLOG_DBG("RSTP instance creation done");
290 return rstp;
291}
292
293/* Called by rstp_set_bridge_address() and rstp_set_bridge_priority(),
294 * it updates the bridge priority vector according to the values passed by
295 * those setters.
296 */
297static void
298set_bridge_priority__(struct rstp *rstp)
f025bcb7 299 OVS_REQUIRES(rstp_mutex)
9efd308e 300{
37db915e
JR
301 struct rstp_port *p;
302
9efd308e
DV
303 rstp->bridge_priority.root_bridge_id = rstp->bridge_identifier;
304 rstp->bridge_priority.designated_bridge_id = rstp->bridge_identifier;
305 VLOG_DBG("%s: new bridge identifier: "RSTP_ID_FMT"", rstp->name,
306 RSTP_ID_ARGS(rstp->bridge_identifier));
aaab616a
JR
307
308 /* [17.13] When the bridge address changes, recalculates all priority
309 * vectors.
310 */
d5f31dc8 311 HMAP_FOR_EACH (p, node, &rstp->ports) {
37db915e
JR
312 p->selected = false;
313 p->reselect = true;
aaab616a
JR
314 }
315 rstp->changes = true;
f025bcb7 316 updt_roles_tree__(rstp);
9efd308e
DV
317}
318
319/* Sets the bridge address. */
f025bcb7
JR
320static void
321rstp_set_bridge_address__(struct rstp *rstp, rstp_identifier bridge_address)
322 OVS_REQUIRES(rstp_mutex)
9efd308e 323{
9efd308e
DV
324 VLOG_DBG("%s: set bridge address to: "RSTP_ID_FMT"", rstp->name,
325 RSTP_ID_ARGS(bridge_address));
036dc965
DV
326 if (rstp->address != bridge_address) {
327 rstp->address = bridge_address;
328 rstp->bridge_identifier &= 0xffff000000000000ULL;
329 rstp->bridge_identifier |= bridge_address;
330 set_bridge_priority__(rstp);
331 }
f025bcb7
JR
332}
333
334/* Sets the bridge address. */
335void
336rstp_set_bridge_address(struct rstp *rstp, rstp_identifier bridge_address)
337 OVS_EXCLUDED(rstp_mutex)
338{
339 ovs_mutex_lock(&rstp_mutex);
340 rstp_set_bridge_address__(rstp, bridge_address);
341 ovs_mutex_unlock(&rstp_mutex);
9efd308e
DV
342}
343
344const char *
345rstp_get_name(const struct rstp *rstp)
f025bcb7 346 OVS_EXCLUDED(rstp_mutex)
9efd308e
DV
347{
348 char *name;
349
f025bcb7 350 ovs_mutex_lock(&rstp_mutex);
9efd308e 351 name = rstp->name;
f025bcb7 352 ovs_mutex_unlock(&rstp_mutex);
9efd308e
DV
353 return name;
354}
355
356rstp_identifier
357rstp_get_bridge_id(const struct rstp *rstp)
f025bcb7 358 OVS_EXCLUDED(rstp_mutex)
9efd308e
DV
359{
360 rstp_identifier bridge_id;
361
f025bcb7 362 ovs_mutex_lock(&rstp_mutex);
9efd308e 363 bridge_id = rstp->bridge_identifier;
f025bcb7
JR
364 ovs_mutex_unlock(&rstp_mutex);
365
9efd308e
DV
366 return bridge_id;
367}
368
369/* Sets the bridge priority. */
f025bcb7
JR
370static void
371rstp_set_bridge_priority__(struct rstp *rstp, int new_priority)
372 OVS_REQUIRES(rstp_mutex)
9efd308e 373{
aaab616a
JR
374 new_priority = ROUND_DOWN(new_priority, RSTP_PRIORITY_STEP);
375
036dc965
DV
376 if (rstp->priority != new_priority
377 && new_priority >= RSTP_MIN_PRIORITY
aaab616a
JR
378 && new_priority <= RSTP_MAX_PRIORITY) {
379 VLOG_DBG("%s: set bridge priority to %d", rstp->name, new_priority);
9efd308e 380
aaab616a 381 rstp->priority = new_priority;
4b30ba05 382 rstp->bridge_identifier &= 0x0000ffffffffffffULL;
aaab616a 383 rstp->bridge_identifier |= (uint64_t)new_priority << 48;
9efd308e 384 set_bridge_priority__(rstp);
9efd308e
DV
385 }
386}
387
9efd308e 388void
f025bcb7
JR
389rstp_set_bridge_priority(struct rstp *rstp, int new_priority)
390 OVS_EXCLUDED(rstp_mutex)
391{
392 ovs_mutex_lock(&rstp_mutex);
393 rstp_set_bridge_priority__(rstp, new_priority);
394 ovs_mutex_unlock(&rstp_mutex);
395}
396
397/* Sets the bridge ageing time. */
398static void
399rstp_set_bridge_ageing_time__(struct rstp *rstp, int new_ageing_time)
400 OVS_REQUIRES(rstp_mutex)
9efd308e 401{
4b30ba05
JR
402 if (new_ageing_time >= RSTP_MIN_AGEING_TIME
403 && new_ageing_time <= RSTP_MAX_AGEING_TIME) {
9efd308e 404 VLOG_DBG("%s: set ageing time to %d", rstp->name, new_ageing_time);
4b30ba05 405
9efd308e 406 rstp->ageing_time = new_ageing_time;
9efd308e
DV
407 }
408}
409
f025bcb7
JR
410void
411rstp_set_bridge_ageing_time(struct rstp *rstp, int new_ageing_time)
412 OVS_EXCLUDED(rstp_mutex)
413{
414 ovs_mutex_lock(&rstp_mutex);
415 rstp_set_bridge_ageing_time__(rstp, new_ageing_time);
416 ovs_mutex_unlock(&rstp_mutex);
417}
418
9efd308e
DV
419/* Reinitializes RSTP when switching from RSTP mode to STP mode
420 * or vice versa.
421 */
422static void
423reinitialize_rstp__(struct rstp *rstp)
f025bcb7 424 OVS_REQUIRES(rstp_mutex)
9efd308e
DV
425{
426 struct rstp temp;
d5f31dc8 427 static struct hmap ports;
37db915e 428 struct rstp_port *p;
9efd308e
DV
429
430 /* Copy rstp in temp */
431 temp = *rstp;
432 ports = rstp->ports;
4b30ba05 433
9efd308e
DV
434 /* stop and clear rstp */
435 memset(rstp, 0, sizeof(struct rstp));
436
437 /* Initialize rstp. */
438 rstp->name = temp.name;
37db915e 439
d5f31dc8 440 /* Initialize the ports hmap before calling any setters,
37db915e 441 * so that the state machines will see an empty ports list. */
d5f31dc8 442 hmap_init(&rstp->ports);
37db915e 443
9efd308e 444 /* Set bridge address. */
f025bcb7 445 rstp_set_bridge_address__(rstp, temp.address);
9efd308e 446 /* Set default parameters values. */
f025bcb7
JR
447 rstp_set_bridge_priority__(rstp, RSTP_DEFAULT_PRIORITY);
448 rstp_set_bridge_ageing_time__(rstp, RSTP_DEFAULT_AGEING_TIME);
449 rstp_set_bridge_forward_delay__(rstp, RSTP_DEFAULT_BRIDGE_FORWARD_DELAY);
450 rstp_set_bridge_hello_time__(rstp);
451 rstp_set_bridge_max_age__(rstp, RSTP_DEFAULT_BRIDGE_MAX_AGE);
452 rstp_set_bridge_migrate_time__(rstp);
453 rstp_set_bridge_transmit_hold_count__(rstp,
454 RSTP_DEFAULT_TRANSMIT_HOLD_COUNT);
455 rstp_set_bridge_times__(rstp, RSTP_DEFAULT_BRIDGE_FORWARD_DELAY,
456 RSTP_BRIDGE_HELLO_TIME,
457 RSTP_DEFAULT_BRIDGE_MAX_AGE, 0);
9efd308e
DV
458
459 rstp->send_bpdu = temp.send_bpdu;
460 rstp->aux = temp.aux;
461 rstp->node = temp.node;
462 rstp->changes = false;
463 rstp->begin = true;
4b30ba05 464
37db915e
JR
465 /* Restore ports. */
466 rstp->ports = ports;
4b30ba05 467
d5f31dc8 468 HMAP_FOR_EACH (p, node, &rstp->ports) {
37db915e 469 reinitialize_port__(p);
9efd308e 470 }
37db915e 471
9efd308e
DV
472 rstp->ref_cnt = temp.ref_cnt;
473}
474
475/* Sets the force protocol version parameter. */
f025bcb7
JR
476static void
477rstp_set_bridge_force_protocol_version__(struct rstp *rstp,
9efd308e 478 enum rstp_force_protocol_version new_force_protocol_version)
f025bcb7 479 OVS_REQUIRES(rstp_mutex)
9efd308e
DV
480{
481 if (new_force_protocol_version != rstp->force_protocol_version &&
482 (new_force_protocol_version == FPV_STP_COMPATIBILITY ||
483 new_force_protocol_version == FPV_DEFAULT)) {
484 VLOG_DBG("%s: set bridge Force Protocol Version to %d", rstp->name,
485 new_force_protocol_version);
f025bcb7 486
9efd308e
DV
487 /* [17.13] The Spanning Tree Protocol Entity shall be reinitialized,
488 * as specified by the assertion of BEGIN (17.18.1) in the state
489 * machine specification.
490 */
491 reinitialize_rstp__(rstp);
492 rstp->force_protocol_version = new_force_protocol_version;
493 if (rstp->force_protocol_version < 2) {
494 rstp->stp_version = true;
495 rstp->rstp_version = false;
496 } else {
497 rstp->stp_version = false;
498 rstp->rstp_version = true;
499 }
500 rstp->changes = true;
f025bcb7 501 move_rstp__(rstp);
9efd308e
DV
502 }
503}
504
9efd308e 505void
f025bcb7
JR
506rstp_set_bridge_force_protocol_version(struct rstp *rstp,
507 enum rstp_force_protocol_version new_force_protocol_version)
508 OVS_EXCLUDED(rstp_mutex)
509{
510 ovs_mutex_lock(&rstp_mutex);
511 rstp_set_bridge_force_protocol_version__(rstp, new_force_protocol_version);
512 ovs_mutex_unlock(&rstp_mutex);
513}
514
515/* Sets the bridge Hello Time parameter. */
516static void
517rstp_set_bridge_hello_time__(struct rstp *rstp)
518 OVS_REQUIRES(rstp_mutex)
9efd308e
DV
519{
520 VLOG_DBG("%s: set RSTP Hello Time to %d", rstp->name,
521 RSTP_BRIDGE_HELLO_TIME);
522 /* 2 is the only acceptable value. */
9efd308e 523 rstp->bridge_hello_time = RSTP_BRIDGE_HELLO_TIME;
9efd308e
DV
524}
525
526/* Sets the bridge max age parameter. */
f025bcb7
JR
527static void
528rstp_set_bridge_max_age__(struct rstp *rstp, int new_max_age)
529 OVS_REQUIRES(rstp_mutex)
9efd308e 530{
53a68641
JR
531 if (rstp->bridge_max_age != new_max_age
532 && new_max_age >= RSTP_MIN_BRIDGE_MAX_AGE
533 && new_max_age <= RSTP_MAX_BRIDGE_MAX_AGE) {
9efd308e 534 /* [17.13] */
4b30ba05
JR
535 if ((2 * (rstp->bridge_forward_delay - 1) >= new_max_age)
536 && (new_max_age >= 2 * rstp->bridge_hello_time)) {
9efd308e
DV
537 VLOG_DBG("%s: set RSTP bridge Max Age to %d", rstp->name,
538 new_max_age);
f025bcb7 539
9efd308e
DV
540 rstp->bridge_max_age = new_max_age;
541 rstp->bridge_times.max_age = new_max_age;
53a68641
JR
542 rstp->changes = true;
543 updt_roles_tree__(rstp);
9efd308e
DV
544 }
545 }
546}
547
9efd308e 548void
f025bcb7
JR
549rstp_set_bridge_max_age(struct rstp *rstp, int new_max_age)
550 OVS_EXCLUDED(rstp_mutex)
551{
552 ovs_mutex_lock(&rstp_mutex);
553 rstp_set_bridge_max_age__(rstp, new_max_age);
554 ovs_mutex_unlock(&rstp_mutex);
555}
556
557/* Sets the bridge forward delay parameter. */
558static void
559rstp_set_bridge_forward_delay__(struct rstp *rstp, int new_forward_delay)
560 OVS_REQUIRES(rstp_mutex)
9efd308e 561{
53a68641
JR
562 if (rstp->bridge_forward_delay != new_forward_delay
563 && new_forward_delay >= RSTP_MIN_BRIDGE_FORWARD_DELAY
564 && new_forward_delay <= RSTP_MAX_BRIDGE_FORWARD_DELAY) {
9efd308e
DV
565 if (2 * (new_forward_delay - 1) >= rstp->bridge_max_age) {
566 VLOG_DBG("%s: set RSTP Forward Delay to %d", rstp->name,
567 new_forward_delay);
9efd308e
DV
568 rstp->bridge_forward_delay = new_forward_delay;
569 rstp->bridge_times.forward_delay = new_forward_delay;
53a68641
JR
570 rstp->changes = true;
571 updt_roles_tree__(rstp);
9efd308e
DV
572 }
573 }
574}
575
9efd308e 576void
f025bcb7
JR
577rstp_set_bridge_forward_delay(struct rstp *rstp, int new_forward_delay)
578 OVS_EXCLUDED(rstp_mutex)
579{
580 ovs_mutex_lock(&rstp_mutex);
581 rstp_set_bridge_forward_delay__(rstp, new_forward_delay);
582 ovs_mutex_unlock(&rstp_mutex);
583}
584
585/* Sets the bridge transmit hold count parameter. */
586static void
587rstp_set_bridge_transmit_hold_count__(struct rstp *rstp,
588 int new_transmit_hold_count)
589 OVS_REQUIRES(rstp_mutex)
9efd308e 590{
036dc965
DV
591 if (rstp->transmit_hold_count != new_transmit_hold_count
592 && new_transmit_hold_count >= RSTP_MIN_TRANSMIT_HOLD_COUNT
4b30ba05 593 && new_transmit_hold_count <= RSTP_MAX_TRANSMIT_HOLD_COUNT) {
37db915e
JR
594 struct rstp_port *p;
595
9efd308e
DV
596 VLOG_DBG("%s: set RSTP Transmit Hold Count to %d", rstp->name,
597 new_transmit_hold_count);
598 /* Resetting txCount on all ports [17.13]. */
f025bcb7 599
9efd308e 600 rstp->transmit_hold_count = new_transmit_hold_count;
d5f31dc8 601 HMAP_FOR_EACH (p, node, &rstp->ports) {
37db915e 602 p->tx_count = 0;
9efd308e 603 }
9efd308e
DV
604 }
605}
606
9efd308e 607void
f025bcb7
JR
608rstp_set_bridge_transmit_hold_count(struct rstp *rstp,
609 int new_transmit_hold_count)
610 OVS_EXCLUDED(rstp_mutex)
611{
612 ovs_mutex_lock(&rstp_mutex);
613 rstp_set_bridge_transmit_hold_count__(rstp, new_transmit_hold_count);
614 ovs_mutex_unlock(&rstp_mutex);
615}
616
617/* Sets the bridge migrate time parameter. */
618static void
619rstp_set_bridge_migrate_time__(struct rstp *rstp)
620 OVS_REQUIRES(rstp_mutex)
9efd308e
DV
621{
622 VLOG_DBG("%s: set RSTP Migrate Time to %d", rstp->name,
623 RSTP_MIGRATE_TIME);
624 /* 3 is the only acceptable value */
9efd308e 625 rstp->migrate_time = RSTP_MIGRATE_TIME;
9efd308e
DV
626}
627
628/* Sets the bridge times. */
f025bcb7
JR
629static void
630rstp_set_bridge_times__(struct rstp *rstp, int new_forward_delay,
631 int new_hello_time, int new_max_age,
632 int new_message_age)
633 OVS_REQUIRES(rstp_mutex)
9efd308e
DV
634{
635 VLOG_DBG("%s: set RSTP times to (%d, %d, %d, %d)", rstp->name,
636 new_forward_delay, new_hello_time, new_max_age, new_message_age);
4b30ba05
JR
637 if (new_forward_delay >= RSTP_MIN_BRIDGE_FORWARD_DELAY
638 && new_forward_delay <= RSTP_MAX_BRIDGE_FORWARD_DELAY) {
9efd308e 639 rstp->bridge_times.forward_delay = new_forward_delay;
4b30ba05
JR
640 }
641 if (new_hello_time == RSTP_BRIDGE_HELLO_TIME) {
9efd308e 642 rstp->bridge_times.hello_time = new_hello_time;
4b30ba05
JR
643 }
644 if (new_max_age >= RSTP_MIN_BRIDGE_MAX_AGE
645 && new_max_age <= RSTP_MAX_BRIDGE_MAX_AGE) {
9efd308e 646 rstp->bridge_times.max_age = new_max_age;
4b30ba05 647 }
9efd308e
DV
648 rstp->bridge_times.message_age = new_message_age;
649}
650
f025bcb7
JR
651/* Sets the port id, it is called by rstp_port_set_port_number__() or
652 * rstp_port_set_priority__().
9efd308e
DV
653 */
654static void
655set_port_id__(struct rstp_port *p)
f025bcb7 656 OVS_REQUIRES(rstp_mutex)
9efd308e
DV
657{
658 struct rstp *rstp;
659
660 rstp = p->rstp;
661 /* [9.2.7] Port identifier. */
662 p->port_id = p->port_number | (p->priority << 8);
663 VLOG_DBG("%s: new RSTP port id "RSTP_PORT_ID_FMT"", rstp->name,
664 p->port_id);
665}
666
667/* Sets the port priority. */
f025bcb7
JR
668static void
669rstp_port_set_priority__(struct rstp_port *port, int priority)
670 OVS_REQUIRES(rstp_mutex)
9efd308e 671{
036dc965
DV
672 if (port->priority != priority
673 && priority >= RSTP_MIN_PORT_PRIORITY
f025bcb7
JR
674 && priority <= RSTP_MAX_PORT_PRIORITY) {
675 VLOG_DBG("%s, port %u: set RSTP port priority to %d", port->rstp->name,
676 port->port_number, priority);
677
678 priority -= priority % RSTP_STEP_PORT_PRIORITY;
679 port->priority = priority;
680 set_port_id__(port);
681 port->selected = false;
682 port->reselect = true;
9efd308e
DV
683 }
684}
685
801171c1 686/* Checks if a port number is available. */
9efd308e 687static bool
801171c1 688is_port_number_available__(struct rstp *rstp, int n, struct rstp_port *port)
f025bcb7 689 OVS_REQUIRES(rstp_mutex)
9efd308e 690{
801171c1 691 if (n >= 1 && n <= RSTP_MAX_PORTS) {
f025bcb7 692 struct rstp_port *p = rstp_get_port__(rstp, n);
9efd308e 693
801171c1 694 return p == NULL || p == port;
9efd308e 695 }
9efd308e
DV
696 return false;
697}
698
699static uint16_t
801171c1 700rstp_first_free_number__(struct rstp *rstp, struct rstp_port *rstp_port)
f025bcb7 701 OVS_REQUIRES(rstp_mutex)
801171c1
JR
702{
703 int free_number = 1;
9efd308e 704
9efd308e 705 while (free_number <= RSTP_MAX_PORTS) {
801171c1 706 if (is_port_number_available__(rstp, free_number, rstp_port)) {
9efd308e
DV
707 return free_number;
708 }
709 free_number++;
710 }
9efd308e
DV
711 VLOG_DBG("%s, No free port number available.", rstp->name);
712 return 0;
713}
714
715/* Sets the port number. */
f025bcb7
JR
716static void
717rstp_port_set_port_number__(struct rstp_port *port, uint16_t port_number)
718 OVS_REQUIRES(rstp_mutex)
9efd308e 719{
2372c146
JR
720 int old_port_number = port->port_number;
721
801171c1
JR
722 /* If new_port_number is available, use it, otherwise use the first free
723 * available port number. */
036dc965
DV
724 if (port->port_number != port_number || port_number == 0) {
725 port->port_number =
726 is_port_number_available__(port->rstp, port_number, port)
727 ? port_number
728 : rstp_first_free_number__(port->rstp, port);
729
730 if (port->port_number != old_port_number) {
731 set_port_id__(port);
732 /* [17.13] is not clear. I suppose that a port number change
733 * should trigger reselection like a port priority change. */
734 port->selected = false;
735 port->reselect = true;
736
737 /* Adjust the ports hmap. */
738 if (!hmap_node_is_null(&port->node)) {
739 hmap_remove(&port->rstp->ports, &port->node);
740 }
741 hmap_insert(&port->rstp->ports, &port->node,
742 hash_int(port->port_number, 0));
2372c146 743
036dc965
DV
744 VLOG_DBG("%s: set new RSTP port number %d", port->rstp->name,
745 port->port_number);
2372c146 746 }
2372c146 747 }
9efd308e
DV
748}
749
750/* Converts the link speed to a port path cost [Table 17-3]. */
751uint32_t
752rstp_convert_speed_to_cost(unsigned int speed)
753{
754 uint32_t value;
755
756 value = speed >= 10000000 ? 2 /* 10 Tb/s. */
757 : speed >= 1000000 ? 20 /* 1 Tb/s. */
758 : speed >= 100000 ? 200 /* 100 Gb/s. */
759 : speed >= 10000 ? 2000 /* 10 Gb/s. */
760 : speed >= 1000 ? 20000 /* 1 Gb/s. */
761 : speed >= 100 ? 200000 /* 100 Mb/s. */
762 : speed >= 10 ? 2000000 /* 10 Mb/s. */
763 : speed >= 1 ? 20000000 /* 1 Mb/s. */
764 : RSTP_DEFAULT_PORT_PATH_COST; /* 100 Mb/s. */
765
766 return value;
767}
768
769/* Sets the port path cost. */
f025bcb7
JR
770static void
771rstp_port_set_path_cost__(struct rstp_port *port, uint32_t path_cost)
772 OVS_REQUIRES(rstp_mutex)
9efd308e 773{
036dc965
DV
774 if (port->port_path_cost != path_cost
775 && path_cost >= RSTP_MIN_PORT_PATH_COST
f025bcb7
JR
776 && path_cost <= RSTP_MAX_PORT_PATH_COST) {
777 VLOG_DBG("%s, port %u, set RSTP port path cost to %d",
778 port->rstp->name, port->port_number, path_cost);
779
780 port->port_path_cost = path_cost;
781 port->selected = false;
782 port->reselect = true;
9efd308e
DV
783 }
784}
785
786/* Gets the root path cost. */
787uint32_t
788rstp_get_root_path_cost(const struct rstp *rstp)
f025bcb7 789 OVS_EXCLUDED(rstp_mutex)
9efd308e
DV
790{
791 uint32_t cost;
792
f025bcb7 793 ovs_mutex_lock(&rstp_mutex);
9efd308e 794 cost = rstp->root_priority.root_path_cost;
f025bcb7 795 ovs_mutex_unlock(&rstp_mutex);
9efd308e
DV
796 return cost;
797}
798
66ff280d
JR
799/* Finds a port which needs to flush its own MAC learning table. A NULL
800 * pointer is returned if no port needs to flush its MAC learning table.
801 * '*port' needs to be NULL in the first call to start the iteration. If
802 * '*port' is passed as non-NULL, it must be the value set by the last
2372c146
JR
803 * invocation of this function.
804 *
805 * This function may only be called by the thread that creates and deletes
806 * ports. Otherwise this function is not thread safe, as the returned
807 * '*port' could become stale before it is used in the next invocation. */
66ff280d
JR
808void *
809rstp_check_and_reset_fdb_flush(struct rstp *rstp, struct rstp_port **port)
f025bcb7 810 OVS_EXCLUDED(rstp_mutex)
9efd308e 811{
66ff280d 812 void *aux = NULL;
9efd308e 813
f025bcb7 814 ovs_mutex_lock(&rstp_mutex);
66ff280d
JR
815 if (*port == NULL) {
816 struct rstp_port *p;
817
818 HMAP_FOR_EACH (p, node, &rstp->ports) {
819 if (p->fdb_flush) {
820 aux = p->aux;
821 *port = p;
822 goto out;
823 }
824 }
825 } else { /* continue */
826 struct rstp_port *p = *port;
827
828 HMAP_FOR_EACH_CONTINUE (p, node, &rstp->ports) {
829 if (p->fdb_flush) {
830 aux = p->aux;
831 *port = p;
832 goto out;
833 }
9efd308e
DV
834 }
835 }
66ff280d
JR
836 /* No port needs flushing. */
837 *port = NULL;
838out:
839 /* fdb_flush should be reset by the filtering database
840 * once the entries are removed if rstp_version is TRUE, and
841 * immediately if stp_version is TRUE.*/
842 if (*port != NULL) {
843 (*port)->fdb_flush = false;
844 }
f025bcb7 845 ovs_mutex_unlock(&rstp_mutex);
2372c146 846
66ff280d 847 return aux;
2372c146 848}
9efd308e 849
f025bcb7
JR
850/* Finds a port whose state has changed, and returns the aux pointer set for
851 * the port. A NULL pointer is returned when no changed port is found. On
852 * return '*portp' contains the pointer to the rstp port that changed, or NULL
853 * if no changed port can be found.
4b30ba05 854 *
f025bcb7
JR
855 * If '*portp' is passed as non-NULL, it must be the value set by the last
856 * invocation of this function.
857 *
858 * This function may only be called by the thread that creates and deletes
859 * ports. Otherwise this function is not thread safe, as the returned
860 * '*portp' could become stale before it is used in the next invocation. */
861void *
862rstp_get_next_changed_port_aux(struct rstp *rstp, struct rstp_port **portp)
9efd308e 863{
f025bcb7 864 void *aux = NULL;
9efd308e 865
f025bcb7
JR
866 ovs_mutex_lock(&rstp_mutex);
867 if (*portp == NULL) {
4b30ba05
JR
868 struct rstp_port *p;
869
d5f31dc8 870 HMAP_FOR_EACH (p, node, &rstp->ports) {
9efd308e
DV
871 if (p->state_changed) {
872 p->state_changed = false;
f025bcb7
JR
873 aux = p->aux;
874 *portp = p;
875 goto out;
876 }
877 }
878 } else { /* continue */
879 struct rstp_port *p = *portp;
880
d5f31dc8 881 HMAP_FOR_EACH_CONTINUE (p, node, &rstp->ports) {
f025bcb7
JR
882 if (p->state_changed) {
883 p->state_changed = false;
884 aux = p->aux;
9efd308e 885 *portp = p;
f025bcb7 886 goto out;
9efd308e
DV
887 }
888 }
889 }
f025bcb7 890 /* No changed port found. */
9efd308e 891 *portp = NULL;
f025bcb7
JR
892out:
893 ovs_mutex_unlock(&rstp_mutex);
2372c146 894
f025bcb7 895 return aux;
9efd308e
DV
896}
897
2372c146
JR
898bool
899rstp_shift_root_learned_address(struct rstp *rstp)
900{
901 bool ret;
902
903 ovs_mutex_lock(&rstp_mutex);
904 ret = rstp->root_changed;
905 ovs_mutex_unlock(&rstp_mutex);
906
907 return ret;
908}
909
910void *
911rstp_get_old_root_aux(struct rstp *rstp)
912{
913 void *aux;
914
915 ovs_mutex_lock(&rstp_mutex);
916 aux = rstp->old_root_aux;
917 ovs_mutex_unlock(&rstp_mutex);
918
919 return aux;
920}
921
922void *
923rstp_get_new_root_aux(struct rstp *rstp)
924{
925 void *aux;
926
927 ovs_mutex_lock(&rstp_mutex);
928 aux = rstp->new_root_aux;
929 ovs_mutex_unlock(&rstp_mutex);
930
931 return aux;
932}
933
934void
935rstp_reset_root_changed(struct rstp *rstp)
936{
937 ovs_mutex_lock(&rstp_mutex);
938 rstp->root_changed = false;
939 ovs_mutex_unlock(&rstp_mutex);
940}
941
54d3863b
JR
942/* Returns the port in 'rstp' with number 'port_number'.
943 *
944 * XXX: May only be called while concurrent deletion of ports is excluded. */
f025bcb7
JR
945static struct rstp_port *
946rstp_get_port__(struct rstp *rstp, uint16_t port_number)
947 OVS_REQUIRES(rstp_mutex)
9efd308e
DV
948{
949 struct rstp_port *port;
950
f025bcb7
JR
951 ovs_assert(rstp && port_number > 0 && port_number <= RSTP_MAX_PORTS);
952
d5f31dc8
JR
953 HMAP_FOR_EACH_WITH_HASH (port, node, hash_int(port_number, 0),
954 &rstp->ports) {
37db915e
JR
955 if (port->port_number == port_number) {
956 return port;
9efd308e
DV
957 }
958 }
9efd308e
DV
959 return NULL;
960}
961
f025bcb7
JR
962struct rstp_port *
963rstp_get_port(struct rstp *rstp, uint16_t port_number)
964 OVS_EXCLUDED(rstp_mutex)
965{
966 struct rstp_port *p;
967
968 ovs_mutex_lock(&rstp_mutex);
969 p = rstp_get_port__(rstp, port_number);
970 ovs_mutex_unlock(&rstp_mutex);
971 return p;
972}
973
974void *
2372c146
JR
975rstp_get_port_aux__(struct rstp *rstp, uint16_t port_number)
976 OVS_REQUIRES(rstp_mutex)
f025bcb7
JR
977{
978 struct rstp_port *p;
f025bcb7 979 p = rstp_get_port__(rstp, port_number);
2372c146
JR
980 if (p) {
981 return p->aux;
982 }
983 return NULL;
f025bcb7
JR
984}
985
9efd308e
DV
986/* Updates the port_enabled parameter. */
987static void
988update_port_enabled__(struct rstp_port *p)
f025bcb7 989 OVS_REQUIRES(rstp_mutex)
9efd308e 990{
54d3863b
JR
991 if (p->mac_operational && p->is_administrative_bridge_port
992 == RSTP_ADMIN_BRIDGE_PORT_STATE_ENABLED) {
9efd308e
DV
993 p->port_enabled = true;
994 } else {
995 p->port_enabled = false;
996 }
997}
998
999/* Sets the port MAC_Operational parameter [6.4.2]. */
1000void
1001rstp_port_set_mac_operational(struct rstp_port *p, bool new_mac_operational)
f025bcb7 1002 OVS_EXCLUDED(rstp_mutex)
9efd308e
DV
1003{
1004 struct rstp *rstp;
1005
f025bcb7 1006 ovs_mutex_lock(&rstp_mutex);
9efd308e 1007 rstp = p->rstp;
f025bcb7
JR
1008 if (p->mac_operational != new_mac_operational) {
1009 p->mac_operational = new_mac_operational;
1010 update_port_enabled__(p);
1011 rstp->changes = true;
1012 move_rstp__(rstp);
1013 }
1014 ovs_mutex_unlock(&rstp_mutex);
9efd308e
DV
1015}
1016
1017/* Sets the port Administrative Bridge Port parameter. */
f025bcb7
JR
1018static void
1019rstp_port_set_administrative_bridge_port__(struct rstp_port *p,
37a4efd1
JR
1020 uint8_t admin_port_state,
1021 bool initializing)
f025bcb7 1022 OVS_REQUIRES(rstp_mutex)
9efd308e 1023{
67e8c1ac
JR
1024 VLOG_DBG("%s, port %u: set RSTP port admin-port-state to %d",
1025 p->rstp->name, p->port_number, admin_port_state);
1026
036dc965
DV
1027 if (p->is_administrative_bridge_port != admin_port_state
1028 && (admin_port_state == RSTP_ADMIN_BRIDGE_PORT_STATE_DISABLED
1029 || admin_port_state == RSTP_ADMIN_BRIDGE_PORT_STATE_ENABLED)) {
f025bcb7 1030 p->is_administrative_bridge_port = admin_port_state;
9efd308e 1031 update_port_enabled__(p);
37a4efd1
JR
1032
1033 if (!initializing) {
1034 struct rstp *rstp = p->rstp;
1035
1036 rstp->changes = true;
1037 move_rstp__(rstp);
1038 }
9efd308e
DV
1039 }
1040}
1041
1042/* Sets the port oper_point_to_point_mac parameter. */
f025bcb7
JR
1043static void
1044rstp_port_set_oper_point_to_point_mac__(struct rstp_port *p,
1045 uint8_t new_oper_p2p_mac)
1046 OVS_REQUIRES(rstp_mutex)
9efd308e 1047{
036dc965
DV
1048 if (p->oper_point_to_point_mac != new_oper_p2p_mac
1049 && (new_oper_p2p_mac == RSTP_OPER_P2P_MAC_STATE_DISABLED
1050 || new_oper_p2p_mac == RSTP_OPER_P2P_MAC_STATE_ENABLED)) {
f025bcb7 1051
9efd308e
DV
1052 p->oper_point_to_point_mac = new_oper_p2p_mac;
1053 update_port_enabled__(p);
1054 }
1055}
1056
1057/* Initializes a port with the defaults values for its parameters. */
1058static void
54d3863b 1059rstp_initialize_port_defaults__(struct rstp_port *p)
f025bcb7 1060 OVS_REQUIRES(rstp_mutex)
9efd308e 1061{
f025bcb7 1062 rstp_port_set_administrative_bridge_port__(p,
37a4efd1
JR
1063 RSTP_ADMIN_BRIDGE_PORT_STATE_ENABLED,
1064 true);
7ac97d76
DV
1065 rstp_port_set_oper_point_to_point_mac__(p,
1066 RSTP_OPER_P2P_MAC_STATE_ENABLED);
f025bcb7
JR
1067 rstp_port_set_path_cost__(p, RSTP_DEFAULT_PORT_PATH_COST);
1068 rstp_port_set_admin_edge__(p, false);
1069 rstp_port_set_auto_edge__(p, true);
1070 rstp_port_set_mcheck__(p, false);
9efd308e 1071
54d3863b 1072 /* Initialize state machines. */
9efd308e
DV
1073 p->port_receive_sm_state = PORT_RECEIVE_SM_INIT;
1074 p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_INIT;
1075 p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_INIT;
1076 p->port_transmit_sm_state = PORT_TRANSMIT_SM_INIT;
1077 p->port_information_sm_state = PORT_INFORMATION_SM_INIT;
1078 p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_INIT;
1079 p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_INIT;
1080 p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_INIT;
9efd308e
DV
1081 p->uptime = 0;
1082
9efd308e
DV
1083}
1084
54d3863b
JR
1085static void
1086reinitialize_port__(struct rstp_port *p)
f025bcb7 1087 OVS_REQUIRES(rstp_mutex)
9efd308e
DV
1088{
1089 struct rstp_port temp_port;
1090 struct rstp *rstp;
1091
1092 rstp = p->rstp;
1093 temp_port = *p;
1094 memset(p, 0, sizeof(struct rstp_port));
f025bcb7
JR
1095
1096 p->ref_cnt = temp_port.ref_cnt;
9efd308e
DV
1097 p->rstp = rstp;
1098 p->node = temp_port.node;
1099 p->aux = temp_port.aux;
1100 p->port_number = temp_port.port_number;
1101 p->port_priority = temp_port.port_priority;
1102 p->port_id = temp_port.port_id;
1103 p->rstp_state = RSTP_DISCARDING;
1104
54d3863b 1105 rstp_initialize_port_defaults__(p);
9efd308e
DV
1106
1107 VLOG_DBG("%s: RSTP port "RSTP_PORT_ID_FMT" reinitialized.", rstp->name,
54d3863b
JR
1108 p->port_id);
1109}
1110
1111void
1112reinitialize_port(struct rstp_port *p)
f025bcb7 1113 OVS_EXCLUDED(rstp_mutex)
54d3863b 1114{
f025bcb7 1115 ovs_mutex_lock(&rstp_mutex);
54d3863b 1116 reinitialize_port__(p);
f025bcb7 1117 ovs_mutex_unlock(&rstp_mutex);
9efd308e
DV
1118}
1119
1120/* Sets the port state. */
1121void
f025bcb7
JR
1122rstp_port_set_state__(struct rstp_port *p, enum rstp_state state)
1123 OVS_REQUIRES(rstp_mutex)
9efd308e
DV
1124{
1125 struct rstp *rstp;
1126
1127 rstp = p->rstp;
1128 VLOG_DBG("%s, port %u: set RSTP port state %s -> %s", rstp->name,
1129 p->port_number,
1130 rstp_state_name(p->rstp_state), rstp_state_name(state));
1131
1132 if (state != p->rstp_state && !p->state_changed) {
1133 p->state_changed = true;
1134 seq_change(connectivity_seq_get());
1135 }
1136 p->rstp_state = state;
1137}
1138
f025bcb7
JR
1139void
1140rstp_port_set_state(struct rstp_port *p, enum rstp_state state)
1141 OVS_EXCLUDED(rstp_mutex)
1142{
1143 ovs_mutex_lock(&rstp_mutex);
1144 rstp_port_set_state__(p, state);
1145 ovs_mutex_unlock(&rstp_mutex);
1146}
1147
9efd308e
DV
1148/* Adds a RSTP port. */
1149struct rstp_port *
cc33c223 1150rstp_add_port(struct rstp *rstp)
f025bcb7 1151 OVS_EXCLUDED(rstp_mutex)
cc33c223 1152{
9efd308e
DV
1153 struct rstp_port *p = xzalloc(sizeof *p);
1154
f025bcb7 1155 ovs_refcount_init(&p->ref_cnt);
2372c146 1156 hmap_node_nullify(&p->node);
f025bcb7
JR
1157
1158 ovs_mutex_lock(&rstp_mutex);
9efd308e 1159 p->rstp = rstp;
f025bcb7
JR
1160 rstp_port_set_priority__(p, RSTP_DEFAULT_PORT_PRIORITY);
1161 rstp_port_set_port_number__(p, 0);
54d3863b
JR
1162 p->aux = NULL;
1163 rstp_initialize_port_defaults__(p);
1164 VLOG_DBG("%s: RSTP port "RSTP_PORT_ID_FMT" initialized.", rstp->name,
1165 p->port_id);
1166
f025bcb7 1167 rstp_port_set_state__(p, RSTP_DISCARDING);
9efd308e 1168 rstp->changes = true;
f025bcb7 1169 move_rstp__(rstp);
9efd308e 1170 VLOG_DBG("%s: added port "RSTP_PORT_ID_FMT"", rstp->name, p->port_id);
f025bcb7 1171 ovs_mutex_unlock(&rstp_mutex);
9efd308e
DV
1172 return p;
1173}
1174
f025bcb7
JR
1175/* Caller has to hold a reference to prevent 'rstp_port' from being deleted
1176 * while taking a new reference. */
1177struct rstp_port *
1178rstp_port_ref(const struct rstp_port *rp_)
1179 OVS_EXCLUDED(rstp_mutex)
cc33c223 1180{
f025bcb7 1181 struct rstp_port *rp = CONST_CAST(struct rstp_port *, rp_);
9efd308e 1182
f025bcb7
JR
1183 if (rp) {
1184 ovs_refcount_ref(&rp->ref_cnt);
1185 }
1186 return rp;
9efd308e
DV
1187}
1188
f025bcb7 1189/* Frees RSTP struct. This can be caller by any thread. */
9efd308e 1190void
f025bcb7
JR
1191rstp_port_unref(struct rstp_port *rp)
1192 OVS_EXCLUDED(rstp_mutex)
9efd308e 1193{
f5920067 1194 if (rp && ovs_refcount_unref_relaxed(&rp->ref_cnt) == 1) {
cc33c223
JR
1195 struct rstp *rstp;
1196
f025bcb7
JR
1197 ovs_mutex_lock(&rstp_mutex);
1198 rstp = rp->rstp;
1199 rstp_port_set_state__(rp, RSTP_DISABLED);
d5f31dc8 1200 hmap_remove(&rstp->ports, &rp->node);
37db915e
JR
1201 VLOG_DBG("%s: removed port "RSTP_PORT_ID_FMT"", rstp->name,
1202 rp->port_id);
f025bcb7
JR
1203 ovs_mutex_unlock(&rstp_mutex);
1204 free(rp);
1205 }
1206}
1207
1208/* Sets the port Admin Edge parameter. */
1209static void
1210rstp_port_set_admin_edge__(struct rstp_port *port, bool admin_edge)
1211 OVS_REQUIRES(rstp_mutex)
1212{
1213 if (port->admin_edge != admin_edge) {
1214 VLOG_DBG("%s, port %u: set RSTP Admin Edge to %d", port->rstp->name,
1215 port->port_number, admin_edge);
1216
1217 port->admin_edge = admin_edge;
9efd308e
DV
1218 }
1219}
1220
1221/* Sets the port Auto Edge parameter. */
f025bcb7
JR
1222static void
1223rstp_port_set_auto_edge__(struct rstp_port *port, bool auto_edge)
1224 OVS_REQUIRES(rstp_mutex)
9efd308e 1225{
f025bcb7
JR
1226 if (port->auto_edge != auto_edge) {
1227 VLOG_DBG("%s, port %u: set RSTP Auto Edge to %d", port->rstp->name,
1228 port->port_number, auto_edge);
cc33c223 1229
f025bcb7 1230 port->auto_edge = auto_edge;
9efd308e
DV
1231 }
1232}
1233
67e8c1ac
JR
1234/* Sets the port admin_point_to_point_mac parameter. */
1235static void rstp_port_set_admin_point_to_point_mac__(struct rstp_port *port,
1236 enum rstp_admin_point_to_point_mac_state admin_p2p_mac_state)
1237 OVS_REQUIRES(rstp_mutex)
1238{
1239 VLOG_DBG("%s, port %u: set RSTP port admin-point-to-point-mac to %d",
1240 port->rstp->name, port->port_number, admin_p2p_mac_state);
036dc965
DV
1241 if (port->admin_point_to_point_mac != admin_p2p_mac_state) {
1242 if (admin_p2p_mac_state == RSTP_ADMIN_P2P_MAC_FORCE_TRUE) {
1243 port->admin_point_to_point_mac = admin_p2p_mac_state;
1244 rstp_port_set_oper_point_to_point_mac__(
1245 port, RSTP_OPER_P2P_MAC_STATE_ENABLED);
1246 } else if (admin_p2p_mac_state == RSTP_ADMIN_P2P_MAC_FORCE_FALSE) {
1247 port->admin_point_to_point_mac = admin_p2p_mac_state;
1248 rstp_port_set_oper_point_to_point_mac__(
1249 port, RSTP_OPER_P2P_MAC_STATE_DISABLED);
1250 } else if (admin_p2p_mac_state == RSTP_ADMIN_P2P_MAC_AUTO) {
1251 /* If adminPointToPointMAC is set to Auto, then the value of
1252 * operPointToPointMAC is determined in accordance with the
1253 * specific procedures defined for the MAC entity concerned, as
1254 * defined in 6.5. If these procedures determine that the MAC
1255 * entity is connected to a point-to-point LAN, then
1256 * operPointToPointMAC is set TRUE; otherwise it is set FALSE.
1257 * In the absence of a specific definition of how to determine
1258 * whether the MAC is connected to a point-to-point LAN or not,
1259 * the value of operPointToPointMAC shall be FALSE. */
1260 port->admin_point_to_point_mac = admin_p2p_mac_state;
1261 rstp_port_set_oper_point_to_point_mac__(
1262 port, RSTP_OPER_P2P_MAC_STATE_DISABLED);
1263 }
67e8c1ac
JR
1264 }
1265}
1266
9efd308e
DV
1267/* Sets the port mcheck parameter.
1268 * [17.19.13] May be set by management to force the Port Protocol Migration
1269 * state machine to transmit RST BPDUs for a MigrateTime (17.13.9) period, to
1270 * test whether all STP Bridges (17.4) on the attached LAN have been removed
1271 * and the Port can continue to transmit RSTP BPDUs. Setting mcheck has no
1272 * effect if stpVersion (17.20.12) is TRUE, i.e., the Bridge is operating in
f025bcb7 1273 * STP Compatibility mode.
9efd308e 1274 */
f025bcb7
JR
1275static void
1276rstp_port_set_mcheck__(struct rstp_port *port, bool mcheck)
1277 OVS_REQUIRES(rstp_mutex)
9efd308e 1278{
f025bcb7
JR
1279 if (mcheck == true && port->rstp->force_protocol_version >= 2) {
1280 port->mcheck = true;
9efd308e 1281
f025bcb7
JR
1282 VLOG_DBG("%s, port %u: set RSTP mcheck to %d", port->rstp->name,
1283 port->port_number, mcheck);
9efd308e 1284 }
9efd308e
DV
1285}
1286
1287/* Returns the designated bridge id. */
1288rstp_identifier
1289rstp_get_designated_id(const struct rstp *rstp)
f025bcb7 1290 OVS_EXCLUDED(rstp_mutex)
9efd308e
DV
1291{
1292 rstp_identifier designated_id;
1293
f025bcb7 1294 ovs_mutex_lock(&rstp_mutex);
9efd308e 1295 designated_id = rstp->root_priority.designated_bridge_id;
f025bcb7 1296 ovs_mutex_unlock(&rstp_mutex);
cc33c223 1297
9efd308e
DV
1298 return designated_id;
1299}
1300
1301/* Returns the root bridge id. */
1302rstp_identifier
1303rstp_get_root_id(const struct rstp *rstp)
f025bcb7 1304 OVS_EXCLUDED(rstp_mutex)
9efd308e
DV
1305{
1306 rstp_identifier root_id;
1307
f025bcb7 1308 ovs_mutex_lock(&rstp_mutex);
9efd308e 1309 root_id = rstp->root_priority.root_bridge_id;
f025bcb7 1310 ovs_mutex_unlock(&rstp_mutex);
cc33c223 1311
9efd308e
DV
1312 return root_id;
1313}
1314
1315/* Returns the designated port id. */
1316uint16_t
1317rstp_get_designated_port_id(const struct rstp *rstp)
f025bcb7 1318 OVS_EXCLUDED(rstp_mutex)
9efd308e
DV
1319{
1320 uint16_t designated_port_id;
1321
f025bcb7 1322 ovs_mutex_lock(&rstp_mutex);
9efd308e 1323 designated_port_id = rstp->root_priority.designated_port_id;
f025bcb7 1324 ovs_mutex_unlock(&rstp_mutex);
cc33c223 1325
9efd308e
DV
1326 return designated_port_id;
1327}
1328
1329/* Return the bridge port id. */
1330uint16_t
1331rstp_get_bridge_port_id(const struct rstp *rstp)
f025bcb7 1332 OVS_EXCLUDED(rstp_mutex)
9efd308e
DV
1333{
1334 uint16_t bridge_port_id;
1335
f025bcb7 1336 ovs_mutex_lock(&rstp_mutex);
9efd308e 1337 bridge_port_id = rstp->root_priority.bridge_port_id;
f025bcb7 1338 ovs_mutex_unlock(&rstp_mutex);
cc33c223 1339
9efd308e
DV
1340 return bridge_port_id;
1341}
1342
1343/* Returns true if the bridge believes to the be root of the spanning tree,
1344 * false otherwise.
1345 */
1346bool
1347rstp_is_root_bridge(const struct rstp *rstp)
f025bcb7 1348 OVS_EXCLUDED(rstp_mutex)
9efd308e
DV
1349{
1350 bool is_root;
1351
f025bcb7 1352 ovs_mutex_lock(&rstp_mutex);
9efd308e
DV
1353 is_root = rstp->bridge_identifier ==
1354 rstp->root_priority.designated_bridge_id;
f025bcb7 1355 ovs_mutex_unlock(&rstp_mutex);
cc33c223 1356
9efd308e
DV
1357 return is_root;
1358}
1359
1360/* Returns the bridge ID of the bridge currently believed to be the root. */
1361rstp_identifier
1362rstp_get_designated_root(const struct rstp *rstp)
f025bcb7 1363 OVS_EXCLUDED(rstp_mutex)
9efd308e
DV
1364{
1365 rstp_identifier designated_root;
1366
f025bcb7 1367 ovs_mutex_lock(&rstp_mutex);
9efd308e 1368 designated_root = rstp->root_priority.designated_bridge_id;
f025bcb7 1369 ovs_mutex_unlock(&rstp_mutex);
cc33c223 1370
9efd308e
DV
1371 return designated_root;
1372}
1373
1374/* Returns the port connecting 'rstp' to the root bridge, or a null pointer if
1375 * there is no such port.
1376 */
1377struct rstp_port *
1378rstp_get_root_port(struct rstp *rstp)
f025bcb7 1379 OVS_EXCLUDED(rstp_mutex)
9efd308e
DV
1380{
1381 struct rstp_port *p;
1382
f025bcb7 1383 ovs_mutex_lock(&rstp_mutex);
d5f31dc8 1384 HMAP_FOR_EACH (p, node, &rstp->ports) {
37db915e
JR
1385 if (p->port_id == rstp->root_port_id) {
1386 ovs_mutex_unlock(&rstp_mutex);
1387 return p;
9efd308e
DV
1388 }
1389 }
f025bcb7 1390 ovs_mutex_unlock(&rstp_mutex);
9efd308e
DV
1391 return NULL;
1392}
1393
9efd308e
DV
1394/* Returns the state of port 'p'. */
1395enum rstp_state
1396rstp_port_get_state(const struct rstp_port *p)
2372c146 1397 OVS_EXCLUDED(rstp_mutex)
9efd308e
DV
1398{
1399 enum rstp_state state;
1400
f025bcb7 1401 ovs_mutex_lock(&rstp_mutex);
9efd308e 1402 state = p->rstp_state;
f025bcb7 1403 ovs_mutex_unlock(&rstp_mutex);
cc33c223 1404
9efd308e
DV
1405 return state;
1406}
1407
f025bcb7 1408/* Retrieves port status. */
9efd308e 1409void
f025bcb7
JR
1410rstp_port_get_status(const struct rstp_port *p, uint16_t *id,
1411 enum rstp_state *state, enum rstp_port_role *role,
9c64e6b8
JR
1412 rstp_identifier *designated_bridge_id,
1413 uint16_t *designated_port_id,
1414 uint32_t *designated_path_cost, int *tx_count,
1415 int *rx_count, int *error_count, int *uptime)
f025bcb7 1416 OVS_EXCLUDED(rstp_mutex)
9efd308e 1417{
f025bcb7
JR
1418 ovs_mutex_lock(&rstp_mutex);
1419 *id = p->port_id;
1420 *state = p->rstp_state;
1421 *role = p->role;
1422
9c64e6b8
JR
1423 *designated_bridge_id = p->port_priority.designated_bridge_id;
1424 *designated_port_id = p->port_priority.designated_port_id;
1425 *designated_path_cost = p->port_priority.root_path_cost;
1426
9efd308e
DV
1427 *tx_count = p->tx_count;
1428 *rx_count = p->rx_rstp_bpdu_cnt;
1429 *error_count = p->error_count;
1430 *uptime = p->uptime;
f025bcb7 1431 ovs_mutex_unlock(&rstp_mutex);
9efd308e
DV
1432}
1433
1434void
f025bcb7
JR
1435rstp_port_set(struct rstp_port *port, uint16_t port_num, int priority,
1436 uint32_t path_cost, bool is_admin_edge, bool is_auto_edge,
67e8c1ac
JR
1437 enum rstp_admin_point_to_point_mac_state admin_p2p_mac_state,
1438 bool admin_port_state, bool do_mcheck, void *aux)
f025bcb7 1439 OVS_EXCLUDED(rstp_mutex)
9efd308e 1440{
f025bcb7
JR
1441 ovs_mutex_lock(&rstp_mutex);
1442 port->aux = aux;
1443 rstp_port_set_priority__(port, priority);
1444 rstp_port_set_port_number__(port, port_num);
1445 rstp_port_set_path_cost__(port, path_cost);
1446 rstp_port_set_admin_edge__(port, is_admin_edge);
1447 rstp_port_set_auto_edge__(port, is_auto_edge);
67e8c1ac 1448 rstp_port_set_admin_point_to_point_mac__(port, admin_p2p_mac_state);
37a4efd1 1449 rstp_port_set_administrative_bridge_port__(port, admin_port_state, false);
f025bcb7
JR
1450 rstp_port_set_mcheck__(port, do_mcheck);
1451 ovs_mutex_unlock(&rstp_mutex);
9efd308e
DV
1452}
1453
f025bcb7
JR
1454/* Individual setters only used by test-rstp.c. */
1455void
1456rstp_port_set_priority(struct rstp_port *port, int priority)
1457 OVS_EXCLUDED(rstp_mutex)
9efd308e 1458{
f025bcb7
JR
1459 ovs_mutex_lock(&rstp_mutex);
1460 rstp_port_set_priority__(port, priority);
1461 ovs_mutex_unlock(&rstp_mutex);
1462}
9efd308e 1463
f025bcb7
JR
1464void
1465rstp_port_set_path_cost(struct rstp_port *port, uint32_t path_cost)
1466 OVS_EXCLUDED(rstp_mutex)
1467{
1468 ovs_mutex_lock(&rstp_mutex);
1469 rstp_port_set_path_cost__(port, path_cost);
1470 ovs_mutex_unlock(&rstp_mutex);
1471}
cc33c223 1472
f025bcb7
JR
1473void
1474rstp_port_set_aux(struct rstp_port *port, void *aux)
1475 OVS_EXCLUDED(rstp_mutex)
1476{
1477 ovs_mutex_lock(&rstp_mutex);
1478 port->aux = aux;
1479 ovs_mutex_unlock(&rstp_mutex);
9efd308e
DV
1480}
1481
9efd308e
DV
1482/* Unixctl. */
1483static struct rstp *
cc33c223 1484rstp_find(const char *name)
f025bcb7 1485 OVS_REQUIRES(rstp_mutex)
9efd308e
DV
1486{
1487 struct rstp *rstp;
1488
1489 LIST_FOR_EACH (rstp, node, all_rstps) {
1490 if (!strcmp(rstp->name, name)) {
1491 return rstp;
1492 }
1493 }
1494 return NULL;
1495}
1496
1497static void
1498rstp_unixctl_tcn(struct unixctl_conn *conn, int argc,
1499 const char *argv[], void *aux OVS_UNUSED)
f025bcb7 1500 OVS_EXCLUDED(rstp_mutex)
9efd308e 1501{
f025bcb7 1502 ovs_mutex_lock(&rstp_mutex);
9efd308e
DV
1503 if (argc > 1) {
1504 struct rstp *rstp = rstp_find(argv[1]);
1505 if (!rstp) {
1506 unixctl_command_reply_error(conn, "No such RSTP object");
1507 goto out;
1508 }
1509 rstp->changes = true;
f025bcb7 1510 move_rstp__(rstp);
9efd308e
DV
1511 } else {
1512 struct rstp *rstp;
1513 LIST_FOR_EACH (rstp, node, all_rstps) {
1514 rstp->changes = true;
f025bcb7 1515 move_rstp__(rstp);
9efd308e
DV
1516 }
1517 }
1518 unixctl_command_reply(conn, "OK");
1519
1520out:
f025bcb7 1521 ovs_mutex_unlock(&rstp_mutex);
9efd308e 1522}