Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Nicira, Inc.
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 : : /* Based on sample implementation in 802.1D-1998. Above copyright and license
18 : : * applies to all modifications. */
19 : :
20 : : #include <config.h>
21 : :
22 : : #include "stp.h"
23 : : #include <sys/types.h>
24 : : #include <netinet/in.h>
25 : : #include <arpa/inet.h>
26 : : #include <inttypes.h>
27 : : #include <stdlib.h>
28 : : #include "byte-order.h"
29 : : #include "connectivity.h"
30 : : #include "openvswitch/ofpbuf.h"
31 : : #include "ovs-atomic.h"
32 : : #include "dp-packet.h"
33 : : #include "packets.h"
34 : : #include "seq.h"
35 : : #include "unixctl.h"
36 : : #include "util.h"
37 : : #include "openvswitch/vlog.h"
38 : :
39 : 2462 : VLOG_DEFINE_THIS_MODULE(stp);
40 : :
41 : : static struct vlog_rate_limit stp_rl = VLOG_RATE_LIMIT_INIT(60, 60);
42 : :
43 : : #define STP_PROTOCOL_ID 0x0000
44 : : #define STP_PROTOCOL_VERSION 0x00
45 : : #define STP_TYPE_CONFIG 0x00
46 : : #define STP_TYPE_TCN 0x80
47 : :
48 : : OVS_PACKED(
49 : : struct stp_bpdu_header {
50 : : ovs_be16 protocol_id; /* STP_PROTOCOL_ID. */
51 : : uint8_t protocol_version; /* STP_PROTOCOL_VERSION. */
52 : : uint8_t bpdu_type; /* One of STP_TYPE_*. */
53 : : });
54 : : BUILD_ASSERT_DECL(sizeof(struct stp_bpdu_header) == 4);
55 : :
56 : : enum stp_config_bpdu_flags {
57 : : STP_CONFIG_TOPOLOGY_CHANGE_ACK = 0x80,
58 : : STP_CONFIG_TOPOLOGY_CHANGE = 0x01
59 : : };
60 : :
61 : : OVS_PACKED(
62 : : struct stp_config_bpdu {
63 : : struct stp_bpdu_header header; /* Type STP_TYPE_CONFIG. */
64 : : uint8_t flags; /* STP_CONFIG_* flags. */
65 : : ovs_be64 root_id; /* 8.5.1.1: Bridge believed to be root. */
66 : : ovs_be32 root_path_cost; /* 8.5.1.2: Cost of path to root. */
67 : : ovs_be64 bridge_id; /* 8.5.1.3: ID of transmitting bridge. */
68 : : ovs_be16 port_id; /* 8.5.1.4: Port transmitting the BPDU. */
69 : : ovs_be16 message_age; /* 8.5.1.5: Age of BPDU at tx time. */
70 : : ovs_be16 max_age; /* 8.5.1.6: Timeout for received data. */
71 : : ovs_be16 hello_time; /* 8.5.1.7: Time between BPDU generation. */
72 : : ovs_be16 forward_delay; /* 8.5.1.8: State progression delay. */
73 : : });
74 : : BUILD_ASSERT_DECL(sizeof(struct stp_config_bpdu) == 35);
75 : :
76 : : OVS_PACKED(
77 : : struct stp_tcn_bpdu {
78 : : struct stp_bpdu_header header; /* Type STP_TYPE_TCN. */
79 : : });
80 : : BUILD_ASSERT_DECL(sizeof(struct stp_tcn_bpdu) == 4);
81 : :
82 : : struct stp_timer {
83 : : bool active; /* Timer in use? */
84 : : int value; /* Current value of timer, counting up. */
85 : : };
86 : :
87 : : struct stp_port {
88 : : struct stp *stp;
89 : : char *port_name; /* Human-readable name for log messages. */
90 : : void *aux; /* Auxiliary data the user may retrieve. */
91 : : int port_id; /* 8.5.5.1: Unique port identifier. */
92 : : enum stp_state state; /* 8.5.5.2: Current state. */
93 : : int path_cost; /* 8.5.5.3: Cost of tx/rx on this port. */
94 : : stp_identifier designated_root; /* 8.5.5.4. */
95 : : int designated_cost; /* 8.5.5.5: Path cost to root on port. */
96 : : stp_identifier designated_bridge; /* 8.5.5.6. */
97 : : int designated_port; /* 8.5.5.7: Port to send config msgs on. */
98 : : bool topology_change_ack; /* 8.5.5.8: Flag for next config BPDU. */
99 : : bool config_pending; /* 8.5.5.9: Send BPDU when hold expires? */
100 : : bool change_detection_enabled; /* 8.5.5.10: Detect topology changes? */
101 : :
102 : : struct stp_timer message_age_timer; /* 8.5.6.1: Age of received info. */
103 : : struct stp_timer forward_delay_timer; /* 8.5.6.2: State change timer. */
104 : : struct stp_timer hold_timer; /* 8.5.6.3: BPDU rate limit timer. */
105 : :
106 : : int tx_count; /* Number of BPDUs transmitted. */
107 : : int rx_count; /* Number of valid BPDUs received. */
108 : : int error_count; /* Number of bad BPDUs received. */
109 : :
110 : : bool state_changed;
111 : : };
112 : :
113 : : struct stp {
114 : : struct ovs_list node; /* Node in all_stps list. */
115 : :
116 : : /* Static bridge data. */
117 : : char *name; /* Human-readable name for log messages. */
118 : : stp_identifier bridge_id; /* 8.5.3.7: This bridge. */
119 : : int max_age; /* 8.5.3.4: Time to drop received data. */
120 : : int hello_time; /* 8.5.3.5: Time between sending BPDUs. */
121 : : int forward_delay; /* 8.5.3.6: Delay between state changes. */
122 : : int bridge_max_age; /* 8.5.3.8: max_age when we're root. */
123 : : int bridge_hello_time; /* 8.5.3.9: hello_time as root. */
124 : : int bridge_forward_delay; /* 8.5.3.10: forward_delay as root. */
125 : : int rq_max_age; /* User-requested max age, in ms. */
126 : : int rq_hello_time; /* User-requested hello time, in ms. */
127 : : int rq_forward_delay; /* User-requested forward delay, in ms. */
128 : : int elapsed_remainder; /* Left-over msecs from last stp_tick(). */
129 : :
130 : : /* Dynamic bridge data. */
131 : : stp_identifier designated_root; /* 8.5.3.1: Bridge believed to be root. */
132 : : unsigned int root_path_cost; /* 8.5.3.2: Cost of path to root. */
133 : : struct stp_port *root_port; /* 8.5.3.3: Lowest cost port to root. */
134 : : bool topology_change_detected; /* 8.5.3.11: Detected a topology change? */
135 : : bool topology_change; /* 8.5.3.12: Received topology change? */
136 : :
137 : : /* Bridge timers. */
138 : : struct stp_timer hello_timer; /* 8.5.4.1: Hello timer. */
139 : : struct stp_timer tcn_timer; /* 8.5.4.2: Topology change timer. */
140 : : struct stp_timer topology_change_timer; /* 8.5.4.3. */
141 : :
142 : : /* Ports. */
143 : : struct stp_port ports[STP_MAX_PORTS];
144 : :
145 : : /* Interface to client. */
146 : : bool fdb_needs_flush; /* MAC learning tables needs flushing. */
147 : : struct stp_port *first_changed_port;
148 : : void (*send_bpdu)(struct dp_packet *bpdu, int port_no, void *aux);
149 : : void *aux;
150 : :
151 : : struct ovs_refcount ref_cnt;
152 : : };
153 : :
154 : : static struct ovs_mutex mutex;
155 : : static struct ovs_list all_stps__ = OVS_LIST_INITIALIZER(&all_stps__);
156 : : static struct ovs_list *const all_stps OVS_GUARDED_BY(mutex) = &all_stps__;
157 : :
158 : : #define FOR_EACH_ENABLED_PORT(PORT, STP) \
159 : : for ((PORT) = stp_next_enabled_port((STP), (STP)->ports); \
160 : : (PORT); \
161 : : (PORT) = stp_next_enabled_port((STP), (PORT) + 1))
162 : : static struct stp_port *
163 : 310254 : stp_next_enabled_port(const struct stp *stp, const struct stp_port *port)
164 : : OVS_REQUIRES(mutex)
165 : : {
166 [ + + ]: 17364206 : for (; port < &stp->ports[ARRAY_SIZE(stp->ports)]; port++) {
167 [ + + ]: 17296378 : if (port->state != STP_DISABLED) {
168 : 242426 : return CONST_CAST(struct stp_port *, port);
169 : : }
170 : : }
171 : 67828 : return NULL;
172 : : }
173 : :
174 : : #define MESSAGE_AGE_INCREMENT 1
175 : :
176 : : static void stp_transmit_config(struct stp_port *) OVS_REQUIRES(mutex);
177 : : static bool stp_supersedes_port_info(const struct stp_port *,
178 : : const struct stp_config_bpdu *)
179 : : OVS_REQUIRES(mutex);
180 : : static void stp_record_config_information(struct stp_port *,
181 : : const struct stp_config_bpdu *)
182 : : OVS_REQUIRES(mutex);
183 : : static void stp_record_config_timeout_values(struct stp *,
184 : : const struct stp_config_bpdu *)
185 : : OVS_REQUIRES(mutex);
186 : : static bool stp_is_designated_port(const struct stp_port *)
187 : : OVS_REQUIRES(mutex);
188 : : static void stp_config_bpdu_generation(struct stp *) OVS_REQUIRES(mutex);
189 : : static void stp_transmit_tcn(struct stp *) OVS_REQUIRES(mutex);
190 : : static void stp_configuration_update(struct stp *) OVS_REQUIRES(mutex);
191 : : static bool stp_supersedes_root(const struct stp_port *root,
192 : : const struct stp_port *) OVS_REQUIRES(mutex);
193 : : static void stp_root_selection(struct stp *) OVS_REQUIRES(mutex);
194 : : static void stp_designated_port_selection(struct stp *) OVS_REQUIRES(mutex);
195 : : static void stp_become_designated_port(struct stp_port *)
196 : : OVS_REQUIRES(mutex);
197 : : static void stp_port_state_selection(struct stp *) OVS_REQUIRES(mutex);
198 : : static void stp_make_forwarding(struct stp_port *) OVS_REQUIRES(mutex);
199 : : static void stp_make_blocking(struct stp_port *) OVS_REQUIRES(mutex);
200 : : static void stp_set_port_state(struct stp_port *, enum stp_state)
201 : : OVS_REQUIRES(mutex);
202 : : static void stp_topology_change_detection(struct stp *) OVS_REQUIRES(mutex);
203 : : static void stp_topology_change_acknowledged(struct stp *)
204 : : OVS_REQUIRES(mutex);
205 : : static void stp_acknowledge_topology_change(struct stp_port *)
206 : : OVS_REQUIRES(mutex);
207 : : static void stp_received_config_bpdu(struct stp *, struct stp_port *,
208 : : const struct stp_config_bpdu *)
209 : : OVS_REQUIRES(mutex);
210 : : static void stp_received_tcn_bpdu(struct stp *, struct stp_port *)
211 : : OVS_REQUIRES(mutex);
212 : : static void stp_hello_timer_expiry(struct stp *) OVS_REQUIRES(mutex);
213 : : static void stp_message_age_timer_expiry(struct stp_port *)
214 : : OVS_REQUIRES(mutex);
215 : : static bool stp_is_designated_for_some_port(const struct stp *)
216 : : OVS_REQUIRES(mutex);
217 : : static void stp_forward_delay_timer_expiry(struct stp_port *)
218 : : OVS_REQUIRES(mutex);
219 : : static void stp_tcn_timer_expiry(struct stp *) OVS_REQUIRES(mutex);
220 : : static void stp_topology_change_timer_expiry(struct stp *)
221 : : OVS_REQUIRES(mutex);
222 : : static void stp_hold_timer_expiry(struct stp_port *) OVS_REQUIRES(mutex);
223 : : static void stp_initialize_port(struct stp_port *, enum stp_state)
224 : : OVS_REQUIRES(mutex);
225 : : static void stp_become_root_bridge(struct stp *) OVS_REQUIRES(mutex);
226 : : static void stp_update_bridge_timers(struct stp *) OVS_REQUIRES(mutex);
227 : :
228 : : static int clamp(int x, int min, int max);
229 : : static int ms_to_timer(int ms);
230 : : static int timer_to_ms(int timer);
231 : : static void stp_start_timer(struct stp_timer *, int value);
232 : : static void stp_stop_timer(struct stp_timer *);
233 : : static bool stp_timer_expired(struct stp_timer *, int elapsed, int timeout);
234 : :
235 : : static void stp_send_bpdu(struct stp_port *, const void *, size_t)
236 : : OVS_REQUIRES(mutex);
237 : : static void stp_unixctl_tcn(struct unixctl_conn *, int argc,
238 : : const char *argv[], void *aux);
239 : :
240 : : void
241 : 663 : stp_init(void)
242 : : {
243 : : static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
244 : :
245 [ + + ]: 663 : if (ovsthread_once_start(&once)) {
246 : : /* We need a recursive mutex because stp_send_bpdu() could loop back
247 : : * into the stp module through a patch port. This happens
248 : : * intentionally as part of the unit tests. Ideally we'd ditch
249 : : * the call back function, but for now this is what we have. */
250 : 630 : ovs_mutex_init_recursive(&mutex);
251 : :
252 : 630 : unixctl_command_register("stp/tcn", "[bridge]", 0, 1, stp_unixctl_tcn,
253 : : NULL);
254 : 630 : ovsthread_once_done(&once);
255 : : }
256 : 663 : }
257 : :
258 : : /* Creates and returns a new STP instance that initially has no ports enabled.
259 : : *
260 : : * 'bridge_id' should be a 48-bit MAC address as returned by
261 : : * eth_addr_to_uint64(). 'bridge_id' may also have a priority value in its top
262 : : * 16 bits; if those bits are set to 0, STP_DEFAULT_BRIDGE_PRIORITY is used.
263 : : * (This priority may be changed with stp_set_bridge_priority().)
264 : : *
265 : : * When the bridge needs to send out a BPDU, it calls 'send_bpdu'. This
266 : : * callback may be called from stp_tick() or stp_received_bpdu(). The
267 : : * arguments to 'send_bpdu' are an STP BPDU encapsulated in 'bpdu',
268 : : * the spanning tree port number 'port_no' that should transmit the
269 : : * packet, and auxiliary data to be passed to the callback in 'aux'.
270 : : */
271 : : struct stp *
272 : 46 : stp_create(const char *name, stp_identifier bridge_id,
273 : : void (*send_bpdu)(struct dp_packet *bpdu, int port_no, void *aux),
274 : : void *aux)
275 : : {
276 : : struct stp *stp;
277 : : struct stp_port *p;
278 : :
279 : 46 : stp_init();
280 : :
281 : 46 : ovs_mutex_lock(&mutex);
282 : 46 : stp = xzalloc(sizeof *stp);
283 : 46 : stp->name = xstrdup(name);
284 : 46 : stp->bridge_id = bridge_id;
285 [ + - ]: 46 : if (!(stp->bridge_id >> 48)) {
286 : 46 : stp->bridge_id |= (uint64_t) STP_DEFAULT_BRIDGE_PRIORITY << 48;
287 : : }
288 : :
289 : 46 : stp->rq_max_age = STP_DEFAULT_MAX_AGE;
290 : 46 : stp->rq_hello_time = STP_DEFAULT_HELLO_TIME;
291 : 46 : stp->rq_forward_delay = STP_DEFAULT_FWD_DELAY;
292 : 46 : stp_update_bridge_timers(stp);
293 : 46 : stp->max_age = stp->bridge_max_age;
294 : 46 : stp->hello_time = stp->bridge_hello_time;
295 : 46 : stp->forward_delay = stp->bridge_forward_delay;
296 : :
297 : 46 : stp->designated_root = stp->bridge_id;
298 : 46 : stp->root_path_cost = 0;
299 : 46 : stp->root_port = NULL;
300 : 46 : stp->topology_change_detected = false;
301 : 46 : stp->topology_change = false;
302 : :
303 : 46 : stp_stop_timer(&stp->tcn_timer);
304 : 46 : stp_stop_timer(&stp->topology_change_timer);
305 : 46 : stp_start_timer(&stp->hello_timer, 0);
306 : :
307 : 46 : stp->send_bpdu = send_bpdu;
308 : 46 : stp->aux = aux;
309 : :
310 : 46 : stp->first_changed_port = &stp->ports[ARRAY_SIZE(stp->ports)];
311 [ + + ]: 11776 : for (p = stp->ports; p < &stp->ports[ARRAY_SIZE(stp->ports)]; p++) {
312 : 11730 : p->stp = stp;
313 : 11730 : p->port_id = (stp_port_no(p) + 1) | (STP_DEFAULT_PORT_PRIORITY << 8);
314 : 11730 : p->path_cost = 19; /* Recommended default for 100 Mb/s link. */
315 : 11730 : stp_initialize_port(p, STP_DISABLED);
316 : : }
317 : 46 : ovs_refcount_init(&stp->ref_cnt);
318 : :
319 : 46 : ovs_list_push_back(all_stps, &stp->node);
320 : 46 : ovs_mutex_unlock(&mutex);
321 : 46 : return stp;
322 : : }
323 : :
324 : : struct stp *
325 : 64 : stp_ref(const struct stp *stp_)
326 : : {
327 : 64 : struct stp *stp = CONST_CAST(struct stp *, stp_);
328 [ + - ]: 64 : if (stp) {
329 : 64 : ovs_refcount_ref(&stp->ref_cnt);
330 : : }
331 : 64 : return stp;
332 : : }
333 : :
334 : : /* Destroys 'stp'. */
335 : : void
336 : 46572 : stp_unref(struct stp *stp)
337 : : {
338 [ + + ][ + + ]: 46572 : if (stp && ovs_refcount_unref_relaxed(&stp->ref_cnt) == 1) {
339 : : size_t i;
340 : :
341 : 44 : ovs_mutex_lock(&mutex);
342 : 44 : ovs_list_remove(&stp->node);
343 : 44 : ovs_mutex_unlock(&mutex);
344 : 44 : free(stp->name);
345 : :
346 [ + + ]: 11264 : for (i = 0; i < STP_MAX_PORTS; i++) {
347 : 11220 : free(stp->ports[i].port_name);
348 : : }
349 : 44 : free(stp);
350 : : }
351 : 46572 : }
352 : :
353 : : /* Runs 'stp' given that 'ms' milliseconds have passed. */
354 : : void
355 : 14462 : stp_tick(struct stp *stp, int ms)
356 : : {
357 : : struct stp_port *p;
358 : : int elapsed;
359 : :
360 : 14462 : ovs_mutex_lock(&mutex);
361 : : /* Convert 'ms' to STP timer ticks. Preserve any leftover milliseconds
362 : : * from previous stp_tick() calls so that we don't lose STP ticks when we
363 : : * are called too frequently. */
364 : 14462 : ms = clamp(ms, 0, INT_MAX - 1000) + stp->elapsed_remainder;
365 : 14462 : elapsed = ms_to_timer(ms);
366 : 14462 : stp->elapsed_remainder = ms - timer_to_ms(elapsed);
367 [ + + ]: 14462 : if (!elapsed) {
368 : 15 : goto out;
369 : : }
370 : :
371 [ + + ]: 14447 : if (stp_timer_expired(&stp->hello_timer, elapsed, stp->hello_time)) {
372 : 2113 : stp_hello_timer_expiry(stp);
373 : : }
374 [ + + ]: 14447 : if (stp_timer_expired(&stp->tcn_timer, elapsed, stp->bridge_hello_time)) {
375 : 1 : stp_tcn_timer_expiry(stp);
376 : : }
377 [ + + ]: 14447 : if (stp_timer_expired(&stp->topology_change_timer, elapsed,
378 : 14447 : stp->max_age + stp->forward_delay)) {
379 : 21 : stp_topology_change_timer_expiry(stp);
380 : : }
381 [ + + ]: 64163 : FOR_EACH_ENABLED_PORT (p, stp) {
382 [ + + ]: 49716 : if (stp_timer_expired(&p->message_age_timer, elapsed, stp->max_age)) {
383 : 2 : stp_message_age_timer_expiry(p);
384 : : }
385 : : }
386 [ + + ]: 64163 : FOR_EACH_ENABLED_PORT (p, stp) {
387 [ + + ]: 49716 : if (stp_timer_expired(&p->forward_delay_timer, elapsed,
388 : : stp->forward_delay)) {
389 : 270 : stp_forward_delay_timer_expiry(p);
390 : : }
391 [ + + ]: 49716 : if (stp_timer_expired(&p->hold_timer, elapsed, ms_to_timer(1000))) {
392 : 15146 : stp_hold_timer_expiry(p);
393 : : }
394 : : }
395 : :
396 : : out:
397 : 14462 : ovs_mutex_unlock(&mutex);
398 : 14462 : }
399 : :
400 : : static void
401 : 31 : set_bridge_id(struct stp *stp, stp_identifier new_bridge_id)
402 : : OVS_REQUIRES(mutex)
403 : : {
404 [ + + ]: 31 : if (new_bridge_id != stp->bridge_id) {
405 : : bool root;
406 : : struct stp_port *p;
407 : :
408 : 11 : root = stp_is_root_bridge(stp);
409 [ + + ]: 39 : FOR_EACH_ENABLED_PORT (p, stp) {
410 [ + + ]: 28 : if (stp_is_designated_port(p)) {
411 : 12 : p->designated_bridge = new_bridge_id;
412 : : }
413 : : }
414 : 11 : stp->bridge_id = new_bridge_id;
415 : 11 : stp_configuration_update(stp);
416 : 11 : stp_port_state_selection(stp);
417 [ + - ][ + + ]: 11 : if (stp_is_root_bridge(stp) && !root) {
418 : 6 : stp_become_root_bridge(stp);
419 : : }
420 : : }
421 : 31 : }
422 : :
423 : : void
424 : 10 : stp_set_bridge_id(struct stp *stp, stp_identifier bridge_id)
425 : : {
426 : 10 : const uint64_t mac_bits = (UINT64_C(1) << 48) - 1;
427 : 10 : const uint64_t pri_bits = ~mac_bits;
428 : 10 : ovs_mutex_lock(&mutex);
429 : 10 : set_bridge_id(stp, (stp->bridge_id & pri_bits) | (bridge_id & mac_bits));
430 : 10 : ovs_mutex_unlock(&mutex);
431 : 10 : }
432 : :
433 : : void
434 : 21 : stp_set_bridge_priority(struct stp *stp, uint16_t new_priority)
435 : : {
436 : 21 : const uint64_t mac_bits = (UINT64_C(1) << 48) - 1;
437 : 21 : ovs_mutex_lock(&mutex);
438 : 21 : set_bridge_id(stp, ((stp->bridge_id & mac_bits)
439 : 21 : | ((uint64_t) new_priority << 48)));
440 : 21 : ovs_mutex_unlock(&mutex);
441 : 21 : }
442 : :
443 : : /* Sets the desired hello time for 'stp' to 'ms', in milliseconds. The actual
444 : : * hello time is clamped to the range of 1 to 10 seconds and subject to the
445 : : * relationship (bridge_max_age >= 2 * (bridge_hello_time + 1 s)). The bridge
446 : : * hello time is only used when 'stp' is the root bridge. */
447 : : void
448 : 10 : stp_set_hello_time(struct stp *stp, int ms)
449 : : {
450 : 10 : ovs_mutex_lock(&mutex);
451 : 10 : stp->rq_hello_time = ms;
452 : 10 : stp_update_bridge_timers(stp);
453 : 10 : ovs_mutex_unlock(&mutex);
454 : 10 : }
455 : :
456 : : /* Sets the desired max age for 'stp' to 'ms', in milliseconds. The actual max
457 : : * age is clamped to the range of 6 to 40 seconds and subject to the
458 : : * relationships (2 * (bridge_forward_delay - 1 s) >= bridge_max_age) and
459 : : * (bridge_max_age >= 2 * (bridge_hello_time + 1 s)). The bridge max age is
460 : : * only used when 'stp' is the root bridge. */
461 : : void
462 : 10 : stp_set_max_age(struct stp *stp, int ms)
463 : : {
464 : 10 : ovs_mutex_lock(&mutex);
465 : 10 : stp->rq_max_age = ms;
466 : 10 : stp_update_bridge_timers(stp);
467 : 10 : ovs_mutex_unlock(&mutex);
468 : 10 : }
469 : :
470 : : /* Sets the desired forward delay for 'stp' to 'ms', in milliseconds. The
471 : : * actual forward delay is clamped to the range of 4 to 30 seconds and subject
472 : : * to the relationship (2 * (bridge_forward_delay - 1 s) >= bridge_max_age).
473 : : * The bridge forward delay is only used when 'stp' is the root bridge. */
474 : : void
475 : 10 : stp_set_forward_delay(struct stp *stp, int ms)
476 : : {
477 : 10 : ovs_mutex_lock(&mutex);
478 : 10 : stp->rq_forward_delay = ms;
479 : 10 : stp_update_bridge_timers(stp);
480 : 10 : ovs_mutex_unlock(&mutex);
481 : 10 : }
482 : :
483 : : /* Returns the name given to 'stp' in the call to stp_create(). */
484 : : const char *
485 : 0 : stp_get_name(const struct stp *stp)
486 : : {
487 : : char *name;
488 : :
489 : 0 : ovs_mutex_lock(&mutex);
490 : 0 : name = stp->name;
491 : 0 : ovs_mutex_unlock(&mutex);
492 : 0 : return name;
493 : : }
494 : :
495 : : /* Returns the bridge ID for 'stp'. */
496 : : stp_identifier
497 : 38 : stp_get_bridge_id(const struct stp *stp)
498 : : {
499 : : stp_identifier bridge_id;
500 : :
501 : 38 : ovs_mutex_lock(&mutex);
502 : 38 : bridge_id = stp->bridge_id;
503 : 38 : ovs_mutex_unlock(&mutex);
504 : 38 : return bridge_id;
505 : : }
506 : :
507 : : /* Returns the bridge ID of the bridge currently believed to be the root. */
508 : : stp_identifier
509 : 52 : stp_get_designated_root(const struct stp *stp)
510 : : {
511 : : stp_identifier designated_root;
512 : :
513 : 52 : ovs_mutex_lock(&mutex);
514 : 52 : designated_root = stp->designated_root;
515 : 52 : ovs_mutex_unlock(&mutex);
516 : 52 : return designated_root;
517 : : }
518 : :
519 : : /* Returns true if 'stp' believes itself to the be root of the spanning tree,
520 : : * false otherwise. */
521 : : bool
522 : 36660 : stp_is_root_bridge(const struct stp *stp)
523 : : {
524 : : bool is_root;
525 : :
526 : 36660 : ovs_mutex_lock(&mutex);
527 : 36660 : is_root = stp->bridge_id == stp->designated_root;
528 : 36660 : ovs_mutex_unlock(&mutex);
529 : 36660 : return is_root;
530 : : }
531 : :
532 : : /* Returns the cost of the path from 'stp' to the root of the spanning tree. */
533 : : int
534 : 116 : stp_get_root_path_cost(const struct stp *stp)
535 : : {
536 : : int cost;
537 : :
538 : 116 : ovs_mutex_lock(&mutex);
539 : 116 : cost = stp->root_path_cost;
540 : 116 : ovs_mutex_unlock(&mutex);
541 : 116 : return cost;
542 : : }
543 : :
544 : : /* Returns the bridge hello time, in ms. The returned value is not necessarily
545 : : * the value passed to stp_set_hello_time(): it is clamped to the valid range
546 : : * and quantized to the STP timer resolution. */
547 : : int
548 : 0 : stp_get_hello_time(const struct stp *stp)
549 : : {
550 : : int time;
551 : :
552 : 0 : ovs_mutex_lock(&mutex);
553 : 0 : time = timer_to_ms(stp->bridge_hello_time);
554 : 0 : ovs_mutex_unlock(&mutex);
555 : 0 : return time;
556 : : }
557 : :
558 : : /* Returns the bridge max age, in ms. The returned value is not necessarily
559 : : * the value passed to stp_set_max_age(): it is clamped to the valid range,
560 : : * quantized to the STP timer resolution, and adjusted to match the constraints
561 : : * due to the hello time. */
562 : : int
563 : 0 : stp_get_max_age(const struct stp *stp)
564 : : {
565 : : int time;
566 : :
567 : 0 : ovs_mutex_lock(&mutex);
568 : 0 : time = timer_to_ms(stp->bridge_max_age);
569 : 0 : ovs_mutex_unlock(&mutex);
570 : 0 : return time;
571 : : }
572 : :
573 : : /* Returns the bridge forward delay, in ms. The returned value is not
574 : : * necessarily the value passed to stp_set_forward_delay(): it is clamped to
575 : : * the valid range, quantized to the STP timer resolution, and adjusted to
576 : : * match the constraints due to the forward delay. */
577 : : int
578 : 0 : stp_get_forward_delay(const struct stp *stp)
579 : : {
580 : : int time;
581 : :
582 : 0 : ovs_mutex_lock(&mutex);
583 : 0 : time = timer_to_ms(stp->bridge_forward_delay);
584 : 0 : ovs_mutex_unlock(&mutex);
585 : 0 : return time;
586 : : }
587 : :
588 : : /* Returns true if something has happened to 'stp' which necessitates flushing
589 : : * the client's MAC learning table. Calling this function resets 'stp' so that
590 : : * future calls will return false until flushing is required again. */
591 : : bool
592 : 244 : stp_check_and_reset_fdb_flush(struct stp *stp)
593 : : {
594 : : bool needs_flush;
595 : :
596 : 244 : ovs_mutex_lock(&mutex);
597 : 244 : needs_flush = stp->fdb_needs_flush;
598 : 244 : stp->fdb_needs_flush = false;
599 : 244 : ovs_mutex_unlock(&mutex);
600 : 244 : return needs_flush;
601 : : }
602 : :
603 : : /* Returns the port in 'stp' with index 'port_no', which must be between 0 and
604 : : * STP_MAX_PORTS. */
605 : : struct stp_port *
606 : 39893 : stp_get_port(struct stp *stp, int port_no)
607 : : {
608 : : struct stp_port *port;
609 : :
610 : 39893 : ovs_mutex_lock(&mutex);
611 [ + - ][ - + ]: 39893 : ovs_assert(port_no >= 0 && port_no < ARRAY_SIZE(stp->ports));
612 : 39893 : port = &stp->ports[port_no];
613 : 39893 : ovs_mutex_unlock(&mutex);
614 : 39893 : return port;
615 : : }
616 : :
617 : : /* Returns the port connecting 'stp' to the root bridge, or a null pointer if
618 : : * there is no such port. */
619 : : struct stp_port *
620 : 165 : stp_get_root_port(struct stp *stp)
621 : : {
622 : : struct stp_port *port;
623 : :
624 : 165 : ovs_mutex_lock(&mutex);
625 : 165 : port = stp->root_port;
626 : 165 : ovs_mutex_unlock(&mutex);
627 : 165 : return port;
628 : : }
629 : :
630 : : /* Finds a port whose state has changed. If successful, stores the port whose
631 : : * state changed in '*portp' and returns true. If no port has changed, stores
632 : : * NULL in '*portp' and returns false. */
633 : : bool
634 : 250 : stp_get_changed_port(struct stp *stp, struct stp_port **portp)
635 : : {
636 : : struct stp_port *end, *p;
637 : 250 : bool changed = false;
638 : :
639 : 250 : ovs_mutex_lock(&mutex);
640 : 250 : end = &stp->ports[ARRAY_SIZE(stp->ports)];
641 [ + + ]: 1774 : for (p = stp->first_changed_port; p < end; p++) {
642 [ + + ]: 1530 : if (p->state_changed) {
643 : 6 : p->state_changed = false;
644 : 6 : stp->first_changed_port = p + 1;
645 : 6 : *portp = p;
646 : 6 : changed = true;
647 : 6 : goto out;
648 : : }
649 : : }
650 : 244 : stp->first_changed_port = end;
651 : 244 : *portp = NULL;
652 : :
653 : : out:
654 : 250 : ovs_mutex_unlock(&mutex);
655 : 250 : return changed;
656 : : }
657 : :
658 : : /* Returns the name for the given 'state' (for use in debugging and log
659 : : * messages). */
660 : : const char *
661 : 50 : stp_state_name(enum stp_state state)
662 : : {
663 [ + + + + : 50 : switch (state) {
- - ]
664 : : case STP_DISABLED:
665 : 4 : return "disabled";
666 : : case STP_LISTENING:
667 : 20 : return "listening";
668 : : case STP_LEARNING:
669 : 16 : return "learning";
670 : : case STP_FORWARDING:
671 : 10 : return "forwarding";
672 : : case STP_BLOCKING:
673 : 0 : return "blocking";
674 : : default:
675 : 0 : OVS_NOT_REACHED();
676 : : }
677 : : }
678 : :
679 : : /* Returns true if 'state' is one in which packets received on a port should
680 : : * be forwarded, false otherwise.
681 : : */
682 : : bool
683 : 26 : stp_forward_in_state(enum stp_state state)
684 : : {
685 : 26 : return (state & STP_FORWARDING) != 0;
686 : : }
687 : :
688 : : /* Returns true if 'state' is one in which MAC learning should be done on
689 : : * packets received on a port, false otherwise.
690 : : */
691 : : bool
692 : 16 : stp_learn_in_state(enum stp_state state)
693 : : {
694 : 16 : return (state & (STP_LEARNING | STP_FORWARDING)) != 0;
695 : : }
696 : :
697 : : /* Returns true if 'state' is one in which bpdus should be forwarded on a
698 : : * port, false otherwise.
699 : : *
700 : : * Returns true if 'state' is STP_DISABLED, since in that case the port does
701 : : * not generate the bpdu and should just forward it (e.g. patch port on pif
702 : : * bridge). */
703 : : bool
704 : 20 : stp_should_forward_bpdu(enum stp_state state)
705 : : {
706 : 20 : return (state &
707 : : ( STP_DISABLED | STP_LISTENING | STP_LEARNING
708 : : | STP_FORWARDING)) != 0;
709 : : }
710 : :
711 : : /* Returns the name for the given 'role' (for use in debugging and log
712 : : * messages). */
713 : : const char *
714 : 34 : stp_role_name(enum stp_role role)
715 : : {
716 [ + + - - : 34 : switch (role) {
- ]
717 : : case STP_ROLE_ROOT:
718 : 14 : return "root";
719 : : case STP_ROLE_DESIGNATED:
720 : 20 : return "designated";
721 : : case STP_ROLE_ALTERNATE:
722 : 0 : return "alternate";
723 : : case STP_ROLE_DISABLED:
724 : 0 : return "disabled";
725 : : default:
726 : 0 : OVS_NOT_REACHED();
727 : : }
728 : : }
729 : :
730 : : /* Notifies the STP entity that bridge protocol data unit 'bpdu', which is
731 : : * 'bpdu_size' bytes in length, was received on port 'p'.
732 : : *
733 : : * This function may call the 'send_bpdu' function provided to stp_create(). */
734 : : void
735 : 10595 : stp_received_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
736 : : {
737 : 10595 : struct stp *stp = p->stp;
738 : : const struct stp_bpdu_header *header;
739 : :
740 : 10595 : ovs_mutex_lock(&mutex);
741 [ + + ]: 10595 : if (p->state == STP_DISABLED) {
742 : 91 : goto out;
743 : : }
744 : :
745 [ - + ]: 10504 : if (bpdu_size < sizeof(struct stp_bpdu_header)) {
746 [ # # ]: 0 : VLOG_WARN("%s: received runt %"PRIuSIZE"-byte BPDU", stp->name, bpdu_size);
747 : 0 : p->error_count++;
748 : 0 : goto out;
749 : : }
750 : :
751 : 10504 : header = bpdu;
752 [ - + ]: 10504 : if (header->protocol_id != htons(STP_PROTOCOL_ID)) {
753 [ # # ]: 0 : VLOG_WARN("%s: received BPDU with unexpected protocol ID %"PRIu16,
754 : : stp->name, ntohs(header->protocol_id));
755 : 0 : p->error_count++;
756 : 0 : goto out;
757 : : }
758 [ - + ]: 10504 : if (header->protocol_version != STP_PROTOCOL_VERSION) {
759 [ # # ]: 0 : VLOG_DBG("%s: received BPDU with unexpected protocol version %"PRIu8,
760 : : stp->name, header->protocol_version);
761 : : }
762 : :
763 [ + + - ]: 10504 : switch (header->bpdu_type) {
764 : : case STP_TYPE_CONFIG:
765 [ - + ]: 10451 : if (bpdu_size < sizeof(struct stp_config_bpdu)) {
766 [ # # ]: 0 : VLOG_WARN("%s: received config BPDU with invalid size %"PRIuSIZE,
767 : : stp->name, bpdu_size);
768 : 0 : p->error_count++;
769 : 0 : goto out;
770 : : }
771 : 10451 : stp_received_config_bpdu(stp, p, bpdu);
772 : 10451 : break;
773 : :
774 : : case STP_TYPE_TCN:
775 [ - + ]: 53 : if (bpdu_size != sizeof(struct stp_tcn_bpdu)) {
776 [ # # ]: 0 : VLOG_WARN("%s: received TCN BPDU with invalid size %"PRIuSIZE,
777 : : stp->name, bpdu_size);
778 : 0 : p->error_count++;
779 : 0 : goto out;
780 : : }
781 : 53 : stp_received_tcn_bpdu(stp, p);
782 : 53 : break;
783 : :
784 : : default:
785 [ # # ]: 0 : VLOG_WARN("%s: received BPDU of unexpected type %"PRIu8,
786 : : stp->name, header->bpdu_type);
787 : 0 : p->error_count++;
788 : 0 : goto out;
789 : : }
790 : 10504 : p->rx_count++;
791 : :
792 : : out:
793 : 10595 : ovs_mutex_unlock(&mutex);
794 : 10595 : }
795 : :
796 : : /* Returns the STP entity in which 'p' is nested. */
797 : : struct stp *
798 : 0 : stp_port_get_stp(struct stp_port *p)
799 : : {
800 : : struct stp *stp;
801 : :
802 : 0 : ovs_mutex_lock(&mutex);
803 : 0 : stp = p->stp;
804 : 0 : ovs_mutex_unlock(&mutex);
805 : 0 : return stp;
806 : : }
807 : :
808 : : void
809 : 6 : stp_port_set_name(struct stp_port *p, const char *name)
810 : : {
811 : : char *old;
812 : :
813 : 6 : ovs_mutex_lock(&mutex);
814 : 6 : old = p->port_name;
815 : 6 : p->port_name = xstrdup(name);
816 : 6 : free(old);
817 : 6 : ovs_mutex_unlock(&mutex);
818 : 6 : }
819 : :
820 : : /* Sets the 'aux' member of 'p'.
821 : : *
822 : : * The 'aux' member will be reset to NULL when stp_port_disable() is
823 : : * called or stp_port_enable() is called when the port is in a Disabled
824 : : * state. */
825 : : void
826 : 6 : stp_port_set_aux(struct stp_port *p, void *aux)
827 : : {
828 : 6 : ovs_mutex_lock(&mutex);
829 : 6 : p->aux = aux;
830 : 6 : ovs_mutex_unlock(&mutex);
831 : 6 : }
832 : :
833 : : /* Returns the 'aux' member of 'p'. */
834 : : void *
835 : 19 : stp_port_get_aux(struct stp_port *p)
836 : : {
837 : : void *aux;
838 : :
839 : 19 : ovs_mutex_lock(&mutex);
840 : 19 : aux = p->aux;
841 : 19 : ovs_mutex_unlock(&mutex);
842 : 19 : return aux;
843 : : }
844 : :
845 : : /* Returns the index of port 'p' within its bridge. */
846 : : int
847 : 27001 : stp_port_no(const struct stp_port *p)
848 : : {
849 : : struct stp *stp;
850 : : int index;
851 : :
852 : 27001 : ovs_mutex_lock(&mutex);
853 : 27001 : stp = p->stp;
854 [ + - ][ - + ]: 27001 : ovs_assert(p >= stp->ports && p < &stp->ports[ARRAY_SIZE(stp->ports)]);
855 : 27001 : index = p - p->stp->ports;
856 : 27001 : ovs_mutex_unlock(&mutex);
857 : 27001 : return index;
858 : : }
859 : :
860 : : /* Returns the port ID for 'p'. */
861 : : int
862 : 34 : stp_port_get_id(const struct stp_port *p)
863 : : {
864 : : int port_id;
865 : :
866 : 34 : ovs_mutex_lock(&mutex);
867 : 34 : port_id = p->port_id;
868 : 34 : ovs_mutex_unlock(&mutex);
869 : 34 : return port_id;
870 : : }
871 : :
872 : : /* Returns the state of port 'p'. */
873 : : enum stp_state
874 : 16197 : stp_port_get_state(const struct stp_port *p)
875 : : {
876 : : enum stp_state state;
877 : :
878 : 16197 : ovs_mutex_lock(&mutex);
879 : 16197 : state = p->state;
880 : 16197 : ovs_mutex_unlock(&mutex);
881 : 16197 : return state;
882 : : }
883 : :
884 : : /* Returns the role of port 'p'. */
885 : : enum stp_role
886 : 34 : stp_port_get_role(const struct stp_port *p)
887 : : {
888 : : struct stp_port *root_port;
889 : : enum stp_role role;
890 : :
891 : 34 : ovs_mutex_lock(&mutex);
892 : 34 : root_port = p->stp->root_port;
893 [ + + ][ + - ]: 34 : if (root_port && root_port->port_id == p->port_id) {
894 : 14 : role = STP_ROLE_ROOT;
895 [ + - ]: 20 : } else if (stp_is_designated_port(p)) {
896 : 20 : role = STP_ROLE_DESIGNATED;
897 [ # # ]: 0 : } else if (p->state == STP_DISABLED) {
898 : 0 : role = STP_ROLE_DISABLED;
899 : : } else {
900 : 0 : role = STP_ROLE_ALTERNATE;
901 : : }
902 : 34 : ovs_mutex_unlock(&mutex);
903 : 34 : return role;
904 : : }
905 : :
906 : : /* Retrieves BPDU transmit and receive counts for 'p'. */
907 : : void
908 : 10 : stp_port_get_counts(const struct stp_port *p,
909 : : int *tx_count, int *rx_count, int *error_count)
910 : : {
911 : 10 : ovs_mutex_lock(&mutex);
912 : 10 : *tx_count = p->tx_count;
913 : 10 : *rx_count = p->rx_count;
914 : 10 : *error_count = p->error_count;
915 : 10 : ovs_mutex_unlock(&mutex);
916 : 10 : }
917 : :
918 : : /* Disables STP on port 'p'. */
919 : : void
920 : 12848 : stp_port_disable(struct stp_port *p)
921 : : {
922 : : struct stp *stp;
923 : :
924 : 12848 : ovs_mutex_lock(&mutex);
925 : 12848 : stp = p->stp;
926 [ + + ]: 12848 : if (p->state != STP_DISABLED) {
927 : 5 : bool root = stp_is_root_bridge(stp);
928 : 5 : stp_become_designated_port(p);
929 : 5 : stp_set_port_state(p, STP_DISABLED);
930 : 5 : p->topology_change_ack = false;
931 : 5 : p->config_pending = false;
932 : 5 : stp_stop_timer(&p->message_age_timer);
933 : 5 : stp_stop_timer(&p->forward_delay_timer);
934 : 5 : stp_configuration_update(stp);
935 : 5 : stp_port_state_selection(stp);
936 [ + + ][ + + ]: 5 : if (stp_is_root_bridge(stp) && !root) {
937 : 1 : stp_become_root_bridge(stp);
938 : : }
939 : 5 : p->aux = NULL;
940 : : }
941 : 12848 : ovs_mutex_unlock(&mutex);
942 : 12848 : }
943 : :
944 : : /* Enables STP on port 'p'. The port will initially be in "blocking" state. */
945 : : void
946 : 156 : stp_port_enable(struct stp_port *p)
947 : : {
948 : 156 : ovs_mutex_lock(&mutex);
949 [ + + ]: 156 : if (p->state == STP_DISABLED) {
950 : 138 : stp_initialize_port(p, STP_BLOCKING);
951 : 138 : stp_port_state_selection(p->stp);
952 : : }
953 : 156 : ovs_mutex_unlock(&mutex);
954 : 156 : }
955 : :
956 : : /* Sets the priority of port 'p' to 'new_priority'. Lower numerical values
957 : : * are interpreted as higher priorities. */
958 : : void
959 : 7 : stp_port_set_priority(struct stp_port *p, uint8_t new_priority)
960 : : {
961 : : uint16_t new_port_id;
962 : :
963 : 7 : ovs_mutex_lock(&mutex);
964 : 7 : new_port_id = (p->port_id & 0xff) | (new_priority << 8);
965 [ + + ]: 7 : if (p->port_id != new_port_id) {
966 : 1 : struct stp *stp = p->stp;
967 [ - + ]: 1 : if (stp_is_designated_port(p)) {
968 : 0 : p->designated_port = new_port_id;
969 : : }
970 : 1 : p->port_id = new_port_id;
971 [ - + ]: 1 : if (stp->bridge_id == p->designated_bridge
972 [ # # ]: 0 : && p->port_id < p->designated_port) {
973 : 0 : stp_become_designated_port(p);
974 : 0 : stp_port_state_selection(stp);
975 : : }
976 : : }
977 : 7 : ovs_mutex_unlock(&mutex);
978 : 7 : }
979 : :
980 : : /* Convert 'speed' (measured in Mb/s) into the path cost. */
981 : : uint16_t
982 : 6 : stp_convert_speed_to_cost(unsigned int speed)
983 : : {
984 : : uint16_t ret;
985 : :
986 : 6 : ovs_mutex_lock(&mutex);
987 [ + - ][ + - ]: 6 : ret = speed >= 10000 ? 2 /* 10 Gb/s. */
[ - + ][ # # ]
[ # # ][ # # ]
988 : : : speed >= 1000 ? 4 /* 1 Gb/s. */
989 : : : speed >= 100 ? 19 /* 100 Mb/s. */
990 : : : speed >= 16 ? 62 /* 16 Mb/s. */
991 : : : speed >= 10 ? 100 /* 10 Mb/s. */
992 : : : speed >= 4 ? 250 /* 4 Mb/s. */
993 : : : 19; /* 100 Mb/s (guess). */
994 : 6 : ovs_mutex_unlock(&mutex);
995 : 6 : return ret;
996 : : }
997 : :
998 : : /* Sets the path cost of port 'p' to 'path_cost'. Lower values are generally
999 : : * used to indicate faster links. Use stp_port_set_speed() to automatically
1000 : : * generate a default path cost from a link speed. */
1001 : : void
1002 : 156 : stp_port_set_path_cost(struct stp_port *p, uint16_t path_cost)
1003 : : {
1004 : 156 : ovs_mutex_lock(&mutex);
1005 [ + + ]: 156 : if (p->path_cost != path_cost) {
1006 : 136 : struct stp *stp = p->stp;
1007 : 136 : p->path_cost = path_cost;
1008 : 136 : stp_configuration_update(stp);
1009 : 136 : stp_port_state_selection(stp);
1010 : : }
1011 : 156 : ovs_mutex_unlock(&mutex);
1012 : 156 : }
1013 : :
1014 : : /* Sets the path cost of port 'p' based on 'speed' (measured in Mb/s). */
1015 : : void
1016 : 0 : stp_port_set_speed(struct stp_port *p, unsigned int speed)
1017 : : {
1018 : 0 : stp_port_set_path_cost(p, stp_convert_speed_to_cost(speed));
1019 : 0 : }
1020 : :
1021 : : /* Enables topology change detection on port 'p'. */
1022 : : void
1023 : 0 : stp_port_enable_change_detection(struct stp_port *p)
1024 : : {
1025 : 0 : p->change_detection_enabled = true;
1026 : 0 : }
1027 : :
1028 : : /* Disables topology change detection on port 'p'. */
1029 : : void
1030 : 0 : stp_port_disable_change_detection(struct stp_port *p)
1031 : : {
1032 : 0 : p->change_detection_enabled = false;
1033 : 0 : }
1034 : :
1035 : : static void
1036 : 15562 : stp_transmit_config(struct stp_port *p) OVS_REQUIRES(mutex)
1037 : : {
1038 : 15562 : struct stp *stp = p->stp;
1039 : 15562 : bool root = stp_is_root_bridge(stp);
1040 [ + + ][ - + ]: 15562 : if (!root && !stp->root_port) {
1041 : 0 : return;
1042 : : }
1043 [ + + ]: 15562 : if (p->hold_timer.active) {
1044 [ - + ]: 362 : VLOG_DBG_RL(&stp_rl, "bridge: %s, port: %s, transmit config bpdu pending",
1045 : : stp->name, p->port_name);
1046 : 362 : p->config_pending = true;
1047 : : } else {
1048 : : struct stp_config_bpdu config;
1049 : 15200 : memset(&config, 0, sizeof config);
1050 : 15200 : config.header.protocol_id = htons(STP_PROTOCOL_ID);
1051 : 15200 : config.header.protocol_version = STP_PROTOCOL_VERSION;
1052 : 15200 : config.header.bpdu_type = STP_TYPE_CONFIG;
1053 : 15200 : config.flags = 0;
1054 [ + + ]: 15200 : if (p->topology_change_ack) {
1055 : 49 : config.flags |= STP_CONFIG_TOPOLOGY_CHANGE_ACK;
1056 : : }
1057 [ + + ]: 15200 : if (stp->topology_change) {
1058 : 3404 : config.flags |= STP_CONFIG_TOPOLOGY_CHANGE;
1059 : : }
1060 : 15200 : config.root_id = htonll(stp->designated_root);
1061 : 15200 : config.root_path_cost = htonl(stp->root_path_cost);
1062 : 15200 : config.bridge_id = htonll(stp->bridge_id);
1063 : 15200 : config.port_id = htons(p->port_id);
1064 [ + + ]: 15200 : if (root) {
1065 : 5974 : config.message_age = htons(0);
1066 : : } else {
1067 : 9226 : config.message_age = htons(stp->root_port->message_age_timer.value
1068 : : + MESSAGE_AGE_INCREMENT);
1069 : : }
1070 : 15200 : config.max_age = htons(stp->max_age);
1071 : 15200 : config.hello_time = htons(stp->hello_time);
1072 : 15200 : config.forward_delay = htons(stp->forward_delay);
1073 [ + - ]: 15200 : if (ntohs(config.message_age) < stp->max_age) {
1074 : 15200 : p->topology_change_ack = false;
1075 : 15200 : p->config_pending = false;
1076 [ - + ]: 15200 : VLOG_DBG_RL(&stp_rl, "bridge: %s, port: %s, transmit config bpdu",
1077 : : stp->name, p->port_name);
1078 : 15200 : stp_send_bpdu(p, &config, sizeof config);
1079 : 15200 : stp_start_timer(&p->hold_timer, 0);
1080 : : }
1081 : : }
1082 : : }
1083 : :
1084 : : static bool
1085 : 10451 : stp_supersedes_port_info(const struct stp_port *p,
1086 : : const struct stp_config_bpdu *config)
1087 : : OVS_REQUIRES(mutex)
1088 : : {
1089 [ + + ]: 10451 : if (ntohll(config->root_id) != p->designated_root) {
1090 : 156 : return ntohll(config->root_id) < p->designated_root;
1091 [ + + ]: 10295 : } else if (ntohl(config->root_path_cost) != p->designated_cost) {
1092 : 65 : return ntohl(config->root_path_cost) < p->designated_cost;
1093 [ + + ]: 10230 : } else if (ntohll(config->bridge_id) != p->designated_bridge) {
1094 : 13 : return ntohll(config->bridge_id) < p->designated_bridge;
1095 : : } else {
1096 : 20434 : return (ntohll(config->bridge_id) != p->stp->bridge_id
1097 [ + + ][ + + ]: 10217 : || ntohs(config->port_id) <= p->designated_port);
1098 : : }
1099 : : }
1100 : :
1101 : : static void
1102 : 10316 : stp_record_config_information(struct stp_port *p,
1103 : : const struct stp_config_bpdu *config)
1104 : : OVS_REQUIRES(mutex)
1105 : : {
1106 : 10316 : p->designated_root = ntohll(config->root_id);
1107 : 10316 : p->designated_cost = ntohl(config->root_path_cost);
1108 : 10316 : p->designated_bridge = ntohll(config->bridge_id);
1109 : 10316 : p->designated_port = ntohs(config->port_id);
1110 : 10316 : stp_start_timer(&p->message_age_timer, ntohs(config->message_age));
1111 : 10316 : }
1112 : :
1113 : : static void
1114 : 5244 : stp_record_config_timeout_values(struct stp *stp,
1115 : : const struct stp_config_bpdu *config)
1116 : : OVS_REQUIRES(mutex)
1117 : : {
1118 : 5244 : stp->max_age = ntohs(config->max_age);
1119 : 5244 : stp->hello_time = ntohs(config->hello_time);
1120 : 5244 : stp->forward_delay = ntohs(config->forward_delay);
1121 : 5244 : stp->topology_change = config->flags & STP_CONFIG_TOPOLOGY_CHANGE;
1122 : 5244 : }
1123 : :
1124 : : static bool
1125 : 132807 : stp_is_designated_port(const struct stp_port *p) OVS_REQUIRES(mutex)
1126 : : {
1127 : 132807 : return (p->designated_bridge == p->stp->bridge_id
1128 [ + + ][ + + ]: 132807 : && p->designated_port == p->port_id);
1129 : : }
1130 : :
1131 : : static void
1132 : 7364 : stp_config_bpdu_generation(struct stp *stp) OVS_REQUIRES(mutex)
1133 : : {
1134 : : struct stp_port *p;
1135 : :
1136 [ + + ]: 32753 : FOR_EACH_ENABLED_PORT (p, stp) {
1137 [ + + ]: 25389 : if (stp_is_designated_port(p)) {
1138 : 15147 : stp_transmit_config(p);
1139 : : }
1140 : : }
1141 : 7364 : }
1142 : :
1143 : : static void
1144 : 49 : stp_transmit_tcn(struct stp *stp) OVS_REQUIRES(mutex)
1145 : : {
1146 : 49 : struct stp_port *p = stp->root_port;
1147 : : struct stp_tcn_bpdu tcn_bpdu;
1148 : :
1149 [ - + ]: 49 : if (!p) {
1150 : 0 : return;
1151 : : }
1152 [ - + ]: 49 : VLOG_DBG_RL(&stp_rl, "bridge: %s, root port: %s, transmit tcn", stp->name,
1153 : : p->port_name);
1154 : 49 : tcn_bpdu.header.protocol_id = htons(STP_PROTOCOL_ID);
1155 : 49 : tcn_bpdu.header.protocol_version = STP_PROTOCOL_VERSION;
1156 : 49 : tcn_bpdu.header.bpdu_type = STP_TYPE_TCN;
1157 : 49 : stp_send_bpdu(p, &tcn_bpdu, sizeof tcn_bpdu);
1158 : : }
1159 : :
1160 : : static void
1161 : 10470 : stp_configuration_update(struct stp *stp) OVS_REQUIRES(mutex)
1162 : : {
1163 : 10470 : stp_root_selection(stp);
1164 : 10470 : stp_designated_port_selection(stp);
1165 : 10470 : seq_change(connectivity_seq_get());
1166 : 10470 : }
1167 : :
1168 : : static bool
1169 : 13059 : stp_supersedes_root(const struct stp_port *root, const struct stp_port *p)
1170 : : OVS_REQUIRES(mutex)
1171 : : {
1172 : 13059 : int p_cost = p->designated_cost + p->path_cost;
1173 : 13059 : int root_cost = root->designated_cost + root->path_cost;
1174 : :
1175 [ + + ]: 13059 : if (p->designated_root != root->designated_root) {
1176 : 14 : return p->designated_root < root->designated_root;
1177 [ + + ]: 13045 : } else if (p_cost != root_cost) {
1178 : 2091 : return p_cost < root_cost;
1179 [ + + ]: 10954 : } else if (p->designated_bridge != root->designated_bridge) {
1180 : 5047 : return p->designated_bridge < root->designated_bridge;
1181 [ + + ]: 5907 : } else if (p->designated_port != root->designated_port) {
1182 : 5542 : return p->designated_port < root->designated_port;
1183 : : } else {
1184 : 365 : return p->port_id < root->port_id;
1185 : : }
1186 : : }
1187 : :
1188 : : static void
1189 : 10470 : stp_root_selection(struct stp *stp) OVS_REQUIRES(mutex)
1190 : : {
1191 : : struct stp_port *p, *root;
1192 : :
1193 : 10470 : root = NULL;
1194 [ + + ]: 49461 : FOR_EACH_ENABLED_PORT (p, stp) {
1195 [ + + ]: 38991 : if (stp_is_designated_port(p)
1196 [ + + ]: 23396 : || p->designated_root >= stp->bridge_id) {
1197 : 15794 : continue;
1198 : : }
1199 [ + + ][ + + ]: 23197 : if (root && !stp_supersedes_root(root, p)) {
1200 : 9919 : continue;
1201 : : }
1202 : 13278 : root = p;
1203 : : }
1204 : 10470 : stp->root_port = root;
1205 [ + + ]: 10470 : if (!root) {
1206 : 332 : stp->designated_root = stp->bridge_id;
1207 : 332 : stp->root_path_cost = 0;
1208 : : } else {
1209 : 10138 : stp->designated_root = root->designated_root;
1210 : 10138 : stp->root_path_cost = root->designated_cost + root->path_cost;
1211 : : }
1212 : 10470 : }
1213 : :
1214 : : static void
1215 : 10470 : stp_designated_port_selection(struct stp *stp) OVS_REQUIRES(mutex)
1216 : : {
1217 : : struct stp_port *p;
1218 : :
1219 [ + + ]: 49461 : FOR_EACH_ENABLED_PORT (p, stp) {
1220 [ + + ]: 38991 : if (stp_is_designated_port(p)
1221 [ + + ]: 23396 : || p->designated_root != stp->designated_root
1222 [ + - ]: 23362 : || stp->root_path_cost < p->designated_cost
1223 [ + + ]: 23362 : || (stp->root_path_cost == p->designated_cost
1224 [ + - ]: 2090 : && (stp->bridge_id < p->designated_bridge
1225 [ + + ]: 2090 : || (stp->bridge_id == p->designated_bridge
1226 [ - + ]: 454 : && p->port_id <= p->designated_port))))
1227 : : {
1228 : 15629 : stp_become_designated_port(p);
1229 : : }
1230 : : }
1231 : 10470 : }
1232 : :
1233 : : static void
1234 : 27504 : stp_become_designated_port(struct stp_port *p) OVS_REQUIRES(mutex)
1235 : : {
1236 : 27504 : struct stp *stp = p->stp;
1237 : 27504 : p->designated_root = stp->designated_root;
1238 : 27504 : p->designated_cost = stp->root_path_cost;
1239 : 27504 : p->designated_bridge = stp->bridge_id;
1240 : 27504 : p->designated_port = p->port_id;
1241 : 27504 : }
1242 : :
1243 : : static void
1244 : 10608 : stp_port_state_selection(struct stp *stp) OVS_REQUIRES(mutex)
1245 : : {
1246 : : struct stp_port *p;
1247 : :
1248 [ + + ]: 49945 : FOR_EACH_ENABLED_PORT (p, stp) {
1249 [ + + ]: 39337 : if (p == stp->root_port) {
1250 : 10138 : p->config_pending = false;
1251 : 10138 : p->topology_change_ack = false;
1252 : 10138 : stp_make_forwarding(p);
1253 [ + + ]: 29199 : } else if (stp_is_designated_port(p)) {
1254 : 15975 : stp_stop_timer(&p->message_age_timer);
1255 : 15975 : stp_make_forwarding(p);
1256 : : } else {
1257 : 13224 : p->config_pending = false;
1258 : 13224 : p->topology_change_ack = false;
1259 : 13224 : stp_make_blocking(p);
1260 : : }
1261 : : }
1262 : 10608 : }
1263 : :
1264 : : static void
1265 : 26113 : stp_make_forwarding(struct stp_port *p) OVS_REQUIRES(mutex)
1266 : : {
1267 [ + + ]: 26113 : if (p->state == STP_BLOCKING) {
1268 : 167 : stp_set_port_state(p, STP_LISTENING);
1269 : 167 : stp_start_timer(&p->forward_delay_timer, 0);
1270 : : }
1271 : 26113 : }
1272 : :
1273 : : static void
1274 : 13224 : stp_make_blocking(struct stp_port *p) OVS_REQUIRES(mutex)
1275 : : {
1276 [ + + ]: 13224 : if (!(p->state & (STP_DISABLED | STP_BLOCKING))) {
1277 [ + + ]: 50 : if (p->state & (STP_FORWARDING | STP_LEARNING)) {
1278 [ + - ]: 18 : if (p->change_detection_enabled) {
1279 : 18 : stp_topology_change_detection(p->stp);
1280 : : }
1281 : : }
1282 : 50 : stp_set_port_state(p, STP_BLOCKING);
1283 : 50 : stp_stop_timer(&p->forward_delay_timer);
1284 : : }
1285 : 13224 : }
1286 : :
1287 : : static void
1288 : 630 : stp_set_port_state(struct stp_port *p, enum stp_state state)
1289 : : OVS_REQUIRES(mutex)
1290 : : {
1291 [ + - ][ + + ]: 630 : if (state != p->state && !p->state_changed) {
1292 : 144 : p->state_changed = true;
1293 [ + + ]: 144 : if (p < p->stp->first_changed_port) {
1294 : 51 : p->stp->first_changed_port = p;
1295 : : }
1296 : 144 : seq_change(connectivity_seq_get());
1297 : : }
1298 : 630 : p->state = state;
1299 : 630 : }
1300 : :
1301 : : static void
1302 : 198 : stp_topology_change_detection(struct stp *stp) OVS_REQUIRES(mutex)
1303 : : {
1304 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
1305 : :
1306 [ + + ]: 198 : if (stp_is_root_bridge(stp)) {
1307 : 80 : stp->topology_change = true;
1308 : 80 : stp_start_timer(&stp->topology_change_timer, 0);
1309 [ + + ]: 118 : } else if (!stp->topology_change_detected) {
1310 : 46 : stp_transmit_tcn(stp);
1311 : 46 : stp_start_timer(&stp->tcn_timer, 0);
1312 : : }
1313 : 198 : stp->fdb_needs_flush = true;
1314 : 198 : stp->topology_change_detected = true;
1315 : 198 : seq_change(connectivity_seq_get());
1316 [ + - ]: 198 : VLOG_INFO_RL(&rl, "%s: detected topology change.", stp->name);
1317 : 198 : }
1318 : :
1319 : : static void
1320 : 50 : stp_topology_change_acknowledged(struct stp *stp) OVS_REQUIRES(mutex)
1321 : : {
1322 : 50 : stp->topology_change_detected = false;
1323 : 50 : stp_stop_timer(&stp->tcn_timer);
1324 : 50 : }
1325 : :
1326 : : static void
1327 : 49 : stp_acknowledge_topology_change(struct stp_port *p) OVS_REQUIRES(mutex)
1328 : : {
1329 : 49 : p->topology_change_ack = true;
1330 : 49 : stp_transmit_config(p);
1331 : 49 : }
1332 : :
1333 : : static void
1334 : 10451 : stp_received_config_bpdu(struct stp *stp, struct stp_port *p,
1335 : : const struct stp_config_bpdu *config)
1336 : : OVS_REQUIRES(mutex)
1337 : : {
1338 [ - + ]: 10451 : if (ntohs(config->message_age) >= ntohs(config->max_age)) {
1339 [ # # ]: 0 : VLOG_WARN("%s: received config BPDU with message age (%u) greater "
1340 : : "than max age (%u)",
1341 : : stp->name,
1342 : : ntohs(config->message_age), ntohs(config->max_age));
1343 : 0 : return;
1344 : : }
1345 [ + - ]: 10451 : if (p->state != STP_DISABLED) {
1346 : 10451 : bool root = stp_is_root_bridge(stp);
1347 [ + + ]: 10451 : if (stp_supersedes_port_info(p, config)) {
1348 : 10316 : stp_record_config_information(p, config);
1349 : 10316 : stp_configuration_update(stp);
1350 : 10316 : stp_port_state_selection(stp);
1351 [ + + ][ + + ]: 10316 : if (!stp_is_root_bridge(stp) && root) {
1352 : 38 : stp_stop_timer(&stp->hello_timer);
1353 [ + + ]: 38 : if (stp->topology_change_detected) {
1354 : 2 : stp_stop_timer(&stp->topology_change_timer);
1355 : 2 : stp_transmit_tcn(stp);
1356 : 2 : stp_start_timer(&stp->tcn_timer, 0);
1357 : : }
1358 : : }
1359 [ + + ]: 10316 : if (p == stp->root_port) {
1360 : 5244 : stp_record_config_timeout_values(stp, config);
1361 : 5244 : stp_config_bpdu_generation(stp);
1362 [ + + ]: 5244 : if (config->flags & STP_CONFIG_TOPOLOGY_CHANGE_ACK) {
1363 : 50 : stp_topology_change_acknowledged(stp);
1364 : : }
1365 [ + + ]: 5244 : if (config->flags & STP_CONFIG_TOPOLOGY_CHANGE) {
1366 : 10316 : stp->fdb_needs_flush = true;
1367 : : }
1368 : : }
1369 [ + + ]: 135 : } else if (stp_is_designated_port(p)) {
1370 : 124 : stp_transmit_config(p);
1371 : : }
1372 : : }
1373 : : }
1374 : :
1375 : : static void
1376 : 53 : stp_received_tcn_bpdu(struct stp *stp, struct stp_port *p)
1377 : : OVS_REQUIRES(mutex)
1378 : : {
1379 [ + - ]: 53 : if (p->state != STP_DISABLED) {
1380 [ + + ]: 53 : if (stp_is_designated_port(p)) {
1381 : 49 : stp_topology_change_detection(stp);
1382 : 49 : stp_acknowledge_topology_change(p);
1383 : : }
1384 : : }
1385 : 53 : }
1386 : :
1387 : : static void
1388 : 2113 : stp_hello_timer_expiry(struct stp *stp) OVS_REQUIRES(mutex)
1389 : : {
1390 : 2113 : stp_config_bpdu_generation(stp);
1391 : 2113 : stp_start_timer(&stp->hello_timer, 0);
1392 : 2113 : }
1393 : :
1394 : : static void
1395 : 2 : stp_message_age_timer_expiry(struct stp_port *p) OVS_REQUIRES(mutex)
1396 : : {
1397 : 2 : struct stp *stp = p->stp;
1398 : 2 : bool root = stp_is_root_bridge(stp);
1399 : :
1400 [ - + ]: 2 : VLOG_DBG_RL(&stp_rl, "bridge: %s, port: %s, message age timer expired",
1401 : : stp->name, p->port_name);
1402 : 2 : stp_become_designated_port(p);
1403 : 2 : stp_configuration_update(stp);
1404 : 2 : stp_port_state_selection(stp);
1405 [ - + ][ # # ]: 2 : if (stp_is_root_bridge(stp) && !root) {
1406 : 0 : stp->max_age = stp->bridge_max_age;
1407 : 0 : stp->hello_time = stp->bridge_hello_time;
1408 : 0 : stp->forward_delay = stp->bridge_forward_delay;
1409 : 0 : stp_topology_change_detection(stp);
1410 : 0 : stp_stop_timer(&stp->tcn_timer);
1411 : 0 : stp_config_bpdu_generation(stp);
1412 : 0 : stp_start_timer(&stp->hello_timer, 0);
1413 : : }
1414 : 2 : }
1415 : :
1416 : : static bool
1417 : 135 : stp_is_designated_for_some_port(const struct stp *stp) OVS_REQUIRES(mutex)
1418 : : {
1419 : : const struct stp_port *p;
1420 : :
1421 [ + + ]: 269 : FOR_EACH_ENABLED_PORT (p, stp) {
1422 [ + + ]: 258 : if (p->designated_bridge == stp->bridge_id) {
1423 : 124 : return true;
1424 : : }
1425 : : }
1426 : 11 : return false;
1427 : : }
1428 : :
1429 : : static void
1430 : 270 : stp_forward_delay_timer_expiry(struct stp_port *p) OVS_REQUIRES(mutex)
1431 : : {
1432 [ + + ]: 270 : if (p->state == STP_LISTENING) {
1433 : 135 : stp_set_port_state(p, STP_LEARNING);
1434 : 135 : stp_start_timer(&p->forward_delay_timer, 0);
1435 [ + - ]: 135 : } else if (p->state == STP_LEARNING) {
1436 : 135 : stp_set_port_state(p, STP_FORWARDING);
1437 [ + + ]: 135 : if (stp_is_designated_for_some_port(p->stp)) {
1438 [ + - ]: 124 : if (p->change_detection_enabled) {
1439 : 124 : stp_topology_change_detection(p->stp);
1440 : : }
1441 : : }
1442 : : }
1443 : 270 : }
1444 : :
1445 : : static void
1446 : 1 : stp_tcn_timer_expiry(struct stp *stp) OVS_REQUIRES(mutex)
1447 : : {
1448 : 1 : stp_transmit_tcn(stp);
1449 : 1 : stp_start_timer(&stp->tcn_timer, 0);
1450 : 1 : }
1451 : :
1452 : : static void
1453 : 21 : stp_topology_change_timer_expiry(struct stp *stp) OVS_REQUIRES(mutex)
1454 : : {
1455 : 21 : stp->topology_change_detected = false;
1456 : 21 : stp->topology_change = false;
1457 : 21 : }
1458 : :
1459 : : static void
1460 : 15146 : stp_hold_timer_expiry(struct stp_port *p) OVS_REQUIRES(mutex)
1461 : : {
1462 [ + + ]: 15146 : if (p->config_pending) {
1463 : 242 : stp_transmit_config(p);
1464 : : }
1465 : 15146 : }
1466 : :
1467 : : static void
1468 : 11868 : stp_initialize_port(struct stp_port *p, enum stp_state state)
1469 : : OVS_REQUIRES(mutex)
1470 : : {
1471 [ - + ]: 11868 : ovs_assert(state & (STP_DISABLED | STP_BLOCKING));
1472 : 11868 : stp_become_designated_port(p);
1473 : :
1474 [ + + ][ + - ]: 11868 : if (!p->state && state == STP_DISABLED) {
1475 : 11730 : p->state = state; /* Do not trigger state change when initializing. */
1476 : : } else {
1477 : 138 : stp_set_port_state(p, state);
1478 : : }
1479 : 11868 : p->topology_change_ack = false;
1480 : 11868 : p->config_pending = false;
1481 : 11868 : p->change_detection_enabled = true;
1482 : 11868 : p->aux = NULL;
1483 : 11868 : stp_stop_timer(&p->message_age_timer);
1484 : 11868 : stp_stop_timer(&p->forward_delay_timer);
1485 : 11868 : stp_stop_timer(&p->hold_timer);
1486 : 11868 : p->tx_count = p->rx_count = p->error_count = 0;
1487 : 11868 : }
1488 : :
1489 : : static void
1490 : 7 : stp_become_root_bridge(struct stp *stp) OVS_REQUIRES(mutex)
1491 : : {
1492 : 7 : stp->max_age = stp->bridge_max_age;
1493 : 7 : stp->hello_time = stp->bridge_hello_time;
1494 : 7 : stp->forward_delay = stp->bridge_forward_delay;
1495 : 7 : stp_topology_change_detection(stp);
1496 : 7 : stp_stop_timer(&stp->tcn_timer);
1497 : 7 : stp_config_bpdu_generation(stp);
1498 : 7 : stp_start_timer(&stp->hello_timer, 0);
1499 : 7 : }
1500 : :
1501 : : static void
1502 : 28113 : stp_start_timer(struct stp_timer *timer, int value) OVS_REQUIRES(mutex)
1503 : : {
1504 : 28113 : timer->value = value;
1505 : 28113 : timer->active = true;
1506 : 28113 : }
1507 : :
1508 : : static void
1509 : 51828 : stp_stop_timer(struct stp_timer *timer) OVS_REQUIRES(mutex)
1510 : : {
1511 : 51828 : timer->active = false;
1512 : 51828 : }
1513 : :
1514 : : static bool
1515 : 192489 : stp_timer_expired(struct stp_timer *timer, int elapsed, int timeout)
1516 : : OVS_REQUIRES(mutex)
1517 : : {
1518 [ + + ]: 192489 : if (timer->active) {
1519 : 44427 : timer->value += elapsed;
1520 [ + + ]: 44427 : if (timer->value >= timeout) {
1521 : 17553 : timer->active = false;
1522 : 17553 : return true;
1523 : : }
1524 : : }
1525 : 174936 : return false;
1526 : : }
1527 : :
1528 : : /* Returns the number of whole STP timer ticks in 'ms' milliseconds. There
1529 : : * are 256 STP timer ticks per second. */
1530 : : static int
1531 : 64406 : ms_to_timer(int ms)
1532 : : {
1533 : 64406 : return ms * 0x100 / 1000;
1534 : : }
1535 : :
1536 : : /* Returns the number of whole milliseconds in 'timer' STP timer ticks. There
1537 : : * are 256 STP timer ticks per second. */
1538 : : static int
1539 : 14462 : timer_to_ms(int timer)
1540 : : {
1541 : 14462 : return timer * 1000 / 0x100;
1542 : : }
1543 : :
1544 : : static int
1545 : 14690 : clamp(int x, int min, int max)
1546 : : {
1547 [ + + ]: 14690 : return x < min ? min : x > max ? max : x;
1548 : : }
1549 : :
1550 : : static void
1551 : 76 : stp_update_bridge_timers(struct stp *stp) OVS_REQUIRES(mutex)
1552 : : {
1553 : : int ht, ma, fd;
1554 : :
1555 : 76 : ht = clamp(stp->rq_hello_time, 1000, 10000);
1556 : 76 : ma = clamp(stp->rq_max_age, MAX(2 * (ht + 1000), 6000), 40000);
1557 : 76 : fd = clamp(stp->rq_forward_delay, ma / 2 + 1000, 30000);
1558 : :
1559 : 76 : stp->bridge_hello_time = ms_to_timer(ht);
1560 : 76 : stp->bridge_max_age = ms_to_timer(ma);
1561 : 76 : stp->bridge_forward_delay = ms_to_timer(fd);
1562 : :
1563 [ + + ]: 76 : if (stp_is_root_bridge(stp)) {
1564 : 30 : stp->max_age = stp->bridge_max_age;
1565 : 30 : stp->hello_time = stp->bridge_hello_time;
1566 : 30 : stp->forward_delay = stp->bridge_forward_delay;
1567 : : }
1568 : 76 : }
1569 : :
1570 : : static void
1571 : 15249 : stp_send_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size)
1572 : : OVS_REQUIRES(mutex)
1573 : : {
1574 : : struct eth_header *eth;
1575 : : struct llc_header *llc;
1576 : : struct dp_packet *pkt;
1577 : :
1578 : : /* Skeleton. */
1579 : 15249 : pkt = dp_packet_new(ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size);
1580 : 15249 : eth = dp_packet_put_zeros(pkt, sizeof *eth);
1581 : 15249 : llc = dp_packet_put_zeros(pkt, sizeof *llc);
1582 : 15249 : dp_packet_reset_offsets(pkt);
1583 : 15249 : dp_packet_set_l3(pkt, dp_packet_put(pkt, bpdu, bpdu_size));
1584 : :
1585 : : /* 802.2 header. */
1586 : 15249 : eth->eth_dst = eth_addr_stp;
1587 : : /* p->stp->send_bpdu() must fill in source address. */
1588 : 15249 : eth->eth_type = htons(dp_packet_size(pkt) - ETH_HEADER_LEN);
1589 : :
1590 : : /* LLC header. */
1591 : 15249 : llc->llc_dsap = STP_LLC_DSAP;
1592 : 15249 : llc->llc_ssap = STP_LLC_SSAP;
1593 : 15249 : llc->llc_cntl = STP_LLC_CNTL;
1594 : :
1595 : 15249 : p->stp->send_bpdu(pkt, stp_port_no(p), p->stp->aux);
1596 : 15249 : p->tx_count++;
1597 : 15249 : }
1598 : :
1599 : : /* Unixctl. */
1600 : :
1601 : : static struct stp *
1602 : 0 : stp_find(const char *name) OVS_REQUIRES(mutex)
1603 : : {
1604 : : struct stp *stp;
1605 : :
1606 [ # # ]: 0 : LIST_FOR_EACH (stp, node, all_stps) {
1607 [ # # ]: 0 : if (!strcmp(stp->name, name)) {
1608 : 0 : return stp;
1609 : : }
1610 : : }
1611 : 0 : return NULL;
1612 : : }
1613 : :
1614 : : static void
1615 : 0 : stp_unixctl_tcn(struct unixctl_conn *conn, int argc,
1616 : : const char *argv[], void *aux OVS_UNUSED)
1617 : : {
1618 : 0 : ovs_mutex_lock(&mutex);
1619 [ # # ]: 0 : if (argc > 1) {
1620 : 0 : struct stp *stp = stp_find(argv[1]);
1621 : :
1622 [ # # ]: 0 : if (!stp) {
1623 : 0 : unixctl_command_reply_error(conn, "no such stp object");
1624 : 0 : goto out;
1625 : : }
1626 : 0 : stp_topology_change_detection(stp);
1627 : : } else {
1628 : : struct stp *stp;
1629 : :
1630 [ # # ]: 0 : LIST_FOR_EACH (stp, node, all_stps) {
1631 : 0 : stp_topology_change_detection(stp);
1632 : : }
1633 : : }
1634 : :
1635 : 0 : unixctl_command_reply(conn, "OK");
1636 : :
1637 : : out:
1638 : 0 : ovs_mutex_unlock(&mutex);
1639 : 0 : }
|