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