]>
Commit | Line | Data |
---|---|---|
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) state machines | |
19 | * implementation. | |
20 | * | |
21 | * Authors: | |
22 | * Martino Fornasa <mf@fornasa.it> | |
23 | * Daniele Venturino <daniele.venturino@m3s.it> | |
24 | * | |
25 | * References to IEEE 802.1D-2004 standard are enclosed in square brackets. | |
26 | * E.g. [17.3], [Table 17-1], etc. | |
27 | * | |
28 | */ | |
29 | ||
30 | #include <config.h> | |
31 | #include "rstp.h" | |
32 | #include "rstp-state-machines.h" | |
33 | #include <arpa/inet.h> | |
34 | #include <inttypes.h> | |
35 | #include <netinet/in.h> | |
36 | #include <stdlib.h> | |
37 | #include <sys/types.h> | |
38 | #include "byte-order.h" | |
39 | #include "connectivity.h" | |
40 | #include "ofpbuf.h" | |
cf62fa4c | 41 | #include "dp-packet.h" |
9efd308e DV |
42 | #include "packets.h" |
43 | #include "seq.h" | |
44 | #include "unixctl.h" | |
45 | #include "util.h" | |
e6211adc | 46 | #include "openvswitch/vlog.h" |
9efd308e DV |
47 | |
48 | VLOG_DEFINE_THIS_MODULE(rstp_sm); | |
49 | ||
50 | #define ROLE_FLAG_MASK 0xC | |
51 | #define ROLE_FLAG_SHIFT 2 | |
52 | ||
53 | enum port_flag { | |
54 | PORT_UNKN = 0, | |
55 | PORT_ALT_BACK = 1, | |
56 | PORT_ROOT = 2, | |
57 | PORT_DES = 3 | |
58 | }; | |
59 | ||
60 | enum bpdu_size { | |
61 | CONFIGURATION_BPDU_SIZE = 35, | |
62 | TOPOLOGY_CHANGE_NOTIFICATION_BPDU_SIZE = 4, | |
63 | RAPID_SPANNING_TREE_BPDU_SIZE = 36 | |
64 | }; | |
65 | ||
a0478216 JR |
66 | /* Same is a subset of SUPERIOR, so can be used as a boolean when the |
67 | * distinction is not significant. */ | |
9efd308e DV |
68 | enum vector_comparison { |
69 | INFERIOR = 0, | |
70 | SUPERIOR = 1, | |
71 | SAME = 2 | |
72 | }; | |
73 | ||
74 | static void decrement_timer(uint16_t *); | |
f025bcb7 JR |
75 | static void rstp_send_bpdu(struct rstp_port *, const void *, size_t) |
76 | OVS_REQUIRES(rstp_mutex); | |
77 | static int validate_received_bpdu(struct rstp_port *, const void *, size_t) | |
78 | OVS_REQUIRES(rstp_mutex); | |
9efd308e DV |
79 | static ovs_be16 time_encode(uint8_t); |
80 | static uint8_t time_decode(ovs_be16); | |
81 | static enum vector_comparison | |
a0478216 JR |
82 | compare_rstp_priority_vectors(const struct rstp_priority_vector *, |
83 | const struct rstp_priority_vector *); | |
9efd308e DV |
84 | static bool rstp_times_equal(struct rstp_times *, struct rstp_times *); |
85 | ||
86 | /* Per-Bridge State Machine */ | |
f025bcb7 JR |
87 | static int port_role_selection_sm(struct rstp *) |
88 | OVS_REQUIRES(rstp_mutex); | |
9efd308e | 89 | /* Per-Port State Machines */ |
f025bcb7 JR |
90 | static int port_receive_sm(struct rstp_port *) |
91 | OVS_REQUIRES(rstp_mutex); | |
92 | static int port_protocol_migration_sm(struct rstp_port *) | |
93 | OVS_REQUIRES(rstp_mutex); | |
94 | static int bridge_detection_sm(struct rstp_port *) | |
95 | OVS_REQUIRES(rstp_mutex); | |
96 | static int port_transmit_sm(struct rstp_port *) | |
97 | OVS_REQUIRES(rstp_mutex); | |
98 | static int port_information_sm(struct rstp_port *) | |
99 | OVS_REQUIRES(rstp_mutex); | |
100 | static int port_role_transition_sm(struct rstp_port *) | |
101 | OVS_REQUIRES(rstp_mutex); | |
102 | static int port_state_transition_sm(struct rstp_port *) | |
103 | OVS_REQUIRES(rstp_mutex); | |
104 | static int topology_change_sm(struct rstp_port *) | |
105 | OVS_REQUIRES(rstp_mutex); | |
9efd308e DV |
106 | /* port_timers_sm() not defined as a state machine */ |
107 | ||
108 | void | |
006f3386 | 109 | process_received_bpdu__(struct rstp_port *p, const void *bpdu_, |
f025bcb7 JR |
110 | size_t bpdu_size) |
111 | OVS_REQUIRES(rstp_mutex) | |
9efd308e | 112 | { |
006f3386 JR |
113 | struct rstp *rstp = p->rstp; |
114 | struct rstp_bpdu *bpdu = (struct rstp_bpdu *)bpdu_; | |
9efd308e | 115 | |
311fb15b | 116 | if (!p->port_enabled) { |
9efd308e | 117 | return; |
311fb15b JR |
118 | } |
119 | if (p->rcvd_bpdu) { | |
9efd308e | 120 | return; |
311fb15b | 121 | } |
006f3386 JR |
122 | |
123 | /* [9.2.9 Encoding of Port Role values] | |
124 | * NOTE. If the Unknown value of the Port Role parameter is received, the | |
125 | * state machines will effectively treat the RST BPDU as if it were a | |
126 | * Configuration BPDU. | |
127 | */ | |
128 | if (bpdu->bpdu_type == RAPID_SPANNING_TREE_BPDU) { | |
129 | uint8_t role = (bpdu->flags & ROLE_FLAG_MASK) >> ROLE_FLAG_SHIFT; | |
130 | ||
131 | if (role == PORT_UNKN) { | |
132 | bpdu->bpdu_type = CONFIGURATION_BPDU; | |
133 | } | |
134 | } | |
135 | ||
9efd308e DV |
136 | if (validate_received_bpdu(p, bpdu, bpdu_size) == 0) { |
137 | p->rcvd_bpdu = true; | |
138 | p->rx_rstp_bpdu_cnt++; | |
139 | ||
140 | memcpy(&p->received_bpdu_buffer, bpdu, sizeof(struct rstp_bpdu)); | |
141 | ||
142 | rstp->changes = true; | |
f025bcb7 | 143 | move_rstp__(rstp); |
9efd308e | 144 | } else { |
f025bcb7 | 145 | VLOG_DBG("%s, port %u: Bad STP or RSTP BPDU received", p->rstp->name, |
4b30ba05 | 146 | p->port_number); |
9efd308e DV |
147 | p->error_count++; |
148 | } | |
149 | } | |
150 | ||
311fb15b | 151 | /* Returns 0 on success. */ |
9efd308e DV |
152 | static int |
153 | validate_received_bpdu(struct rstp_port *p, const void *bpdu, size_t bpdu_size) | |
f025bcb7 | 154 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
155 | { |
156 | /* Validation of received BPDU, see [9.3.4]. */ | |
157 | const struct rstp_bpdu *temp; | |
158 | ||
159 | temp = bpdu; | |
160 | if (bpdu_size < TOPOLOGY_CHANGE_NOTIFICATION_BPDU_SIZE || | |
161 | ntohs(temp->protocol_identifier) != 0) { | |
162 | return -1; | |
163 | } else { | |
311fb15b JR |
164 | if (temp->bpdu_type == CONFIGURATION_BPDU |
165 | && bpdu_size >= CONFIGURATION_BPDU_SIZE | |
166 | && (time_decode(temp->message_age) < time_decode(temp->max_age))) { | |
9efd308e | 167 | if ((ntohll(temp->designated_bridge_id) != |
311fb15b JR |
168 | p->rstp->bridge_identifier) |
169 | || ((ntohll(temp->designated_bridge_id) == | |
170 | p->rstp->bridge_identifier) | |
171 | && (ntohs(temp->designated_port_id) != p->port_id))) { | |
9efd308e | 172 | return 0; |
311fb15b | 173 | } else { |
9efd308e DV |
174 | return -1; |
175 | } | |
176 | } else if (temp->bpdu_type == TOPOLOGY_CHANGE_NOTIFICATION_BPDU) { | |
177 | return 0; | |
178 | } else if (temp->bpdu_type == RAPID_SPANNING_TREE_BPDU && | |
179 | bpdu_size >= RAPID_SPANNING_TREE_BPDU_SIZE) { | |
180 | return 0; | |
311fb15b | 181 | } else { |
9efd308e DV |
182 | return -1; |
183 | } | |
184 | } | |
185 | } | |
186 | ||
187 | /* | |
f025bcb7 JR |
188 | * move_rstp__() |
189 | * This method is invoked to move the State Machines. The SMs move only if the | |
9efd308e DV |
190 | * boolean 'changes' is true, meaning that something changed and the SMs need |
191 | * to work to process this change. | |
192 | * The boolean 'changes' is set every time a SM modifies its state, a BPDU is | |
311fb15b | 193 | * received, a timer expires or port down event is detected. If a parameter is |
9efd308e DV |
194 | * set by management, then 'changes' is set. |
195 | */ | |
196 | #define MAX_RSTP_ITERATIONS 1000 /* safeguard */ | |
197 | int | |
f025bcb7 JR |
198 | move_rstp__(struct rstp *rstp) |
199 | OVS_REQUIRES(rstp_mutex) | |
9efd308e | 200 | { |
9efd308e DV |
201 | int num_iterations; |
202 | num_iterations = 0; | |
203 | ||
204 | while (rstp->changes == true && num_iterations < MAX_RSTP_ITERATIONS) { | |
311fb15b JR |
205 | struct rstp_port *p; |
206 | ||
9efd308e | 207 | VLOG_DBG("%s: move_rstp()", rstp->name); |
311fb15b | 208 | |
9efd308e DV |
209 | rstp->changes = false; |
210 | port_role_selection_sm(rstp); | |
d5f31dc8 | 211 | HMAP_FOR_EACH (p, node, &rstp->ports) { |
37db915e JR |
212 | if (p->rstp_state != RSTP_DISABLED) { |
213 | port_receive_sm(p); | |
214 | bridge_detection_sm(p); | |
215 | port_information_sm(p); | |
216 | port_role_transition_sm(p); | |
217 | port_state_transition_sm(p); | |
218 | topology_change_sm(p); | |
219 | port_transmit_sm(p); | |
220 | port_protocol_migration_sm(p); | |
9efd308e DV |
221 | } |
222 | } | |
223 | num_iterations++; | |
224 | seq_change(connectivity_seq_get()); | |
225 | } | |
226 | if (num_iterations >= MAX_RSTP_ITERATIONS) { | |
227 | VLOG_ERR("%s: move_rstp() reached the iteration safeguard limit!", | |
228 | rstp->name); | |
229 | } | |
230 | return 0; | |
231 | } | |
232 | ||
f025bcb7 JR |
233 | void decrease_rstp_port_timers__(struct rstp *r) |
234 | OVS_REQUIRES(rstp_mutex) | |
9efd308e DV |
235 | { |
236 | struct rstp_port *p; | |
237 | ||
d5f31dc8 | 238 | HMAP_FOR_EACH (p, node, &r->ports) { |
37db915e JR |
239 | decrement_timer(&p->hello_when); |
240 | decrement_timer(&p->tc_while); | |
241 | decrement_timer(&p->fd_while); | |
242 | decrement_timer(&p->rcvd_info_while); | |
243 | decrement_timer(&p->rr_while); | |
244 | decrement_timer(&p->rb_while); | |
245 | decrement_timer(&p->mdelay_while); | |
246 | decrement_timer(&p->edge_delay_while); | |
247 | decrement_timer(&p->tx_count); | |
248 | p->uptime += 1; | |
9efd308e DV |
249 | } |
250 | r->changes = true; | |
f025bcb7 | 251 | move_rstp__(r); |
9efd308e DV |
252 | } |
253 | ||
254 | static void | |
255 | decrement_timer(uint16_t *timer) | |
256 | { | |
257 | if (*timer != 0) { | |
258 | *timer -= 1; | |
259 | } | |
260 | } | |
261 | ||
262 | /* Bridge State Machine. */ | |
263 | /* [17.28] Port Role Selection state machine. */ | |
264 | ||
265 | static void | |
266 | updt_role_disabled_tree(struct rstp *r) | |
f025bcb7 | 267 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
268 | { |
269 | struct rstp_port *p; | |
270 | ||
d5f31dc8 | 271 | HMAP_FOR_EACH (p, node, &r->ports) { |
37db915e | 272 | p->selected_role = ROLE_DISABLED; |
9efd308e DV |
273 | } |
274 | } | |
275 | ||
276 | static void | |
277 | clear_reselect_tree(struct rstp *r) | |
f025bcb7 | 278 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
279 | { |
280 | struct rstp_port *p; | |
281 | ||
d5f31dc8 | 282 | HMAP_FOR_EACH (p, node, &r->ports) { |
37db915e | 283 | p->reselect = false; |
9efd308e DV |
284 | } |
285 | } | |
286 | ||
287 | void | |
f025bcb7 JR |
288 | updt_roles_tree__(struct rstp *r) |
289 | OVS_REQUIRES(rstp_mutex) | |
9efd308e DV |
290 | { |
291 | struct rstp_port *p; | |
292 | int vsel; | |
293 | struct rstp_priority_vector best_vector, candidate_vector; | |
2372c146 JR |
294 | enum rstp_port_role new_root_old_role = ROLE_DESIGNATED; |
295 | uint16_t old_root_port_number = 0; | |
296 | uint16_t new_root_port_number = 0; | |
9efd308e | 297 | |
2372c146 JR |
298 | old_root_port_number = r->root_port_id & 0x00ff; |
299 | if (old_root_port_number) { | |
300 | r->old_root_aux = rstp_get_port_aux__(r, old_root_port_number); | |
301 | } | |
9efd308e DV |
302 | vsel = -1; |
303 | best_vector = r->bridge_priority; | |
304 | /* Letter c1) */ | |
305 | r->root_times = r->bridge_times; | |
306 | /* Letters a) b) c) */ | |
d5f31dc8 | 307 | HMAP_FOR_EACH (p, node, &r->ports) { |
37db915e JR |
308 | uint32_t old_root_path_cost; |
309 | uint32_t root_path_cost; | |
311fb15b | 310 | |
37db915e JR |
311 | if (p->info_is != INFO_IS_RECEIVED) { |
312 | continue; | |
313 | } | |
314 | /* [17.6] */ | |
315 | candidate_vector = p->port_priority; | |
316 | candidate_vector.bridge_port_id = p->port_id; | |
317 | old_root_path_cost = candidate_vector.root_path_cost; | |
318 | root_path_cost = old_root_path_cost + p->port_path_cost; | |
319 | candidate_vector.root_path_cost = root_path_cost; | |
320 | ||
321 | if ((candidate_vector.designated_bridge_id & 0xffffffffffffULL) == | |
322 | (r->bridge_priority.designated_bridge_id & 0xffffffffffffULL)) { | |
55f8993c | 323 | continue; |
37db915e JR |
324 | } |
325 | if (compare_rstp_priority_vectors(&candidate_vector, | |
326 | &best_vector) == SUPERIOR) { | |
327 | best_vector = candidate_vector; | |
328 | r->root_times = p->port_times; | |
329 | r->root_times.message_age++; | |
330 | vsel = p->port_number; | |
2372c146 | 331 | new_root_old_role = p->role; |
9efd308e DV |
332 | } |
333 | } | |
334 | r->root_priority = best_vector; | |
335 | r->root_port_id = best_vector.bridge_port_id; | |
4b30ba05 | 336 | VLOG_DBG("%s: new Root is "RSTP_ID_FMT, r->name, |
9efd308e | 337 | RSTP_ID_ARGS(r->root_priority.root_bridge_id)); |
2372c146 JR |
338 | new_root_port_number = r->root_port_id & 0x00ff; |
339 | if (new_root_port_number) { | |
340 | r->new_root_aux = rstp_get_port_aux__(r, new_root_port_number); | |
341 | } | |
342 | /* Shift learned MAC addresses from an old Root Port to an existing | |
343 | * Alternate Port. */ | |
344 | if (!r->root_changed | |
345 | && new_root_old_role == ROLE_ALTERNATE | |
346 | && new_root_port_number | |
347 | && old_root_port_number | |
348 | && new_root_port_number != old_root_port_number) { | |
349 | r->root_changed = true; | |
350 | } | |
9efd308e | 351 | /* Letters d) e) */ |
d5f31dc8 | 352 | HMAP_FOR_EACH (p, node, &r->ports) { |
37db915e JR |
353 | p->designated_priority_vector.root_bridge_id = |
354 | r->root_priority.root_bridge_id; | |
355 | p->designated_priority_vector.root_path_cost = | |
356 | r->root_priority.root_path_cost; | |
357 | p->designated_priority_vector.designated_bridge_id = | |
358 | r->bridge_identifier; | |
359 | p->designated_priority_vector.designated_port_id = | |
360 | p->port_id; | |
361 | p->designated_times = r->root_times; | |
362 | p->designated_times.hello_time = r->bridge_times.hello_time; | |
9efd308e | 363 | } |
d5f31dc8 | 364 | HMAP_FOR_EACH (p, node, &r->ports) { |
37db915e JR |
365 | switch (p->info_is) { |
366 | case INFO_IS_DISABLED: | |
367 | p->selected_role = ROLE_DISABLED; | |
368 | break; | |
369 | case INFO_IS_AGED: | |
370 | p->updt_info = true; | |
371 | p->selected_role = ROLE_DESIGNATED; | |
372 | break; | |
373 | case INFO_IS_MINE: | |
374 | p->selected_role = ROLE_DESIGNATED; | |
375 | if (compare_rstp_priority_vectors( | |
376 | &p->port_priority, &p->designated_priority_vector) != SAME | |
377 | || !rstp_times_equal(&p->designated_times, &r->root_times)) { | |
9efd308e | 378 | p->updt_info = true; |
37db915e JR |
379 | } |
380 | break; | |
381 | case INFO_IS_RECEIVED: | |
382 | if (vsel == p->port_number) { /* Letter i) */ | |
383 | p->selected_role = ROLE_ROOT; | |
384 | p->updt_info = false; | |
385 | } else if (compare_rstp_priority_vectors( | |
386 | &p->designated_priority_vector, | |
387 | &p->port_priority) == INFERIOR) { | |
388 | if (p->port_priority.designated_bridge_id != | |
389 | r->bridge_identifier) { | |
390 | p->selected_role = ROLE_ALTERNATE; | |
9efd308e | 391 | p->updt_info = false; |
9efd308e | 392 | } else { |
37db915e JR |
393 | p->selected_role = ROLE_BACKUP; |
394 | p->updt_info = false; | |
9efd308e | 395 | } |
37db915e JR |
396 | } else { |
397 | p->selected_role = ROLE_DESIGNATED; | |
398 | p->updt_info = true; | |
9efd308e | 399 | } |
37db915e JR |
400 | break; |
401 | default: | |
402 | OVS_NOT_REACHED(); | |
403 | /* no break */ | |
9efd308e DV |
404 | } |
405 | } | |
406 | seq_change(connectivity_seq_get()); | |
407 | } | |
408 | ||
409 | static void | |
410 | set_selected_tree(struct rstp *r) | |
f025bcb7 | 411 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
412 | { |
413 | struct rstp_port *p; | |
414 | ||
d5f31dc8 | 415 | HMAP_FOR_EACH (p, node, &r->ports) { |
37db915e JR |
416 | if (p->reselect) { |
417 | return; | |
9efd308e DV |
418 | } |
419 | } | |
d5f31dc8 | 420 | HMAP_FOR_EACH (p, node, &r->ports) { |
37db915e JR |
421 | p->selected = true; |
422 | } | |
9efd308e DV |
423 | } |
424 | ||
425 | static int | |
426 | port_role_selection_sm(struct rstp *r) | |
f025bcb7 | 427 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
428 | { |
429 | enum port_role_selection_state_machine old_state; | |
430 | struct rstp_port *p; | |
431 | ||
432 | old_state = r->port_role_selection_sm_state; | |
433 | ||
434 | switch (r->port_role_selection_sm_state) { | |
435 | case PORT_ROLE_SELECTION_SM_INIT: | |
311fb15b | 436 | if (r->begin) { |
9efd308e DV |
437 | r->port_role_selection_sm_state = |
438 | PORT_ROLE_SELECTION_SM_INIT_BRIDGE_EXEC; | |
311fb15b | 439 | } |
9efd308e DV |
440 | break; |
441 | case PORT_ROLE_SELECTION_SM_INIT_BRIDGE_EXEC: | |
442 | updt_role_disabled_tree(r); | |
443 | r->port_role_selection_sm_state = PORT_ROLE_SELECTION_SM_INIT_BRIDGE; | |
444 | /* no break */ | |
445 | case PORT_ROLE_SELECTION_SM_INIT_BRIDGE: | |
446 | r->port_role_selection_sm_state = | |
447 | PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC; | |
448 | break; | |
449 | case PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC: | |
450 | clear_reselect_tree(r); | |
f025bcb7 | 451 | updt_roles_tree__(r); |
9efd308e DV |
452 | set_selected_tree(r); |
453 | r->port_role_selection_sm_state = | |
454 | PORT_ROLE_SELECTION_SM_ROLE_SELECTION; | |
455 | /* no break */ | |
456 | case PORT_ROLE_SELECTION_SM_ROLE_SELECTION: | |
d5f31dc8 | 457 | HMAP_FOR_EACH (p, node, &r->ports) { |
37db915e JR |
458 | if (p->reselect) { |
459 | r->port_role_selection_sm_state = | |
460 | PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC; | |
461 | break; | |
9efd308e DV |
462 | } |
463 | } | |
464 | break; | |
465 | default: | |
466 | OVS_NOT_REACHED(); | |
467 | /* no break */ | |
468 | } | |
469 | if (old_state != r->port_role_selection_sm_state) { | |
470 | r->changes = true; | |
4b30ba05 JR |
471 | VLOG_DBG("%s: Port_role_selection_sm %d -> %d", r->name, |
472 | old_state, r->port_role_selection_sm_state); | |
9efd308e DV |
473 | } |
474 | return 0; | |
475 | } | |
476 | ||
477 | /* Port State Machines */ | |
478 | ||
479 | /* [17.23 - Port receive state machine] */ | |
480 | ||
481 | static void | |
482 | updt_bpdu_version(struct rstp_port *p) /* [17.21.22] */ | |
f025bcb7 | 483 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
484 | { |
485 | switch (p->received_bpdu_buffer.bpdu_type) { | |
486 | case CONFIGURATION_BPDU: | |
487 | case TOPOLOGY_CHANGE_NOTIFICATION_BPDU: | |
488 | p->rcvd_rstp = false; | |
489 | p->rcvd_stp = true; | |
490 | break; | |
491 | case RAPID_SPANNING_TREE_BPDU: | |
492 | p->rcvd_rstp = true; | |
493 | p->rcvd_stp = false; | |
494 | break; | |
495 | default: | |
496 | OVS_NOT_REACHED(); | |
497 | /* no break */ | |
498 | } | |
499 | } | |
500 | ||
501 | static int | |
502 | port_receive_sm(struct rstp_port *p) | |
f025bcb7 | 503 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
504 | { |
505 | enum port_receive_state_machine old_state; | |
506 | struct rstp *r; | |
507 | ||
508 | old_state = p->port_receive_sm_state; | |
509 | r = p->rstp; | |
510 | ||
511 | switch (p->port_receive_sm_state) { | |
512 | case PORT_RECEIVE_SM_INIT: | |
513 | if (r->begin || ((p->rcvd_bpdu || (p->edge_delay_while != | |
514 | r->migrate_time)) && !p->port_enabled)) { | |
515 | p->port_receive_sm_state = PORT_RECEIVE_SM_DISCARD_EXEC; | |
516 | } | |
517 | break; | |
518 | case PORT_RECEIVE_SM_DISCARD_EXEC: | |
519 | p->rcvd_bpdu = p->rcvd_rstp = p->rcvd_stp = false; | |
520 | p->rcvd_msg = false; | |
521 | p->edge_delay_while = r->migrate_time; | |
522 | p->port_receive_sm_state = PORT_RECEIVE_SM_DISCARD; | |
523 | /* no break */ | |
524 | case PORT_RECEIVE_SM_DISCARD: | |
6448df94 JR |
525 | if ((p->rcvd_bpdu || (p->edge_delay_while != r->migrate_time)) |
526 | && !p->port_enabled) { | |
527 | /* Global transition. */ | |
528 | p->port_receive_sm_state = PORT_RECEIVE_SM_DISCARD_EXEC; | |
529 | } else if (p->rcvd_bpdu && p->port_enabled) { | |
9efd308e DV |
530 | p->port_receive_sm_state = PORT_RECEIVE_SM_RECEIVE_EXEC; |
531 | } | |
532 | break; | |
533 | case PORT_RECEIVE_SM_RECEIVE_EXEC: | |
534 | updt_bpdu_version(p); | |
535 | p->oper_edge = p->rcvd_bpdu = false; | |
536 | p->rcvd_msg = true; | |
537 | p->edge_delay_while = r->migrate_time; | |
538 | p->port_receive_sm_state = PORT_RECEIVE_SM_RECEIVE; | |
539 | /* no break */ | |
540 | case PORT_RECEIVE_SM_RECEIVE: | |
6448df94 JR |
541 | if ((p->rcvd_bpdu || (p->edge_delay_while != r->migrate_time)) |
542 | && !p->port_enabled) { | |
543 | /* Global transition. */ | |
544 | p->port_receive_sm_state = PORT_RECEIVE_SM_DISCARD_EXEC; | |
545 | } else if (p->rcvd_bpdu && p->port_enabled && !p->rcvd_msg) { | |
9efd308e DV |
546 | p->port_receive_sm_state = PORT_RECEIVE_SM_RECEIVE_EXEC; |
547 | } | |
548 | break; | |
549 | default: | |
550 | OVS_NOT_REACHED(); | |
551 | /* no break */ | |
552 | } | |
553 | if (old_state != p->port_receive_sm_state) { | |
554 | r->changes = true; | |
555 | VLOG_DBG("%s, port %u: Port_receive_sm %d -> %d", p->rstp->name, | |
556 | p->port_number, old_state, p->port_receive_sm_state); | |
557 | } | |
558 | return 0; | |
559 | } | |
560 | ||
561 | /* [17.24 - Port Protocol Migration state machine] */ | |
562 | static int | |
563 | port_protocol_migration_sm(struct rstp_port *p) | |
f025bcb7 | 564 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
565 | { |
566 | enum port_protocol_migration_state_machine old_state; | |
567 | struct rstp *r; | |
568 | ||
569 | old_state = p->port_protocol_migration_sm_state; | |
570 | r = p->rstp; | |
571 | ||
572 | switch (p->port_protocol_migration_sm_state) { | |
573 | case PORT_PROTOCOL_MIGRATION_SM_INIT: | |
311fb15b JR |
574 | p->port_protocol_migration_sm_state = |
575 | PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC; | |
9efd308e DV |
576 | /* no break */ |
577 | case PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC: | |
578 | p->mcheck = false; | |
579 | p->send_rstp = r->rstp_version; | |
580 | p->mdelay_while = r->migrate_time; | |
581 | p->port_protocol_migration_sm_state = | |
582 | PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP; | |
583 | /* no break */ | |
584 | case PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP: | |
585 | if (p->mdelay_while == 0) { | |
311fb15b JR |
586 | p->port_protocol_migration_sm_state = |
587 | PORT_PROTOCOL_MIGRATION_SM_SENSING_EXEC; | |
588 | } else if ((p->mdelay_while != r->migrate_time) && !p->port_enabled) { | |
9efd308e DV |
589 | p->port_protocol_migration_sm_state = |
590 | PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC; | |
591 | } | |
592 | break; | |
593 | case PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP_EXEC: | |
311fb15b | 594 | p->send_rstp = false; |
9efd308e DV |
595 | p->mdelay_while = r->migrate_time; |
596 | p->port_protocol_migration_sm_state = | |
597 | PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP; | |
598 | /* no break */ | |
599 | case PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP: | |
600 | if ((p->mdelay_while == 0) || (!p->port_enabled) || p->mcheck) { | |
601 | p->port_protocol_migration_sm_state = | |
602 | PORT_PROTOCOL_MIGRATION_SM_SENSING_EXEC; | |
603 | } | |
604 | break; | |
605 | case PORT_PROTOCOL_MIGRATION_SM_SENSING_EXEC: | |
606 | p->rcvd_rstp = false; | |
607 | p->rcvd_stp = false; | |
608 | p->port_protocol_migration_sm_state = | |
609 | PORT_PROTOCOL_MIGRATION_SM_SENSING; | |
610 | /* no break */ | |
611 | case PORT_PROTOCOL_MIGRATION_SM_SENSING: | |
612 | if (!p->port_enabled || p->mcheck || ((r->rstp_version) && | |
311fb15b | 613 | !p->send_rstp && p->rcvd_rstp)) { |
9efd308e DV |
614 | p->port_protocol_migration_sm_state = |
615 | PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC; | |
311fb15b | 616 | } else if (p->send_rstp && p->rcvd_stp) { |
9efd308e DV |
617 | p->port_protocol_migration_sm_state = |
618 | PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP_EXEC; | |
619 | } | |
620 | break; | |
621 | default: | |
622 | OVS_NOT_REACHED(); | |
623 | /* no break */ | |
624 | } | |
625 | if (old_state != p->port_protocol_migration_sm_state) { | |
626 | r->changes = true; | |
627 | VLOG_DBG("%s, port %u: port_protocol_migration_sm %d -> %d", | |
628 | p->rstp->name, p->port_number, old_state, | |
629 | p->port_protocol_migration_sm_state); | |
630 | } | |
631 | ||
632 | return 0; | |
633 | } | |
634 | ||
635 | /* [17.25 - Bridge Detection state machine] */ | |
636 | static int | |
637 | bridge_detection_sm(struct rstp_port *p) | |
f025bcb7 | 638 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
639 | { |
640 | enum bridge_detection_state_machine old_state; | |
641 | struct rstp *r; | |
642 | ||
643 | old_state = p->bridge_detection_sm_state; | |
644 | r = p->rstp; | |
645 | ||
646 | switch (p->bridge_detection_sm_state) { | |
647 | case BRIDGE_DETECTION_SM_INIT: | |
648 | if (r->begin && p->admin_edge) { | |
649 | p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_EDGE_EXEC; | |
650 | } else if (r->begin && !p->admin_edge) { | |
651 | p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_NOT_EDGE_EXEC; | |
652 | } | |
653 | break; | |
654 | case BRIDGE_DETECTION_SM_EDGE_EXEC: | |
655 | p->oper_edge = true; | |
656 | p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_EDGE; | |
657 | /* no break */ | |
658 | case BRIDGE_DETECTION_SM_EDGE: | |
659 | if ((!p->port_enabled && !p->admin_edge) || !p->oper_edge) { | |
660 | p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_NOT_EDGE_EXEC; | |
661 | } | |
662 | break; | |
663 | case BRIDGE_DETECTION_SM_NOT_EDGE_EXEC: | |
664 | p->oper_edge = false; | |
665 | p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_NOT_EDGE; | |
666 | /* no break */ | |
667 | case BRIDGE_DETECTION_SM_NOT_EDGE: | |
311fb15b JR |
668 | if ((!p->port_enabled && p->admin_edge) |
669 | || ((p->edge_delay_while == 0) && p->auto_edge && p->send_rstp | |
670 | && p->proposing)) { | |
9efd308e DV |
671 | p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_EDGE_EXEC; |
672 | } | |
673 | break; | |
674 | default: | |
675 | OVS_NOT_REACHED(); | |
676 | /* no break */ | |
677 | } | |
678 | if (old_state != p->bridge_detection_sm_state) { | |
679 | r->changes = true; | |
680 | VLOG_DBG("%s, port %u: bridge_detection_sm %d -> %d", p->rstp->name, | |
681 | p->port_number, old_state, p->bridge_detection_sm_state); | |
682 | } | |
683 | return 0; | |
684 | } | |
685 | ||
686 | /* [17.26 - Port Transmit state machine] */ | |
687 | static void | |
688 | rstp_send_bpdu(struct rstp_port *p, const void *bpdu, size_t bpdu_size) | |
f025bcb7 | 689 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
690 | { |
691 | struct eth_header *eth; | |
692 | struct llc_header *llc; | |
cf62fa4c | 693 | struct dp_packet *pkt; |
9efd308e DV |
694 | |
695 | /* Skeleton. */ | |
cf62fa4c PS |
696 | pkt = dp_packet_new(ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size); |
697 | eth = dp_packet_put_zeros(pkt, sizeof *eth); | |
698 | llc = dp_packet_put_zeros(pkt, sizeof *llc); | |
82eb5b0a | 699 | dp_packet_reset_offsets(pkt); |
cf62fa4c | 700 | dp_packet_set_l3(pkt, dp_packet_put(pkt, bpdu, bpdu_size)); |
9efd308e DV |
701 | |
702 | /* 802.2 header. */ | |
703 | memcpy(eth->eth_dst, eth_addr_stp, ETH_ADDR_LEN); | |
704 | /* p->rstp->send_bpdu() must fill in source address. */ | |
cf62fa4c | 705 | eth->eth_type = htons(dp_packet_size(pkt) - ETH_HEADER_LEN); |
9efd308e DV |
706 | |
707 | /* LLC header. */ | |
708 | llc->llc_dsap = STP_LLC_DSAP; | |
709 | llc->llc_ssap = STP_LLC_SSAP; | |
710 | llc->llc_cntl = STP_LLC_CNTL; | |
6b90bc57 | 711 | p->rstp->send_bpdu(pkt, p->aux, p->rstp->aux); |
9efd308e DV |
712 | } |
713 | ||
714 | static void | |
715 | record_agreement(struct rstp_port *p) | |
f025bcb7 | 716 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
717 | { |
718 | struct rstp *r; | |
719 | ||
720 | r = p->rstp; | |
721 | if (r->rstp_version && p->oper_point_to_point_mac && | |
311fb15b | 722 | ((p->received_bpdu_buffer.flags & BPDU_FLAG_AGREEMENT))) { |
9efd308e DV |
723 | p->agreed = true; |
724 | p->proposing = false; | |
725 | } else { | |
726 | p->agreed = false; | |
727 | } | |
728 | } | |
729 | ||
730 | static void | |
731 | set_tc_flags(struct rstp_port *p) | |
f025bcb7 | 732 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
733 | { |
734 | /* Sets rcvd_tc and/or rcvd_tc_ack if the Topology Change and/or Topology | |
735 | * Change Acknowledgment flags, respectively, are set in a ConfigBPDU or | |
736 | * RST BPDU. | |
737 | */ | |
738 | if (p->received_bpdu_buffer.bpdu_type == CONFIGURATION_BPDU || | |
311fb15b | 739 | p->received_bpdu_buffer.bpdu_type == RAPID_SPANNING_TREE_BPDU) { |
9efd308e DV |
740 | if ((p->received_bpdu_buffer.flags & BPDU_FLAG_TOPCHANGE) != 0) { |
741 | p->rcvd_tc = true; | |
742 | } | |
743 | if ((p->received_bpdu_buffer.flags & BPDU_FLAG_TOPCHANGEACK) != 0) { | |
744 | p->rcvd_tc_ack = true; | |
745 | } | |
746 | } | |
747 | /* Sets rcvd_tcn true if the BPDU is a TCN BPDU. */ | |
311fb15b JR |
748 | if (p->received_bpdu_buffer.bpdu_type |
749 | == TOPOLOGY_CHANGE_NOTIFICATION_BPDU) { | |
9efd308e DV |
750 | p->rcvd_tcn = true; |
751 | } | |
752 | } | |
753 | ||
754 | static void | |
755 | record_dispute(struct rstp_port *p) | |
f025bcb7 | 756 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
757 | { |
758 | if ((p->received_bpdu_buffer.flags & BPDU_FLAG_LEARNING) != 0) { | |
b5ba2490 JR |
759 | /* 802.1D-2004 says to set the agreed flag and to clear the proposing |
760 | * flag. 802.1q-2008 instead says to set the disputed variable and to | |
761 | * clear the agreed variable. */ | |
762 | p->disputed = true; | |
763 | p->agreed = false; | |
9efd308e DV |
764 | } |
765 | } | |
766 | ||
767 | static void | |
768 | record_proposal(struct rstp_port *p) | |
f025bcb7 | 769 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
770 | { |
771 | enum port_flag role = | |
772 | ((p->received_bpdu_buffer.flags) & ROLE_FLAG_MASK) >> ROLE_FLAG_SHIFT; | |
311fb15b JR |
773 | |
774 | if ((role == PORT_DES) | |
775 | && ((p->received_bpdu_buffer.flags & BPDU_FLAG_PROPOSAL) != 0)) { | |
9efd308e DV |
776 | p->proposed = true; |
777 | } | |
778 | } | |
779 | ||
780 | static void | |
781 | record_priority(struct rstp_port *p) | |
f025bcb7 | 782 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
783 | { |
784 | p->port_priority.root_bridge_id = p->msg_priority.root_bridge_id; | |
785 | p->port_priority.root_path_cost = p->msg_priority.root_path_cost; | |
786 | p->port_priority.designated_bridge_id = | |
787 | p->msg_priority.designated_bridge_id; | |
788 | p->port_priority.designated_port_id = p->msg_priority.designated_port_id; | |
789 | } | |
790 | ||
791 | static void | |
792 | record_times(struct rstp_port *p) | |
f025bcb7 | 793 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
794 | { |
795 | p->port_times = p->msg_times; | |
796 | if (p->msg_times.hello_time == 0) { | |
797 | p->port_times.hello_time = 1; | |
798 | } | |
799 | } | |
800 | ||
801 | static void | |
802 | updt_rcvd_info_while(struct rstp_port *p) | |
f025bcb7 | 803 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
804 | { |
805 | /* [17.21.23] | |
806 | * The value assigned to rcvdInfoWhile is the three times the Hello Time, | |
807 | * if Message Age, incremented by 1 second and rounded to the nearest whole | |
808 | * second, does not exceed Max Age, and is zero otherwise. | |
809 | */ | |
810 | if (p->port_times.message_age < p->port_times.max_age) { | |
811 | p->rcvd_info_while = p->port_times.hello_time * 3; | |
311fb15b | 812 | } else { |
9efd308e | 813 | p->rcvd_info_while = 0; |
311fb15b | 814 | } |
9efd308e DV |
815 | } |
816 | ||
817 | /* Times are internally held in seconds, while the protocol uses 1/256 seconds. | |
818 | * time_encode() is used to convert time values sent in bpdus, while | |
819 | * time_decode() is used to convert time values received in bpdus. | |
820 | */ | |
821 | static ovs_be16 | |
822 | time_encode(uint8_t value) | |
823 | { | |
824 | return htons(value * 256); | |
825 | } | |
826 | ||
827 | static uint8_t | |
828 | time_decode(ovs_be16 encoded) | |
829 | { | |
830 | return ntohs(encoded) / 256; | |
831 | } | |
832 | ||
833 | /* [17.21.19] */ | |
834 | static void | |
835 | tx_config(struct rstp_port *p) | |
f025bcb7 | 836 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
837 | { |
838 | struct rstp_bpdu bpdu; | |
839 | ||
840 | bpdu.protocol_identifier = htons(0); | |
841 | bpdu.protocol_version_identifier = 0; | |
842 | bpdu.bpdu_type = CONFIGURATION_BPDU; | |
843 | bpdu.root_bridge_id = htonll(p->designated_priority_vector.root_bridge_id); | |
844 | bpdu.root_path_cost = htonl(p->designated_priority_vector.root_path_cost); | |
845 | bpdu.designated_bridge_id = | |
846 | htonll(p->designated_priority_vector.designated_bridge_id); | |
847 | bpdu.designated_port_id = | |
848 | htons(p->designated_priority_vector.designated_port_id); | |
849 | bpdu.message_age = time_encode(p->designated_times.message_age); | |
850 | bpdu.max_age = time_encode(p->designated_times.max_age); | |
851 | bpdu.hello_time = time_encode(p->designated_times.hello_time); | |
852 | bpdu.forward_delay = time_encode(p->designated_times.forward_delay); | |
853 | bpdu.flags = 0; | |
854 | if (p->tc_while != 0) { | |
855 | bpdu.flags |= BPDU_FLAG_TOPCHANGE; | |
856 | } | |
857 | if (p->tc_ack != 0) { | |
858 | bpdu.flags |= BPDU_FLAG_TOPCHANGEACK; | |
859 | } | |
860 | rstp_send_bpdu(p, &bpdu, sizeof(struct rstp_bpdu)); | |
861 | } | |
862 | ||
863 | /* [17.21.20] */ | |
864 | static void | |
865 | tx_rstp(struct rstp_port *p) | |
f025bcb7 | 866 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
867 | { |
868 | struct rstp_bpdu bpdu; | |
869 | ||
870 | bpdu.protocol_identifier = htons(0); | |
871 | bpdu.protocol_version_identifier = 2; | |
872 | bpdu.bpdu_type = RAPID_SPANNING_TREE_BPDU; | |
873 | bpdu.root_bridge_id = htonll(p->designated_priority_vector.root_bridge_id); | |
874 | bpdu.root_path_cost = htonl(p->designated_priority_vector.root_path_cost); | |
875 | bpdu.designated_bridge_id = | |
876 | htonll(p->designated_priority_vector.designated_bridge_id); | |
877 | bpdu.designated_port_id = | |
878 | htons(p->designated_priority_vector.designated_port_id); | |
879 | bpdu.message_age = time_encode(p->designated_times.message_age); | |
880 | bpdu.max_age = time_encode(p->designated_times.max_age); | |
881 | bpdu.hello_time = time_encode(p->designated_times.hello_time); | |
882 | bpdu.forward_delay = time_encode(p->designated_times.forward_delay); | |
883 | bpdu.flags = 0; | |
311fb15b | 884 | |
9efd308e DV |
885 | switch (p->role) { |
886 | case ROLE_ROOT: | |
887 | bpdu.flags = PORT_ROOT << ROLE_FLAG_SHIFT; | |
888 | break; | |
889 | case ROLE_DESIGNATED: | |
890 | bpdu.flags = PORT_DES << ROLE_FLAG_SHIFT; | |
891 | break; | |
892 | case ROLE_ALTERNATE: | |
893 | case ROLE_BACKUP: | |
894 | bpdu.flags = PORT_ALT_BACK << ROLE_FLAG_SHIFT; | |
895 | break; | |
896 | case ROLE_DISABLED: | |
4b30ba05 | 897 | /* Should not happen! */ |
9efd308e | 898 | VLOG_ERR("%s transmitting bpdu in disabled role on port " |
4b30ba05 | 899 | RSTP_PORT_ID_FMT, p->rstp->name, p->port_id); |
9efd308e DV |
900 | break; |
901 | } | |
902 | if (p->agree) { | |
903 | bpdu.flags |= BPDU_FLAG_AGREEMENT; | |
904 | } | |
905 | if (p->proposing) { | |
906 | bpdu.flags |= BPDU_FLAG_PROPOSAL; | |
907 | } | |
908 | if (p->tc_while != 0) { | |
909 | bpdu.flags |= BPDU_FLAG_TOPCHANGE; | |
910 | } | |
911 | if (p->learning) { | |
912 | bpdu.flags |= BPDU_FLAG_LEARNING; | |
913 | } | |
914 | if (p->forwarding) { | |
915 | bpdu.flags |= BPDU_FLAG_FORWARDING; | |
916 | } | |
917 | bpdu.version1_length = 0; | |
918 | rstp_send_bpdu(p, &bpdu, sizeof(struct rstp_bpdu)); | |
919 | } | |
920 | ||
921 | /* [17.21.21] */ | |
922 | static void | |
923 | tx_tcn(struct rstp_port *p) | |
f025bcb7 | 924 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
925 | { |
926 | struct rstp_bpdu bpdu; | |
927 | ||
928 | memset(&bpdu, 0, sizeof(struct rstp_bpdu)); | |
929 | ||
930 | bpdu.protocol_identifier = htons(0); | |
931 | bpdu.protocol_version_identifier = 0; | |
932 | bpdu.bpdu_type = TOPOLOGY_CHANGE_NOTIFICATION_BPDU; | |
933 | rstp_send_bpdu(p, &bpdu, sizeof(struct rstp_bpdu)); | |
934 | } | |
935 | ||
936 | static int | |
937 | port_transmit_sm(struct rstp_port *p) | |
f025bcb7 | 938 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
939 | { |
940 | enum port_transmit_state_machine old_state; | |
941 | struct rstp *r; | |
942 | ||
943 | old_state = p->port_transmit_sm_state; | |
944 | r = p->rstp; | |
945 | ||
946 | switch (p->port_transmit_sm_state) { | |
947 | case PORT_TRANSMIT_SM_INIT: | |
948 | if (r->begin) { | |
949 | p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_INIT_EXEC; | |
950 | } | |
951 | break; | |
952 | case PORT_TRANSMIT_SM_TRANSMIT_INIT_EXEC: | |
953 | p->new_info = true; | |
954 | p->tx_count = 0; | |
955 | p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_INIT; | |
956 | /* no break */ | |
957 | case PORT_TRANSMIT_SM_TRANSMIT_INIT: | |
958 | p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC; | |
959 | break; | |
960 | case PORT_TRANSMIT_SM_TRANSMIT_PERIODIC_EXEC: | |
961 | p->new_info = p->new_info || (p->role == ROLE_DESIGNATED || | |
962 | (p->role == ROLE_ROOT && p->tc_while != 0)); | |
963 | p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_PERIODIC; | |
964 | /* no break */ | |
965 | case PORT_TRANSMIT_SM_TRANSMIT_PERIODIC: | |
966 | p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC; | |
967 | break; | |
968 | case PORT_TRANSMIT_SM_IDLE_EXEC: | |
969 | p->hello_when = r->bridge_hello_time; | |
970 | p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE; | |
971 | /* no break */ | |
972 | case PORT_TRANSMIT_SM_IDLE: | |
973 | if (p->role == ROLE_DISABLED) { | |
4b30ba05 JR |
974 | VLOG_DBG("%s, port %u: port_transmit_sm ROLE == DISABLED.", |
975 | p->rstp->name, p->port_number); | |
9efd308e | 976 | break; |
311fb15b JR |
977 | } else if (p->send_rstp && p->new_info |
978 | && p->tx_count < r->transmit_hold_count | |
979 | && p->hello_when != 0 && p->selected && !p->updt_info) { | |
9efd308e | 980 | p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_RSTP_EXEC; |
311fb15b | 981 | } else if (p->hello_when == 0 && p->selected && !p->updt_info) { |
9efd308e DV |
982 | p->port_transmit_sm_state = |
983 | PORT_TRANSMIT_SM_TRANSMIT_PERIODIC_EXEC; | |
311fb15b JR |
984 | } else if (!p->send_rstp && p->new_info && p->role == ROLE_ROOT |
985 | && p->tx_count < r->transmit_hold_count | |
986 | && p->hello_when != 0 && p->selected && !p->updt_info) { | |
9efd308e | 987 | p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_TCN_EXEC; |
311fb15b JR |
988 | } else if (!p->send_rstp && p->new_info && p->role == ROLE_DESIGNATED |
989 | && p->tx_count < r->transmit_hold_count | |
990 | && p->hello_when != 0 && p->selected && !p->updt_info) { | |
9efd308e DV |
991 | p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_CONFIG_EXEC; |
992 | } | |
993 | break; | |
994 | case PORT_TRANSMIT_SM_TRANSMIT_CONFIG_EXEC: | |
995 | p->new_info = false; | |
996 | tx_config(p); | |
997 | p->tx_count += 1; | |
998 | p->tc_ack = false; | |
999 | p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_CONFIG; | |
1000 | /* no break */ | |
1001 | case PORT_TRANSMIT_SM_TRANSMIT_CONFIG: | |
1002 | p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC; | |
1003 | break; | |
1004 | case PORT_TRANSMIT_SM_TRANSMIT_TCN_EXEC: | |
1005 | p->new_info = false; | |
1006 | tx_tcn(p); | |
1007 | p->tx_count += 1; | |
1008 | p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_TCN; | |
1009 | /* no break */ | |
1010 | case PORT_TRANSMIT_SM_TRANSMIT_TCN: | |
1011 | p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC; | |
1012 | break; | |
1013 | case PORT_TRANSMIT_SM_TRANSMIT_RSTP_EXEC: | |
1014 | p->new_info = false; | |
1015 | tx_rstp(p); | |
1016 | p->tx_count += 1; | |
1017 | p->tc_ack = false; | |
1018 | p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_RSTP; | |
1019 | /* no break */ | |
1020 | case PORT_TRANSMIT_SM_TRANSMIT_RSTP: | |
1021 | p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC; | |
1022 | break; | |
1023 | default: | |
1024 | OVS_NOT_REACHED(); | |
1025 | /* no break */ | |
1026 | } | |
1027 | if (old_state != p->port_transmit_sm_state) { | |
1028 | r->changes = true; | |
1029 | VLOG_DBG("%s, port %u: port_transmit_sm %d -> %d", p->rstp->name, | |
1030 | p->port_number, old_state, p->port_transmit_sm_state); | |
1031 | } | |
1032 | return 0; | |
1033 | } | |
1034 | ||
1035 | /* [17.27 Port Information state machine] */ | |
1036 | #define RECEIVED 0 | |
1037 | #define MINE 1 | |
1038 | ||
1039 | static int | |
1040 | rcv_info(struct rstp_port *p) | |
f025bcb7 | 1041 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
1042 | { |
1043 | enum vector_comparison cp; | |
1044 | bool ct; | |
1045 | enum port_flag role; | |
1046 | ||
1047 | p->msg_priority.root_bridge_id = | |
1048 | ntohll(p->received_bpdu_buffer.root_bridge_id); | |
1049 | p->msg_priority.root_path_cost = | |
1050 | ntohl(p->received_bpdu_buffer.root_path_cost); | |
1051 | p->msg_priority.designated_bridge_id = | |
1052 | ntohll(p->received_bpdu_buffer.designated_bridge_id); | |
1053 | p->msg_priority.designated_port_id = | |
1054 | ntohs(p->received_bpdu_buffer.designated_port_id); | |
1055 | ||
1056 | p->msg_times.forward_delay = | |
1057 | time_decode(p->received_bpdu_buffer.forward_delay); | |
1058 | p->msg_times.hello_time = time_decode(p->received_bpdu_buffer.hello_time); | |
1059 | p->msg_times.max_age = time_decode(p->received_bpdu_buffer.max_age); | |
1060 | p->msg_times.message_age = | |
1061 | time_decode(p->received_bpdu_buffer.message_age); | |
1062 | ||
a0478216 | 1063 | cp = compare_rstp_priority_vectors(&p->msg_priority, &p->port_priority); |
9efd308e | 1064 | ct = rstp_times_equal(&p->port_times, &p->msg_times); |
12c14838 DV |
1065 | /* Configuration BPDU conveys a Designated Port Role. */ |
1066 | if (p->received_bpdu_buffer.bpdu_type == CONFIGURATION_BPDU) { | |
1067 | role = PORT_DES; | |
1068 | } else { | |
1069 | role = | |
1070 | (p->received_bpdu_buffer.flags & ROLE_FLAG_MASK) >> ROLE_FLAG_SHIFT; | |
1071 | } | |
9efd308e | 1072 | |
11d4d408 DV |
1073 | /* 802.1D-2004 does not report this behaviour. |
1074 | * 802.1Q-2008 says set rcvdTcn. */ | |
1075 | if (p->received_bpdu_buffer.bpdu_type == | |
1076 | TOPOLOGY_CHANGE_NOTIFICATION_BPDU) { | |
1077 | p->rcvd_tcn = true; | |
1078 | return OTHER_INFO; | |
1079 | } | |
1080 | ||
9efd308e DV |
1081 | /* Returns SuperiorDesignatedInfo if: |
1082 | * a) The received message conveys a Designated Port Role, and | |
1083 | * 1) The message priority is superior (17.6) to the Port.s port priority | |
1084 | * vector, or | |
1085 | * 2) The message priority vector is the same as the Port.s port priority | |
1086 | * vector, and any of the received timer parameter values (msg_times. | |
1087 | * 17.19.15) differ from those already held for the Port (port_times | |
1088 | * 17.19.22). | |
1089 | * NOTE: Configuration BPDU explicitly conveys a Designated Port Role. | |
1090 | */ | |
12c14838 | 1091 | if (role == PORT_DES && (cp == SUPERIOR || (cp == SAME && ct == false))) { |
9efd308e | 1092 | return SUPERIOR_DESIGNATED_INFO; |
9efd308e | 1093 | |
311fb15b JR |
1094 | /* Returns RepeatedDesignatedInfo if: |
1095 | * b) The received message conveys Designated Port Role, and a message | |
1096 | * priority vector and timer parameters that are the same as the | |
1097 | * Port's port priority vector or timer values. */ | |
1098 | } else if (role == PORT_DES && cp == SAME && ct == true) { | |
9efd308e | 1099 | return REPEATED_DESIGNATED_INFO; |
9efd308e | 1100 | |
311fb15b JR |
1101 | /* Returns InferiorDesignatedInfo if: |
1102 | * c) The received message conveys a Designated Port Role, and a | |
1103 | * message priority vector that is worse than the Port's port | |
1104 | * priority vector. */ | |
1105 | } else if (role == PORT_DES && cp == INFERIOR) { | |
9efd308e | 1106 | return INFERIOR_DESIGNATED_INFO; |
9efd308e | 1107 | |
311fb15b JR |
1108 | /* Returns InferiorRootAlternateInfo if: |
1109 | * d) The received message conveys a Root Port, Alternate Port, or | |
1110 | * Backup Port Role and a message priority that is the same as or | |
1111 | * worse than the port priority vector. */ | |
1112 | } else if ((role == PORT_ROOT || role == PORT_ALT_BACK) && | |
1113 | (cp == INFERIOR || cp == SAME)) { | |
9efd308e | 1114 | return INFERIOR_ROOT_ALTERNATE_INFO; |
9efd308e | 1115 | |
311fb15b JR |
1116 | /* Otherwise, returns OtherInfo. */ |
1117 | } else { | |
9efd308e DV |
1118 | return OTHER_INFO; |
1119 | } | |
1120 | } | |
1121 | ||
1122 | static int | |
1123 | better_or_same_info(struct rstp_port *p, int new_info_is) | |
f025bcb7 | 1124 | OVS_REQUIRES(rstp_mutex) |
9efd308e | 1125 | { |
311fb15b JR |
1126 | return |
1127 | (new_info_is == RECEIVED && p->info_is == INFO_IS_RECEIVED | |
a0478216 JR |
1128 | && compare_rstp_priority_vectors(&p->msg_priority, |
1129 | &p->port_priority)) | |
311fb15b | 1130 | || (new_info_is == MINE && p->info_is == INFO_IS_MINE |
a0478216 JR |
1131 | && compare_rstp_priority_vectors(&p->designated_priority_vector, |
1132 | &p->port_priority)); | |
9efd308e DV |
1133 | } |
1134 | ||
1135 | static int | |
1136 | port_information_sm(struct rstp_port *p) | |
f025bcb7 | 1137 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
1138 | { |
1139 | enum port_information_state_machine old_state; | |
1140 | struct rstp *r; | |
b5ba2490 | 1141 | struct rstp_port *p1; |
9efd308e DV |
1142 | |
1143 | old_state = p->port_information_sm_state; | |
1144 | r = p->rstp; | |
1145 | ||
9efd308e DV |
1146 | switch (p->port_information_sm_state) { |
1147 | case PORT_INFORMATION_SM_INIT: | |
6448df94 JR |
1148 | if (r->begin |
1149 | || (!p->port_enabled && p->info_is != INFO_IS_DISABLED)) { | |
9efd308e DV |
1150 | p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; |
1151 | } | |
1152 | break; | |
1153 | case PORT_INFORMATION_SM_DISABLED_EXEC: | |
1154 | p->rcvd_msg = false; | |
1155 | p->proposing = p->proposed = p->agree = p->agreed = false; | |
1156 | p->rcvd_info_while = 0; | |
1157 | p->info_is = INFO_IS_DISABLED; | |
1158 | p->reselect = true; | |
1159 | p->selected = false; | |
1160 | p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED; | |
1161 | /* no break */ | |
1162 | case PORT_INFORMATION_SM_DISABLED: | |
6448df94 JR |
1163 | if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) { |
1164 | /* Global transition. */ | |
1165 | p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; | |
1166 | } else if (p->port_enabled) { | |
9efd308e | 1167 | p->port_information_sm_state = PORT_INFORMATION_SM_AGED_EXEC; |
4b30ba05 JR |
1168 | } else if (p->rcvd_msg) { |
1169 | p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; | |
9efd308e DV |
1170 | } |
1171 | break; | |
1172 | case PORT_INFORMATION_SM_AGED_EXEC: | |
1173 | p->info_is = INFO_IS_AGED; | |
1174 | p->reselect = true; | |
1175 | p->selected = false; | |
1176 | p->port_information_sm_state = PORT_INFORMATION_SM_AGED; | |
1177 | /* no break */ | |
1178 | case PORT_INFORMATION_SM_AGED: | |
6448df94 JR |
1179 | if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) { |
1180 | /* Global transition. */ | |
1181 | p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; | |
1182 | } else if (p->selected && p->updt_info) { | |
9efd308e DV |
1183 | p->port_information_sm_state = PORT_INFORMATION_SM_UPDATE_EXEC; |
1184 | } | |
1185 | break; | |
1186 | case PORT_INFORMATION_SM_UPDATE_EXEC: | |
1187 | p->proposing = p->proposed = false; | |
1188 | /* MINE is not specified in Standard 802.1D-2004. */ | |
1189 | p->agreed = p->agreed && better_or_same_info(p, MINE); | |
1190 | p->synced = p->synced && p->agreed; | |
1191 | p->port_priority.root_bridge_id = | |
1192 | p->designated_priority_vector.root_bridge_id; | |
1193 | p->port_priority.root_path_cost = | |
1194 | p->designated_priority_vector.root_path_cost; | |
1195 | p->port_priority.designated_bridge_id = | |
1196 | p->designated_priority_vector.designated_bridge_id; | |
1197 | p->port_priority.designated_port_id = | |
1198 | p->designated_priority_vector.designated_port_id; | |
1199 | p->port_times = p->designated_times; | |
1200 | p->updt_info = false; | |
1201 | p->info_is = INFO_IS_MINE; | |
1202 | p->new_info = true; | |
1203 | p->port_information_sm_state = PORT_INFORMATION_SM_UPDATE; | |
1204 | /* no break */ | |
1205 | case PORT_INFORMATION_SM_UPDATE: | |
6448df94 JR |
1206 | if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) { |
1207 | /* Global transition. */ | |
1208 | p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; | |
1209 | } else { | |
1210 | p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC; | |
1211 | } | |
9efd308e DV |
1212 | break; |
1213 | case PORT_INFORMATION_SM_CURRENT_EXEC: | |
1214 | p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT; | |
1215 | /* no break */ | |
1216 | case PORT_INFORMATION_SM_CURRENT: | |
6448df94 JR |
1217 | if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) { |
1218 | /* Global transition. */ | |
1219 | p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; | |
1220 | } else if (p->rcvd_msg && !p->updt_info) { | |
9efd308e DV |
1221 | p->port_information_sm_state = PORT_INFORMATION_SM_RECEIVE_EXEC; |
1222 | } else if (p->selected && p->updt_info) { | |
1223 | p->port_information_sm_state = PORT_INFORMATION_SM_UPDATE_EXEC; | |
1224 | } else if ((p->info_is == INFO_IS_RECEIVED) && | |
1225 | (p->rcvd_info_while == 0) && !p->updt_info && | |
1226 | !p->rcvd_msg) { | |
1227 | p->port_information_sm_state = PORT_INFORMATION_SM_AGED_EXEC; | |
1228 | } | |
1229 | break; | |
1230 | case PORT_INFORMATION_SM_RECEIVE_EXEC: | |
1231 | p->rcvd_info = rcv_info(p); | |
1232 | p->port_information_sm_state = PORT_INFORMATION_SM_RECEIVE; | |
1233 | /* no break */ | |
1234 | case PORT_INFORMATION_SM_RECEIVE: | |
6448df94 JR |
1235 | if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) { |
1236 | /* Global transition. */ | |
1237 | p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; | |
1238 | } else { | |
1239 | switch (p->rcvd_info) { | |
1240 | case SUPERIOR_DESIGNATED_INFO: | |
b5ba2490 JR |
1241 | /* 802.1q-2008 has a checkBPDUConsistency() function, called on |
1242 | * a BPDU reception. checkBPDUConsistency() clears the agreed | |
1243 | * variable if the received message priority vector is superior | |
1244 | * to the port priority vector, the BPDU is an ST BPDU or an | |
1245 | * RST BPDU, its port role is Designated and its Learning flag | |
1246 | * is set. */ | |
1247 | if (p->received_bpdu_buffer.flags & BPDU_FLAG_LEARNING) { | |
1248 | HMAP_FOR_EACH (p1, node, &r->ports) { | |
1249 | if (p1->port_number != p->port_number) { | |
1250 | p1->agreed = false; | |
1251 | } | |
1252 | } | |
1253 | } | |
6448df94 JR |
1254 | p->port_information_sm_state = |
1255 | PORT_INFORMATION_SM_SUPERIOR_DESIGNATED_EXEC; | |
1256 | break; | |
1257 | case REPEATED_DESIGNATED_INFO: | |
1258 | p->port_information_sm_state = | |
1259 | PORT_INFORMATION_SM_REPEATED_DESIGNATED_EXEC; | |
1260 | break; | |
1261 | case INFERIOR_DESIGNATED_INFO: | |
1262 | p->port_information_sm_state = | |
1263 | PORT_INFORMATION_SM_INFERIOR_DESIGNATED_EXEC; | |
1264 | break; | |
1265 | case INFERIOR_ROOT_ALTERNATE_INFO: | |
1266 | p->port_information_sm_state = | |
1267 | PORT_INFORMATION_SM_NOT_DESIGNATED_EXEC; | |
1268 | break; | |
1269 | case OTHER_INFO: | |
1270 | p->port_information_sm_state = PORT_INFORMATION_SM_OTHER_EXEC; | |
1271 | break; | |
1272 | default: | |
1273 | OVS_NOT_REACHED(); | |
1274 | /* no break */ | |
1275 | } | |
9efd308e DV |
1276 | } |
1277 | break; | |
1278 | case PORT_INFORMATION_SM_OTHER_EXEC: | |
1279 | p->rcvd_msg = false; | |
1280 | p->port_information_sm_state = PORT_INFORMATION_SM_OTHER; | |
1281 | /* no break */ | |
1282 | case PORT_INFORMATION_SM_OTHER: | |
6448df94 JR |
1283 | if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) { |
1284 | /* Global transition. */ | |
1285 | p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; | |
1286 | } else { | |
1287 | p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC; | |
1288 | } | |
9efd308e DV |
1289 | break; |
1290 | case PORT_INFORMATION_SM_NOT_DESIGNATED_EXEC: | |
1291 | record_agreement(p); | |
1292 | set_tc_flags(p); | |
1293 | p->rcvd_msg = false; | |
1294 | p->port_information_sm_state = PORT_INFORMATION_SM_NOT_DESIGNATED; | |
1295 | /* no break */ | |
1296 | case PORT_INFORMATION_SM_NOT_DESIGNATED: | |
6448df94 JR |
1297 | if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) { |
1298 | /* Global transition. */ | |
1299 | p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; | |
1300 | } else { | |
1301 | p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC; | |
1302 | } | |
9efd308e DV |
1303 | break; |
1304 | case PORT_INFORMATION_SM_INFERIOR_DESIGNATED_EXEC: | |
1305 | record_dispute(p); | |
1306 | p->rcvd_msg = false; | |
1307 | p->port_information_sm_state = PORT_INFORMATION_SM_INFERIOR_DESIGNATED; | |
1308 | /* no break */ | |
1309 | case PORT_INFORMATION_SM_INFERIOR_DESIGNATED: | |
6448df94 JR |
1310 | if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) { |
1311 | /* Global transition. */ | |
1312 | p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; | |
1313 | } else { | |
1314 | p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC; | |
1315 | } | |
9efd308e DV |
1316 | break; |
1317 | case PORT_INFORMATION_SM_REPEATED_DESIGNATED_EXEC: | |
1318 | record_proposal(p); | |
1319 | set_tc_flags(p); | |
b5ba2490 JR |
1320 | /* This record_agreement() is missing in 802.1D-2004, but it's present |
1321 | * in 802.1q-2008. */ | |
1322 | record_agreement(p); | |
9efd308e DV |
1323 | updt_rcvd_info_while(p); |
1324 | p->rcvd_msg = false; | |
1325 | p->port_information_sm_state = PORT_INFORMATION_SM_REPEATED_DESIGNATED; | |
1326 | /* no break */ | |
1327 | case PORT_INFORMATION_SM_REPEATED_DESIGNATED: | |
6448df94 JR |
1328 | if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) { |
1329 | /* Global transition. */ | |
1330 | p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; | |
1331 | } else { | |
1332 | p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC; | |
1333 | } | |
9efd308e DV |
1334 | break; |
1335 | case PORT_INFORMATION_SM_SUPERIOR_DESIGNATED_EXEC: | |
1336 | p->agreed = p->proposing = false; | |
1337 | record_proposal(p); | |
1338 | set_tc_flags(p); | |
1339 | /* RECEIVED is not specified in Standard 802.1D-2004. */ | |
1340 | p->agree = p->agree && better_or_same_info(p, RECEIVED); | |
b5ba2490 JR |
1341 | /* This record_agreement() and the synced assignment are missing in |
1342 | * 802.1D-2004, but they're present in 802.1q-2008. */ | |
1343 | record_agreement(p); | |
1344 | p->synced = p->synced && p->agreed; | |
9efd308e DV |
1345 | record_priority(p); |
1346 | record_times(p); | |
1347 | updt_rcvd_info_while(p); | |
1348 | p->info_is = INFO_IS_RECEIVED; | |
1349 | p->reselect = true; | |
1350 | p->selected = false; | |
1351 | p->rcvd_msg = false; | |
1352 | p->port_information_sm_state = PORT_INFORMATION_SM_SUPERIOR_DESIGNATED; | |
1353 | /* no break */ | |
1354 | case PORT_INFORMATION_SM_SUPERIOR_DESIGNATED: | |
6448df94 JR |
1355 | if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) { |
1356 | /* Global transition. */ | |
1357 | p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; | |
1358 | } else { | |
1359 | p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC; | |
1360 | } | |
9efd308e DV |
1361 | break; |
1362 | default: | |
1363 | OVS_NOT_REACHED(); | |
1364 | /* no break */ | |
1365 | } | |
1366 | if (old_state != p->port_information_sm_state) { | |
1367 | r->changes = true; | |
4b30ba05 JR |
1368 | VLOG_DBG("%s, port %u: Port_information_sm %d -> %d", p->rstp->name, |
1369 | p->port_number, old_state, p->port_information_sm_state); | |
9efd308e DV |
1370 | } |
1371 | return 0; | |
1372 | } | |
1373 | ||
1374 | /* [17.29 Port Role Transitions state machine] */ | |
1375 | ||
1376 | static void | |
1377 | set_re_root_tree(struct rstp_port *p) | |
f025bcb7 | 1378 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
1379 | { |
1380 | struct rstp *r; | |
1381 | struct rstp_port *p1; | |
1382 | ||
1383 | r = p->rstp; | |
d5f31dc8 | 1384 | HMAP_FOR_EACH (p1, node, &r->ports) { |
37db915e | 1385 | p1->re_root = true; |
9efd308e DV |
1386 | } |
1387 | } | |
1388 | ||
1389 | static void | |
1390 | set_sync_tree(struct rstp_port *p) | |
f025bcb7 | 1391 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
1392 | { |
1393 | struct rstp *r; | |
1394 | struct rstp_port *p1; | |
1395 | ||
1396 | r = p->rstp; | |
d5f31dc8 | 1397 | HMAP_FOR_EACH (p1, node, &r->ports) { |
37db915e | 1398 | p1->sync = true; |
9efd308e DV |
1399 | } |
1400 | } | |
1401 | ||
1402 | static int | |
1403 | hello_time(struct rstp_port *p) | |
f025bcb7 | 1404 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
1405 | { |
1406 | return p->designated_times.hello_time; | |
1407 | } | |
1408 | ||
1409 | static int | |
1410 | fwd_delay(struct rstp_port *p) | |
f025bcb7 | 1411 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
1412 | { |
1413 | return p->designated_times.forward_delay; | |
1414 | } | |
1415 | ||
1416 | static int | |
1417 | forward_delay(struct rstp_port *p) | |
f025bcb7 | 1418 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
1419 | { |
1420 | if (p->send_rstp) { | |
1421 | return hello_time(p); | |
1422 | } else { | |
1423 | return fwd_delay(p); | |
1424 | } | |
1425 | } | |
1426 | ||
1427 | static int | |
1428 | edge_delay(struct rstp_port *p) | |
f025bcb7 | 1429 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
1430 | { |
1431 | struct rstp *r; | |
1432 | ||
1433 | r = p->rstp; | |
1434 | if (p->oper_point_to_point_mac == 1) { | |
1435 | return r->migrate_time; | |
1436 | } else { | |
1437 | return p->designated_times.max_age; | |
1438 | } | |
1439 | } | |
1440 | ||
1441 | static int | |
1442 | check_selected_role_change(struct rstp_port *p, int current_role_state) | |
f025bcb7 | 1443 | OVS_REQUIRES(rstp_mutex) |
9efd308e | 1444 | { |
311fb15b JR |
1445 | if (p->selected && !p->updt_info && p->role != p->selected_role |
1446 | && p->selected_role != current_role_state) { | |
9efd308e DV |
1447 | VLOG_DBG("%s, port %u: case: current = %s role = %s selected = %d", |
1448 | p->rstp->name, p->port_number, | |
1449 | rstp_port_role_name(current_role_state), | |
1450 | rstp_port_role_name(p->role), p->selected_role); | |
1451 | switch (p->selected_role) { | |
1452 | case ROLE_ROOT: | |
1453 | p->port_role_transition_sm_state = | |
1454 | PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC; | |
1455 | return true; | |
1456 | case ROLE_DESIGNATED: | |
1457 | p->port_role_transition_sm_state = | |
1458 | PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC; | |
1459 | return true; | |
1460 | case ROLE_ALTERNATE: | |
1461 | p->port_role_transition_sm_state = | |
1462 | PORT_ROLE_TRANSITION_SM_BLOCK_PORT_EXEC; | |
1463 | return true; | |
1464 | case ROLE_BACKUP: | |
1465 | p->port_role_transition_sm_state = | |
1466 | PORT_ROLE_TRANSITION_SM_BLOCK_PORT_EXEC; | |
1467 | return true; | |
1468 | case ROLE_DISABLED: | |
1469 | p->port_role_transition_sm_state = | |
1470 | PORT_ROLE_TRANSITION_SM_DISABLE_PORT_EXEC; | |
1471 | return true; | |
1472 | } | |
1473 | } | |
1474 | return false; | |
1475 | } | |
1476 | ||
1477 | static int | |
1478 | re_rooted(struct rstp_port *p) | |
f025bcb7 | 1479 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
1480 | { |
1481 | struct rstp *r; | |
1482 | struct rstp_port *p1; | |
1483 | ||
1484 | r = p->rstp; | |
d5f31dc8 | 1485 | HMAP_FOR_EACH (p1, node, &r->ports) { |
37db915e JR |
1486 | if ((p1 != p) && (p1->rr_while != 0)) { |
1487 | return false; | |
9efd308e DV |
1488 | } |
1489 | } | |
1490 | return true; | |
1491 | } | |
1492 | ||
1493 | static int | |
1494 | all_synced(struct rstp *r) | |
f025bcb7 | 1495 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
1496 | { |
1497 | struct rstp_port *p; | |
1498 | ||
d5f31dc8 | 1499 | HMAP_FOR_EACH (p, node, &r->ports) { |
37db915e JR |
1500 | if (!(p->selected && p->role == p->selected_role && |
1501 | (p->role == ROLE_ROOT || p->synced == true))) { | |
1502 | return false; | |
9efd308e DV |
1503 | } |
1504 | } | |
1505 | return true; | |
1506 | } | |
1507 | ||
1508 | static int | |
1509 | port_role_transition_sm(struct rstp_port *p) | |
f025bcb7 | 1510 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
1511 | { |
1512 | enum port_role_transition_state_machine old_state; | |
1513 | struct rstp *r; | |
1514 | enum rstp_port_role last_role; | |
1515 | ||
1516 | old_state = p->port_role_transition_sm_state; | |
1517 | r = p->rstp; | |
1518 | last_role = p->role; | |
1519 | ||
1520 | switch (p->port_role_transition_sm_state) { | |
1521 | case PORT_ROLE_TRANSITION_SM_INIT: | |
1522 | if (r->begin) { | |
1523 | p->port_role_transition_sm_state = | |
1524 | PORT_ROLE_TRANSITION_SM_INIT_PORT_EXEC; | |
1525 | } | |
1526 | break; | |
1527 | case PORT_ROLE_TRANSITION_SM_INIT_PORT_EXEC: | |
1528 | p->role = ROLE_DISABLED; | |
1529 | p->learn = p->forward = false; | |
1530 | p->synced = false; | |
1531 | p->sync = p->re_root = true; | |
1532 | p->rr_while = p->designated_times.forward_delay; | |
1533 | p->fd_while = p->designated_times.max_age; | |
1534 | p->rb_while = 0; | |
1535 | p->port_role_transition_sm_state = | |
1536 | PORT_ROLE_TRANSITION_SM_DISABLE_PORT_EXEC; | |
1537 | break; | |
1538 | case PORT_ROLE_TRANSITION_SM_DISABLE_PORT_EXEC: | |
1539 | p->role = p->selected_role; | |
1540 | p->learn = p->forward = false; | |
1541 | p->port_role_transition_sm_state = | |
1542 | PORT_ROLE_TRANSITION_SM_DISABLE_PORT; | |
1543 | /* no break */ | |
1544 | case PORT_ROLE_TRANSITION_SM_DISABLE_PORT: | |
1545 | if (check_selected_role_change(p, ROLE_DISABLED)) { | |
6448df94 | 1546 | /* Global transition. */ |
311fb15b JR |
1547 | } else if (p->selected && !p->updt_info && !p->learning |
1548 | && !p->forwarding) { | |
9efd308e DV |
1549 | p->port_role_transition_sm_state = |
1550 | PORT_ROLE_TRANSITION_SM_DISABLED_PORT_EXEC; | |
1551 | } | |
1552 | break; | |
1553 | case PORT_ROLE_TRANSITION_SM_DISABLED_PORT_EXEC: | |
1554 | p->fd_while = p->designated_times.max_age; | |
1555 | p->synced = true; | |
1556 | p->rr_while = 0; | |
1557 | p->sync = p->re_root = false; | |
1558 | p->port_role_transition_sm_state = | |
1559 | PORT_ROLE_TRANSITION_SM_DISABLED_PORT; | |
1560 | /* no break */ | |
1561 | case PORT_ROLE_TRANSITION_SM_DISABLED_PORT: | |
1562 | if (check_selected_role_change(p, ROLE_DISABLED)) { | |
6448df94 | 1563 | /* Global transition. */ |
311fb15b JR |
1564 | } else if (p->selected && !p->updt_info |
1565 | && (p->fd_while != p->designated_times.max_age || p->sync | |
1566 | || p->re_root || !p->synced)) { | |
9efd308e DV |
1567 | p->port_role_transition_sm_state = |
1568 | PORT_ROLE_TRANSITION_SM_DISABLED_PORT_EXEC; | |
1569 | } | |
1570 | break; | |
1571 | case PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC: | |
1572 | p->role = ROLE_ROOT; | |
1573 | p->rr_while = p->designated_times.forward_delay; | |
1574 | p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT; | |
1575 | /* no break */ | |
1576 | case PORT_ROLE_TRANSITION_SM_ROOT_PORT: | |
1577 | if (check_selected_role_change(p, ROLE_ROOT)) { | |
6448df94 | 1578 | /* Global transition. */ |
311fb15b | 1579 | } else if (p->selected && !p->updt_info) { |
9efd308e DV |
1580 | if (p->rr_while != p->designated_times.forward_delay) { |
1581 | p->port_role_transition_sm_state = | |
1582 | PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC; | |
1583 | break; | |
311fb15b | 1584 | } else if (p->re_root && p->forward) { |
9efd308e DV |
1585 | p->port_role_transition_sm_state = |
1586 | PORT_ROLE_TRANSITION_SM_REROOTED_EXEC; | |
1587 | break; | |
311fb15b JR |
1588 | } else if ((p->fd_while == 0 |
1589 | || ((re_rooted(p) && p->rb_while == 0) | |
1590 | && r->rstp_version)) && !p->learn) { | |
9efd308e DV |
1591 | p->port_role_transition_sm_state = |
1592 | PORT_ROLE_TRANSITION_SM_ROOT_LEARN_EXEC; | |
1593 | break; | |
311fb15b JR |
1594 | } else if ((p->fd_while == 0 |
1595 | || ((re_rooted(p) && p->rb_while == 0) | |
1596 | && r->rstp_version)) && p->learn && !p->forward) { | |
9efd308e DV |
1597 | p->port_role_transition_sm_state = |
1598 | PORT_ROLE_TRANSITION_SM_ROOT_FORWARD_EXEC; | |
1599 | break; | |
311fb15b | 1600 | } else if (p->proposed && !p->agree) { |
9efd308e DV |
1601 | p->port_role_transition_sm_state = |
1602 | PORT_ROLE_TRANSITION_SM_ROOT_PROPOSED_EXEC; | |
1603 | break; | |
311fb15b JR |
1604 | } else if ((all_synced(r) && !p->agree) || |
1605 | (p->proposed && p->agree)) { | |
9efd308e DV |
1606 | p->port_role_transition_sm_state = |
1607 | PORT_ROLE_TRANSITION_SM_ROOT_AGREED_EXEC; | |
1608 | break; | |
311fb15b | 1609 | } else if (!p->forward && !p->re_root) { |
9efd308e DV |
1610 | p->port_role_transition_sm_state = |
1611 | PORT_ROLE_TRANSITION_SM_REROOT_EXEC; | |
1612 | break; | |
1613 | } | |
1614 | } | |
6448df94 | 1615 | break; |
9efd308e | 1616 | case PORT_ROLE_TRANSITION_SM_REROOT_EXEC: |
6448df94 JR |
1617 | if (check_selected_role_change(p, ROLE_ROOT)) { |
1618 | /* Global transition. */ | |
1619 | } else { | |
1620 | set_re_root_tree(p); | |
1621 | p->port_role_transition_sm_state = | |
1622 | PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC; | |
1623 | } | |
9efd308e DV |
1624 | break; |
1625 | case PORT_ROLE_TRANSITION_SM_ROOT_AGREED_EXEC: | |
6448df94 JR |
1626 | if (check_selected_role_change(p, ROLE_ROOT)) { |
1627 | /* Global transition. */ | |
1628 | } else { | |
1629 | p->proposed = p->sync = false; | |
1630 | p->agree = p->new_info = true; | |
1631 | p->port_role_transition_sm_state = | |
1632 | PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC; | |
1633 | } | |
9efd308e DV |
1634 | break; |
1635 | case PORT_ROLE_TRANSITION_SM_ROOT_PROPOSED_EXEC: | |
1636 | set_sync_tree(p); | |
1637 | p->proposed = false; | |
6448df94 JR |
1638 | if (check_selected_role_change(p, ROLE_ROOT)) { |
1639 | /* Global transition. */ | |
1640 | } else { | |
1641 | p->port_role_transition_sm_state = | |
1642 | PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC; | |
1643 | } | |
9efd308e DV |
1644 | break; |
1645 | case PORT_ROLE_TRANSITION_SM_ROOT_FORWARD_EXEC: | |
1646 | p->fd_while = 0; | |
1647 | p->forward = true; | |
6448df94 JR |
1648 | if (check_selected_role_change(p, ROLE_ROOT)) { |
1649 | /* Global transition. */ | |
1650 | } else { | |
1651 | p->port_role_transition_sm_state = | |
1652 | PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC; | |
1653 | } | |
9efd308e DV |
1654 | break; |
1655 | case PORT_ROLE_TRANSITION_SM_ROOT_LEARN_EXEC: | |
1656 | p->fd_while = forward_delay(p); | |
1657 | p->learn = true; | |
6448df94 JR |
1658 | if (check_selected_role_change(p, ROLE_ROOT)) { |
1659 | /* Global transition. */ | |
1660 | } else { | |
1661 | p->port_role_transition_sm_state = | |
1662 | PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC; | |
1663 | } | |
9efd308e DV |
1664 | break; |
1665 | case PORT_ROLE_TRANSITION_SM_REROOTED_EXEC: | |
1666 | p->re_root = false; | |
6448df94 JR |
1667 | if (check_selected_role_change(p, ROLE_ROOT)) { |
1668 | /* Global transition. */ | |
1669 | } else { | |
1670 | p->port_role_transition_sm_state = | |
1671 | PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC; | |
1672 | } | |
9efd308e DV |
1673 | break; |
1674 | case PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC: | |
1675 | p->role = ROLE_DESIGNATED; | |
1676 | p->port_role_transition_sm_state = | |
1677 | PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT; | |
1678 | /* no break */ | |
1679 | case PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT: | |
1680 | if (check_selected_role_change(p, ROLE_DESIGNATED)) { | |
6448df94 | 1681 | /* Global transition. */ |
311fb15b JR |
1682 | } else if (p->selected && !p->updt_info) { |
1683 | if (((p->sync && !p->synced) | |
1684 | || (p->re_root && p->rr_while != 0) || p->disputed) | |
1685 | && !p->oper_edge && (p->learn || p->forward)) { | |
1686 | p->port_role_transition_sm_state = | |
1687 | PORT_ROLE_TRANSITION_SM_DESIGNATED_DISCARD_EXEC; | |
1688 | } else if ((p->fd_while == 0 || p->agreed || p->oper_edge) | |
1689 | && (p->rr_while == 0 || !p->re_root) | |
1690 | && !p->sync && !p->learn) { | |
9efd308e DV |
1691 | p->port_role_transition_sm_state = |
1692 | PORT_ROLE_TRANSITION_SM_DESIGNATED_LEARN_EXEC; | |
311fb15b JR |
1693 | } else if ((p->fd_while == 0 || p->agreed || p->oper_edge) |
1694 | && (p->rr_while == 0 || !p->re_root) | |
1695 | && !p->sync && (p->learn && !p->forward)) { | |
9efd308e DV |
1696 | p->port_role_transition_sm_state = |
1697 | PORT_ROLE_TRANSITION_SM_DESIGNATED_FORWARD_EXEC; | |
311fb15b JR |
1698 | } else if (!p->forward && !p->agreed && !p->proposing && |
1699 | !p->oper_edge) { | |
9efd308e DV |
1700 | p->port_role_transition_sm_state = |
1701 | PORT_ROLE_TRANSITION_SM_DESIGNATED_PROPOSE_EXEC; | |
311fb15b JR |
1702 | } else if ((!p->learning && !p->forwarding && !p->synced) |
1703 | || (p->agreed && !p->synced) | |
1704 | || (p->oper_edge && !p->synced) | |
1705 | || (p->sync && p->synced)) { | |
9efd308e DV |
1706 | p->port_role_transition_sm_state = |
1707 | PORT_ROLE_TRANSITION_SM_DESIGNATED_SYNCED_EXEC; | |
311fb15b | 1708 | } else if (p->rr_while == 0 && p->re_root) { |
9efd308e DV |
1709 | p->port_role_transition_sm_state = |
1710 | PORT_ROLE_TRANSITION_SM_DESIGNATED_RETIRED_EXEC; | |
1711 | } | |
1712 | } | |
1713 | break; | |
1714 | case PORT_ROLE_TRANSITION_SM_DESIGNATED_RETIRED_EXEC: | |
1715 | p->re_root = false; | |
6448df94 JR |
1716 | if (check_selected_role_change(p, ROLE_DESIGNATED)) { |
1717 | /* Global transition. */ | |
1718 | } else { | |
1719 | p->port_role_transition_sm_state = | |
1720 | PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC; | |
1721 | } | |
9efd308e DV |
1722 | break; |
1723 | case PORT_ROLE_TRANSITION_SM_DESIGNATED_SYNCED_EXEC: | |
1724 | p->rr_while = 0; | |
1725 | p->synced = true; | |
1726 | p->sync = false; | |
6448df94 JR |
1727 | if (check_selected_role_change(p, ROLE_DESIGNATED)) { |
1728 | /* Global transition. */ | |
1729 | } else { | |
1730 | p->port_role_transition_sm_state = | |
1731 | PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC; | |
1732 | } | |
9efd308e DV |
1733 | break; |
1734 | case PORT_ROLE_TRANSITION_SM_DESIGNATED_PROPOSE_EXEC: | |
1735 | p->proposing = true; | |
1736 | p->edge_delay_while = edge_delay(p); | |
1737 | p->new_info = true; | |
6448df94 JR |
1738 | if (check_selected_role_change(p, ROLE_DESIGNATED)) { |
1739 | /* Global transition. */ | |
1740 | } else { | |
1741 | p->port_role_transition_sm_state = | |
1742 | PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC; | |
1743 | } | |
9efd308e DV |
1744 | break; |
1745 | case PORT_ROLE_TRANSITION_SM_DESIGNATED_FORWARD_EXEC: | |
1746 | p->forward = true; | |
1747 | p->fd_while = 0; | |
1748 | p->agreed = p->send_rstp; | |
6448df94 JR |
1749 | if (check_selected_role_change(p, ROLE_DESIGNATED)) { |
1750 | /* Global transition. */ | |
1751 | } else { | |
1752 | p->port_role_transition_sm_state = | |
1753 | PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC; | |
1754 | } | |
9efd308e DV |
1755 | break; |
1756 | case PORT_ROLE_TRANSITION_SM_DESIGNATED_LEARN_EXEC: | |
1757 | p->learn = true; | |
1758 | p->fd_while = forward_delay(p); | |
6448df94 JR |
1759 | if (check_selected_role_change(p, ROLE_DESIGNATED)) { |
1760 | /* Global transition. */ | |
1761 | } else { | |
1762 | p->port_role_transition_sm_state = | |
1763 | PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC; | |
1764 | } | |
9efd308e DV |
1765 | break; |
1766 | case PORT_ROLE_TRANSITION_SM_DESIGNATED_DISCARD_EXEC: | |
1767 | p->learn = p->forward = p->disputed = false; | |
1768 | p->fd_while = forward_delay(p); | |
6448df94 JR |
1769 | if (check_selected_role_change(p, ROLE_DESIGNATED)) { |
1770 | /* Global transition. */ | |
1771 | } else { | |
1772 | p->port_role_transition_sm_state = | |
1773 | PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC; | |
1774 | } | |
9efd308e DV |
1775 | break; |
1776 | case PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC: | |
1777 | p->fd_while = p->designated_times.forward_delay; | |
1778 | p->synced = true; | |
1779 | p->rr_while = 0; | |
1780 | p->sync = p->re_root = false; | |
1781 | p->port_role_transition_sm_state = | |
1782 | PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT; | |
1783 | /* no break */ | |
1784 | case PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT: | |
1785 | if (check_selected_role_change(p, ROLE_ALTERNATE)) { | |
6448df94 | 1786 | /* Global transition. */ |
311fb15b JR |
1787 | } else if (p->selected && !p->updt_info) { |
1788 | if (p->rb_while != 2 * p->designated_times.hello_time | |
1789 | && p->role == ROLE_BACKUP) { | |
9efd308e DV |
1790 | p->port_role_transition_sm_state = |
1791 | PORT_ROLE_TRANSITION_SM_BACKUP_PORT_EXEC; | |
311fb15b JR |
1792 | } else if ((p->fd_while != forward_delay(p)) || p->sync |
1793 | || p->re_root || !p->synced) { | |
9efd308e DV |
1794 | p->port_role_transition_sm_state = |
1795 | PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC; | |
311fb15b | 1796 | } else if (p->proposed && !p->agree) { |
9efd308e DV |
1797 | p->port_role_transition_sm_state = |
1798 | PORT_ROLE_TRANSITION_SM_ALTERNATE_PROPOSED_EXEC; | |
311fb15b JR |
1799 | } else if ((all_synced(r) && !p->agree) |
1800 | || (p->proposed && p->agree)) { | |
9efd308e | 1801 | p->port_role_transition_sm_state = |
7ccf8d38 | 1802 | PORT_ROLE_TRANSITION_SM_ALTERNATE_AGREED_EXEC; |
9efd308e DV |
1803 | } |
1804 | } | |
1805 | break; | |
7ccf8d38 | 1806 | case PORT_ROLE_TRANSITION_SM_ALTERNATE_AGREED_EXEC: |
9efd308e DV |
1807 | p->proposed = false; |
1808 | p->agree = true; | |
1809 | p->new_info = true; | |
6448df94 JR |
1810 | if (check_selected_role_change(p, ROLE_ALTERNATE)) { |
1811 | /* Global transition. */ | |
1812 | } else { | |
1813 | p->port_role_transition_sm_state = | |
1814 | PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC; | |
1815 | } | |
9efd308e DV |
1816 | break; |
1817 | case PORT_ROLE_TRANSITION_SM_ALTERNATE_PROPOSED_EXEC: | |
1818 | set_sync_tree(p); | |
1819 | p->proposed = false; | |
6448df94 JR |
1820 | if (check_selected_role_change(p, ROLE_ALTERNATE)) { |
1821 | /* Global transition. */ | |
1822 | } else { | |
1823 | p->port_role_transition_sm_state = | |
1824 | PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC; | |
1825 | } | |
9efd308e DV |
1826 | break; |
1827 | case PORT_ROLE_TRANSITION_SM_BLOCK_PORT_EXEC: | |
1828 | p->role = p->selected_role; | |
1829 | p->learn = p->forward = false; | |
1830 | p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_BLOCK_PORT; | |
1831 | /* no break */ | |
1832 | case PORT_ROLE_TRANSITION_SM_BLOCK_PORT: | |
1833 | if (check_selected_role_change(p, ROLE_ALTERNATE)) { | |
6448df94 | 1834 | /* Global transition. */ |
311fb15b JR |
1835 | } else if (p->selected && !p->updt_info && !p->learning && |
1836 | !p->forwarding) { | |
9efd308e DV |
1837 | p->port_role_transition_sm_state = |
1838 | PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC; | |
1839 | } | |
1840 | break; | |
1841 | case PORT_ROLE_TRANSITION_SM_BACKUP_PORT_EXEC: | |
1842 | p->rb_while = 2 * p->designated_times.hello_time; | |
6448df94 JR |
1843 | if (check_selected_role_change(p, ROLE_ALTERNATE)) { |
1844 | /* Global transition. */ | |
1845 | } else { | |
1846 | p->port_role_transition_sm_state = | |
1847 | PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC; | |
1848 | } | |
9efd308e DV |
1849 | break; |
1850 | default: | |
1851 | OVS_NOT_REACHED(); | |
1852 | /* no break */ | |
1853 | } | |
1854 | if (old_state != p->port_role_transition_sm_state) { | |
1855 | r->changes = true; | |
1856 | VLOG_DBG("%s, port %u: Port_role_transition_sm %d -> %d", | |
1857 | p->rstp->name, p->port_number, old_state, | |
1858 | p->port_role_transition_sm_state); | |
1859 | } | |
1860 | if (last_role != p->role) { | |
9efd308e DV |
1861 | VLOG_DBG("%s, port %u, port role ["RSTP_PORT_ID_FMT"] = %s", |
1862 | p->rstp->name, p->port_number, p->port_id, | |
1863 | rstp_port_role_name(p->role)); | |
1864 | } | |
1865 | return 0; | |
1866 | } | |
1867 | ||
1868 | /* [17.30 - Port state transition state machine] */ | |
1869 | ||
1870 | static void | |
1871 | enable_learning(struct rstp_port *p) | |
f025bcb7 | 1872 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
1873 | { |
1874 | /* [17.21.6 enableLearning()] An implementation dependent procedure that | |
1875 | * causes the Learning Process (7.8) to start learning from frames received | |
1876 | * on the Port. The procedure does not complete until learning has been | |
1877 | * enabled. | |
1878 | */ | |
f025bcb7 | 1879 | rstp_port_set_state__(p, RSTP_LEARNING); |
9efd308e DV |
1880 | } |
1881 | ||
1882 | static void | |
1883 | enable_forwarding(struct rstp_port *p) | |
f025bcb7 | 1884 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
1885 | { |
1886 | /* [17.21.5 enableForwarding()] An implementation dependent procedure that | |
1887 | * causes the Forwarding Process (7.7) to start forwarding frames through | |
1888 | * the Port. The procedure does not complete until forwarding has been | |
1889 | * enabled. | |
1890 | */ | |
f025bcb7 | 1891 | rstp_port_set_state__(p, RSTP_FORWARDING); |
9efd308e DV |
1892 | } |
1893 | ||
1894 | static void | |
1895 | disable_learning(struct rstp_port *p) | |
f025bcb7 | 1896 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
1897 | { |
1898 | /* [17.21.4 - disableLearning()] An implementation dependent procedure that | |
1899 | * causes the Learning Process (7.8) to stop learning from the source | |
1900 | * address of frames received on the Port. The procedure does not complete | |
1901 | * until learning has stopped. | |
1902 | */ | |
f025bcb7 | 1903 | rstp_port_set_state__(p, RSTP_DISCARDING); |
9efd308e DV |
1904 | } |
1905 | ||
1906 | static void | |
1907 | disable_forwarding(struct rstp_port *p) | |
f025bcb7 | 1908 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
1909 | { |
1910 | /* [17.21.3 - disableForwarding()] An implementation dependent procedure | |
1911 | * that causes the Forwarding Process (7.7) to stop forwarding frames | |
1912 | * through the Port. The procedure does not complete until forwarding has | |
1913 | * stopped. | |
1914 | */ | |
f025bcb7 | 1915 | rstp_port_set_state__(p, RSTP_DISCARDING); |
9efd308e DV |
1916 | } |
1917 | ||
1918 | static int | |
1919 | port_state_transition_sm(struct rstp_port *p) | |
f025bcb7 | 1920 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
1921 | { |
1922 | enum port_state_transition_state_machine old_state; | |
1923 | struct rstp *r; | |
1924 | ||
1925 | old_state = p->port_state_transition_sm_state; | |
1926 | r = p->rstp; | |
1927 | ||
1928 | switch (p->port_state_transition_sm_state) { | |
1929 | case PORT_STATE_TRANSITION_SM_INIT: | |
1930 | if (r->begin) { | |
1931 | p->port_state_transition_sm_state = | |
1932 | PORT_STATE_TRANSITION_SM_DISCARDING_EXEC; | |
1933 | } | |
1934 | break; | |
1935 | case PORT_STATE_TRANSITION_SM_DISCARDING_EXEC: | |
1936 | disable_learning(p); | |
1937 | p->learning = false; | |
1938 | disable_forwarding(p); | |
1939 | p->forwarding = false; | |
1940 | p->port_state_transition_sm_state = | |
1941 | PORT_STATE_TRANSITION_SM_DISCARDING; | |
1942 | /* no break */ | |
1943 | case PORT_STATE_TRANSITION_SM_DISCARDING: | |
1944 | if (p->learn) { | |
1945 | p->port_state_transition_sm_state = | |
1946 | PORT_STATE_TRANSITION_SM_LEARNING_EXEC; | |
1947 | } | |
1948 | break; | |
1949 | case PORT_STATE_TRANSITION_SM_LEARNING_EXEC: | |
1950 | enable_learning(p); | |
1951 | p->learning = true; | |
1952 | p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_LEARNING; | |
1953 | /* no break */ | |
1954 | case PORT_STATE_TRANSITION_SM_LEARNING: | |
1955 | if (!p->learn) { | |
1956 | p->port_state_transition_sm_state = | |
1957 | PORT_STATE_TRANSITION_SM_DISCARDING_EXEC; | |
311fb15b | 1958 | } else if (p->forward) { |
9efd308e DV |
1959 | p->port_state_transition_sm_state = |
1960 | PORT_STATE_TRANSITION_SM_FORWARDING_EXEC; | |
1961 | } | |
1962 | break; | |
1963 | case PORT_STATE_TRANSITION_SM_FORWARDING_EXEC: | |
1964 | enable_forwarding(p); | |
1965 | p->forwarding = true; | |
1966 | p->port_state_transition_sm_state = | |
1967 | PORT_STATE_TRANSITION_SM_FORWARDING; | |
1968 | /* no break */ | |
1969 | case PORT_STATE_TRANSITION_SM_FORWARDING: | |
1970 | if (!p->forward) { | |
1971 | p->port_state_transition_sm_state = | |
1972 | PORT_STATE_TRANSITION_SM_DISCARDING_EXEC; | |
1973 | } | |
1974 | break; | |
1975 | default: | |
1976 | OVS_NOT_REACHED(); | |
1977 | /* no break */ | |
1978 | } | |
1979 | if (old_state != p->port_state_transition_sm_state) { | |
1980 | r->changes = true; | |
1981 | VLOG_DBG("%s, port %u: Port_state_transition_sm %d -> %d", | |
1982 | p->rstp->name, p->port_number, old_state, | |
1983 | p->port_state_transition_sm_state); | |
1984 | } | |
1985 | return 0; | |
1986 | } | |
1987 | ||
1988 | /* [17.31 - Topology Change state machine] */ | |
1989 | ||
1990 | static void | |
1991 | new_tc_while(struct rstp_port *p) | |
f025bcb7 | 1992 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
1993 | { |
1994 | struct rstp *r; | |
1995 | ||
1996 | r = p->rstp; | |
1997 | if (p->tc_while == 0 && p->send_rstp == true) { | |
1998 | p->tc_while = r->bridge_hello_time + 1; | |
1999 | p->new_info = true; | |
311fb15b | 2000 | } else if (p->tc_while == 0 && p->send_rstp == false) { |
9efd308e DV |
2001 | p->tc_while = r->bridge_max_age + r->bridge_forward_delay; |
2002 | } | |
2003 | } | |
2004 | ||
2005 | /* [17.21.18 setTcPropTree()] | |
2006 | * Sets tcprop for all Ports except the Port that called the procedure. | |
2007 | */ | |
2008 | static void | |
2009 | set_tc_prop_tree(struct rstp_port *p) | |
f025bcb7 | 2010 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
2011 | { |
2012 | struct rstp *r; | |
2013 | struct rstp_port *p1; | |
2014 | ||
2015 | r = p->rstp; | |
d5f31dc8 | 2016 | HMAP_FOR_EACH (p1, node, &r->ports) { |
37db915e JR |
2017 | /* Set tc_prop on every port, except the one calling this |
2018 | * function. */ | |
2019 | if (p1->port_number != p->port_number) { | |
2020 | p1->tc_prop = true; | |
9efd308e DV |
2021 | } |
2022 | } | |
2023 | } | |
2024 | ||
2025 | static void | |
2026 | set_tc_prop_bridge(struct rstp_port *p) /* not specified in 802.1D-2004. */ | |
f025bcb7 | 2027 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
2028 | { |
2029 | set_tc_prop_tree(p); /* see 802.1w-2001. */ | |
2030 | } | |
2031 | ||
2032 | static int | |
2033 | topology_change_sm(struct rstp_port *p) | |
f025bcb7 | 2034 | OVS_REQUIRES(rstp_mutex) |
9efd308e DV |
2035 | { |
2036 | enum topology_change_state_machine old_state; | |
2037 | struct rstp *r; | |
2038 | ||
2039 | old_state = p->topology_change_sm_state; | |
2040 | r = p->rstp; | |
2041 | ||
2042 | switch (p->topology_change_sm_state) { | |
2043 | case TOPOLOGY_CHANGE_SM_INIT: | |
2044 | if (r->begin) { | |
2045 | p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_INACTIVE_EXEC; | |
2046 | } | |
2047 | break; | |
2048 | case TOPOLOGY_CHANGE_SM_INACTIVE_EXEC: | |
2049 | p->fdb_flush = true; | |
2050 | p->tc_while = 0; | |
2051 | p->tc_ack = false; | |
2052 | p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_INACTIVE; | |
2053 | /* no break */ | |
2054 | case TOPOLOGY_CHANGE_SM_INACTIVE: | |
2055 | if (p->learn && !p->fdb_flush) { | |
2056 | p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING_EXEC; | |
2057 | } | |
2058 | break; | |
2059 | case TOPOLOGY_CHANGE_SM_LEARNING_EXEC: | |
2060 | p->rcvd_tc = p->rcvd_tcn = p->rcvd_tc_ack = false; | |
2061 | p->tc_prop = p->rcvd_tc_ack = false; | |
2062 | p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING; | |
2063 | /* no break */ | |
2064 | case TOPOLOGY_CHANGE_SM_LEARNING: | |
311fb15b | 2065 | if (p->role != ROLE_ROOT && p->role != ROLE_DESIGNATED && |
9efd308e | 2066 | !(p->learn || p->learning) && !(p->rcvd_tc || p->rcvd_tcn || |
311fb15b | 2067 | p->rcvd_tc_ack || p->tc_prop)) { |
9efd308e | 2068 | p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_INACTIVE_EXEC; |
311fb15b | 2069 | } else if (p->rcvd_tc || p->rcvd_tcn || p->rcvd_tc_ack || p->tc_prop) { |
9efd308e | 2070 | p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING_EXEC; |
311fb15b JR |
2071 | } else if ((p->role == ROLE_ROOT || p->role == ROLE_DESIGNATED) |
2072 | && p->forward && !p->oper_edge) { | |
9efd308e DV |
2073 | p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_DETECTED_EXEC; |
2074 | } | |
2075 | break; | |
2076 | case TOPOLOGY_CHANGE_SM_DETECTED_EXEC: | |
2077 | new_tc_while(p); | |
2078 | set_tc_prop_tree(p); | |
2079 | p->new_info = true; | |
2080 | p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE_EXEC; | |
2081 | /* no break */ | |
2082 | case TOPOLOGY_CHANGE_SM_ACTIVE_EXEC: | |
2083 | p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE; | |
2084 | /* no break */ | |
2085 | case TOPOLOGY_CHANGE_SM_ACTIVE: | |
311fb15b JR |
2086 | if ((p->role != ROLE_ROOT && p->role != ROLE_DESIGNATED) |
2087 | || p->oper_edge) { | |
9efd308e | 2088 | p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING_EXEC; |
311fb15b | 2089 | } else if (p->rcvd_tcn) { |
9efd308e | 2090 | p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_NOTIFIED_TCN_EXEC; |
311fb15b | 2091 | } else if (p->rcvd_tc) { |
9efd308e | 2092 | p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_NOTIFIED_TC_EXEC; |
311fb15b | 2093 | } else if (p->tc_prop && !p->oper_edge) { |
9efd308e | 2094 | p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_PROPAGATING_EXEC; |
311fb15b | 2095 | } else if (p->rcvd_tc_ack) { |
9efd308e DV |
2096 | p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACKNOWLEDGED_EXEC; |
2097 | } | |
2098 | break; | |
2099 | case TOPOLOGY_CHANGE_SM_ACKNOWLEDGED_EXEC: | |
2100 | p->tc_while = 0; | |
2101 | p->rcvd_tc_ack = false; | |
2102 | p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE; | |
2103 | break; | |
2104 | case TOPOLOGY_CHANGE_SM_PROPAGATING_EXEC: | |
2105 | new_tc_while(p); | |
2106 | p->fdb_flush = true; | |
2107 | p->tc_prop = false; | |
2108 | p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE; | |
2109 | break; | |
2110 | case TOPOLOGY_CHANGE_SM_NOTIFIED_TC_EXEC: | |
2111 | p->rcvd_tcn = p->rcvd_tc = false; | |
2112 | if (p->role == ROLE_DESIGNATED) { | |
2113 | p->tc_ack = true; | |
2114 | } | |
2115 | set_tc_prop_bridge(p); | |
2116 | p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE; | |
2117 | break; | |
2118 | case TOPOLOGY_CHANGE_SM_NOTIFIED_TCN_EXEC: | |
2119 | new_tc_while(p); | |
75735c80 | 2120 | p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_NOTIFIED_TC_EXEC; |
9efd308e DV |
2121 | break; |
2122 | default: | |
2123 | OVS_NOT_REACHED(); | |
2124 | /* no break */ | |
2125 | } | |
2126 | if (old_state != p->topology_change_sm_state) { | |
2127 | r->changes = true; | |
4b30ba05 JR |
2128 | VLOG_DBG("%s, port %u: Topology_change_sm %d -> %d", p->rstp->name, |
2129 | p->port_number, old_state, p->topology_change_sm_state); | |
9efd308e DV |
2130 | } |
2131 | return 0; | |
2132 | } | |
2133 | ||
2134 | /**************************************************************************** | |
2135 | * [17.6] Priority vector calculation helper functions | |
2136 | ****************************************************************************/ | |
2137 | ||
a0478216 JR |
2138 | /* compare_rstp_priority_vectors() compares two struct rstp_priority_vectors |
2139 | * and returns a value indicating if the first rstp_priority_vector is | |
2140 | * superior, same or inferior to the second one. | |
2141 | * | |
2142 | * Zero return value indicates INFERIOR, a non-zero return value indicates | |
2143 | * SUPERIOR. When it makes a difference the non-zero return value SAME | |
2144 | * indicates the priority vectors are identical (a subset of SUPERIOR). | |
9efd308e DV |
2145 | */ |
2146 | static enum vector_comparison | |
a0478216 JR |
2147 | compare_rstp_priority_vectors(const struct rstp_priority_vector *v1, |
2148 | const struct rstp_priority_vector *v2) | |
9efd308e | 2149 | { |
26c0e924 | 2150 | VLOG_DBG("v1: "RSTP_ID_FMT", %u, "RSTP_ID_FMT", %d, %d", |
9efd308e | 2151 | RSTP_ID_ARGS(v1->root_bridge_id), v1->root_path_cost, |
26c0e924 DV |
2152 | RSTP_ID_ARGS(v1->designated_bridge_id), v1->designated_port_id, |
2153 | v1->bridge_port_id); | |
2154 | VLOG_DBG("v2: "RSTP_ID_FMT", %u, "RSTP_ID_FMT", %d, %d", | |
9efd308e | 2155 | RSTP_ID_ARGS(v2->root_bridge_id), v2->root_path_cost, |
26c0e924 DV |
2156 | RSTP_ID_ARGS(v2->designated_bridge_id), v2->designated_port_id, |
2157 | v2->bridge_port_id); | |
9efd308e | 2158 | |
a0478216 JR |
2159 | /* [17.6] |
2160 | * This message priority vector is superior to the port priority vector and | |
2161 | * will replace it if, and only if, the message priority vector is better | |
2162 | * than the port priority vector, or the message has been transmitted from | |
2163 | * the same Designated Bridge and Designated Port as the port priority | |
2164 | * vector, i.e., if the following is true: | |
2165 | * | |
2166 | * ((RD < RootBridgeID)) || | |
2167 | * ((RD == RootBridgeID) && (RPCD < RootPathCost)) || | |
2168 | * ((RD == RootBridgeID) && (RPCD == RootPathCost) && | |
2169 | * (D < designated_bridge_id)) || | |
2170 | * ((RD == RootBridgeID) && (RPCD == RootPathCost) && | |
2171 | * (D == designated_bridge_id) && (PD < designated_port_id)) || | |
2172 | * ((D == designated_bridge_id.BridgeAddress) && | |
2173 | * (PD == designated_port_id.PortNumber)) | |
2174 | */ | |
2175 | if ((v1->root_bridge_id < v2->root_bridge_id) | |
2176 | || (v1->root_bridge_id == v2->root_bridge_id | |
2177 | && v1->root_path_cost < v2->root_path_cost) | |
2178 | || (v1->root_bridge_id == v2->root_bridge_id | |
2179 | && v1->root_path_cost == v2->root_path_cost | |
2180 | && v1->designated_bridge_id < v2->designated_bridge_id) | |
2181 | || (v1->root_bridge_id == v2->root_bridge_id | |
2182 | && v1->root_path_cost == v2->root_path_cost | |
2183 | && v1->designated_bridge_id == v2->designated_bridge_id | |
2184 | && v1->designated_port_id < v2->designated_port_id) | |
2185 | || (v1->designated_bridge_id == v2->designated_bridge_id | |
2186 | && v1->designated_port_id == v2->designated_port_id)) { | |
2187 | /* SAME is a subset of SUPERIOR. */ | |
2188 | if (v1->root_bridge_id == v2->root_bridge_id | |
2189 | && v1->root_path_cost == v2->root_path_cost | |
2190 | && v1->designated_bridge_id == v2->designated_bridge_id | |
2191 | && v1->designated_port_id == v2->designated_port_id) { | |
26c0e924 DV |
2192 | if (v1->bridge_port_id < v2->bridge_port_id) { |
2193 | VLOG_DBG("superior"); | |
2194 | return SUPERIOR; | |
2195 | } | |
2196 | else if (v1->bridge_port_id > v2->bridge_port_id) { | |
2197 | VLOG_DBG("inferior"); | |
2198 | return INFERIOR; | |
2199 | } | |
a0478216 JR |
2200 | VLOG_DBG("superior_same"); |
2201 | return SAME; | |
2202 | } | |
2203 | VLOG_DBG("superior"); | |
9efd308e DV |
2204 | return SUPERIOR; |
2205 | } | |
a0478216 JR |
2206 | |
2207 | VLOG_DBG("inferior"); | |
2208 | return INFERIOR; | |
9efd308e DV |
2209 | } |
2210 | ||
2211 | static bool | |
311fb15b JR |
2212 | rstp_times_equal(struct rstp_times *t1, struct rstp_times *t2) |
2213 | { | |
2214 | return t1->forward_delay == t2->forward_delay | |
2215 | && t1->hello_time == t2->hello_time | |
2216 | && t1->max_age == t2->max_age | |
2217 | && t1->message_age == t2->message_age; | |
9efd308e | 2218 | } |