Branch data Line data Source code
1 : : /* Copyright (c) 2013, 2014, 2015, 2016 Nicira, Inc.
2 : : *
3 : : * Licensed under the Apache License, Version 2.0 (the "License");
4 : : * you may not use this file except in compliance with the License.
5 : : * You may obtain a copy of the License at:
6 : : *
7 : : * http://www.apache.org/licenses/LICENSE-2.0
8 : : *
9 : : * Unless required by applicable law or agreed to in writing, software
10 : : * distributed under the License is distributed on an "AS IS" BASIS,
11 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : : * See the License for the specific language governing permissions and
13 : : * limitations under the License. */
14 : :
15 : : #include <config.h>
16 : : #include "bfd.h"
17 : :
18 : : #include <sys/types.h>
19 : : #include <arpa/inet.h>
20 : : #include <netinet/in_systm.h>
21 : : #include <netinet/ip.h>
22 : : #include <sys/socket.h>
23 : :
24 : : #include "byte-order.h"
25 : : #include "connectivity.h"
26 : : #include "csum.h"
27 : : #include "dp-packet.h"
28 : : #include "dpif.h"
29 : : #include "openvswitch/dynamic-string.h"
30 : : #include "flow.h"
31 : : #include "hash.h"
32 : : #include "openvswitch/hmap.h"
33 : : #include "openvswitch/list.h"
34 : : #include "netdev.h"
35 : : #include "odp-util.h"
36 : : #include "openvswitch/ofpbuf.h"
37 : : #include "ovs-thread.h"
38 : : #include "openvswitch/types.h"
39 : : #include "packets.h"
40 : : #include "poll-loop.h"
41 : : #include "random.h"
42 : : #include "seq.h"
43 : : #include "smap.h"
44 : : #include "timeval.h"
45 : : #include "unaligned.h"
46 : : #include "unixctl.h"
47 : : #include "util.h"
48 : : #include "openvswitch/vlog.h"
49 : :
50 : 1288 : VLOG_DEFINE_THIS_MODULE(bfd);
51 : :
52 : : /* XXX Finish BFD.
53 : : *
54 : : * The goal of this module is to replace CFM with something both more flexible
55 : : * and standards compliant. In service of this goal, the following needs to be
56 : : * done.
57 : : *
58 : : * - Compliance
59 : : * * Implement Demand mode.
60 : : * * Go through the RFC line by line and verify we comply.
61 : : * * Test against a hardware implementation. Preferably a popular one.
62 : : * * Delete BFD packets with nw_ttl != 255 in the datapath to prevent DOS
63 : : * attacks.
64 : : *
65 : : * - Unit tests.
66 : : *
67 : : * - Set TOS/PCP on the outer tunnel header when encapped.
68 : : *
69 : : * - Sending BFD messages should be in its own thread/process.
70 : : *
71 : : * - Scale testing. How does it operate when there are large number of bfd
72 : : * sessions? Do we ever have random flaps? What's the CPU utilization?
73 : : *
74 : : * - Rely on data traffic for liveness by using BFD demand mode.
75 : : * If we're receiving traffic on a port, we can safely assume it's up (modulo
76 : : * unidrectional failures). BFD has a demand mode in which it can stay quiet
77 : : * unless it feels the need to check the status of the port. Using this, we
78 : : * can implement a strategy in which BFD only sends control messages on dark
79 : : * interfaces.
80 : : *
81 : : * - Depending on how one interprets the spec, it appears that a BFD session
82 : : * can never change bfd.LocalDiag to "No Diagnostic". We should verify that
83 : : * this is what hardware implementations actually do. Seems like "No
84 : : * Diagnostic" should be set once a BFD session state goes UP. */
85 : :
86 : : #define BFD_VERSION 1
87 : :
88 : : enum flags {
89 : : FLAG_MULTIPOINT = 1 << 0,
90 : : FLAG_DEMAND = 1 << 1,
91 : : FLAG_AUTH = 1 << 2,
92 : : FLAG_CTL = 1 << 3,
93 : : FLAG_FINAL = 1 << 4,
94 : : FLAG_POLL = 1 << 5
95 : : };
96 : :
97 : : enum state {
98 : : STATE_ADMIN_DOWN = 0 << 6,
99 : : STATE_DOWN = 1 << 6,
100 : : STATE_INIT = 2 << 6,
101 : : STATE_UP = 3 << 6
102 : : };
103 : :
104 : : enum diag {
105 : : DIAG_NONE = 0, /* No Diagnostic. */
106 : : DIAG_EXPIRED = 1, /* Control Detection Time Expired. */
107 : : DIAG_ECHO_FAILED = 2, /* Echo Function Failed. */
108 : : DIAG_RMT_DOWN = 3, /* Neighbor Signaled Session Down. */
109 : : DIAG_FWD_RESET = 4, /* Forwarding Plane Reset. */
110 : : DIAG_PATH_DOWN = 5, /* Path Down. */
111 : : DIAG_CPATH_DOWN = 6, /* Concatenated Path Down. */
112 : : DIAG_ADMIN_DOWN = 7, /* Administratively Down. */
113 : : DIAG_RCPATH_DOWN = 8 /* Reverse Concatenated Path Down. */
114 : : };
115 : :
116 : : /* RFC 5880 Section 4.1
117 : : * 0 1 2 3
118 : : * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
119 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
120 : : * |Vers | Diag |Sta|P|F|C|A|D|M| Detect Mult | Length |
121 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
122 : : * | My Discriminator |
123 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
124 : : * | Your Discriminator |
125 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
126 : : * | Desired Min TX Interval |
127 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
128 : : * | Required Min RX Interval |
129 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
130 : : * | Required Min Echo RX Interval |
131 : : * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */
132 : : struct msg {
133 : : uint8_t vers_diag; /* Version and diagnostic. */
134 : : uint8_t flags; /* 2bit State field followed by flags. */
135 : : uint8_t mult; /* Fault detection multiplier. */
136 : : uint8_t length; /* Length of this BFD message. */
137 : : ovs_be32 my_disc; /* My discriminator. */
138 : : ovs_be32 your_disc; /* Your discriminator. */
139 : : ovs_be32 min_tx; /* Desired minimum tx interval. */
140 : : ovs_be32 min_rx; /* Required minimum rx interval. */
141 : : ovs_be32 min_rx_echo; /* Required minimum echo rx interval. */
142 : : };
143 : : BUILD_ASSERT_DECL(BFD_PACKET_LEN == sizeof(struct msg));
144 : :
145 : : #define DIAG_MASK 0x1f
146 : : #define VERS_SHIFT 5
147 : : #define STATE_MASK 0xC0
148 : : #define FLAGS_MASK 0x3f
149 : :
150 : : struct bfd {
151 : : struct hmap_node node; /* In 'all_bfds'. */
152 : : uint32_t disc; /* bfd.LocalDiscr. Key in 'all_bfds' hmap. */
153 : :
154 : : char *name; /* Name used for logging. */
155 : :
156 : : bool cpath_down; /* Concatenated Path Down. */
157 : : uint8_t mult; /* bfd.DetectMult. */
158 : :
159 : : struct netdev *netdev;
160 : : uint64_t rx_packets; /* Packets received by 'netdev'. */
161 : :
162 : : enum state state; /* bfd.SessionState. */
163 : : enum state rmt_state; /* bfd.RemoteSessionState. */
164 : :
165 : : enum diag diag; /* bfd.LocalDiag. */
166 : : enum diag rmt_diag; /* Remote diagnostic. */
167 : :
168 : : enum flags flags; /* Flags sent on messages. */
169 : : enum flags rmt_flags; /* Flags last received. */
170 : :
171 : : bool oam; /* Set tunnel OAM flag if applicable. */
172 : :
173 : : uint32_t rmt_disc; /* bfd.RemoteDiscr. */
174 : :
175 : : struct eth_addr local_eth_src; /* Local eth src address. */
176 : : struct eth_addr local_eth_dst; /* Local eth dst address. */
177 : :
178 : : struct eth_addr rmt_eth_dst; /* Remote eth dst address. */
179 : :
180 : : ovs_be32 ip_src; /* IPv4 source address. */
181 : : ovs_be32 ip_dst; /* IPv4 destination address. */
182 : :
183 : : uint16_t udp_src; /* UDP source port. */
184 : :
185 : : /* All timers in milliseconds. */
186 : : long long int rmt_min_rx; /* bfd.RemoteMinRxInterval. */
187 : : long long int rmt_min_tx; /* Remote minimum TX interval. */
188 : :
189 : : long long int cfg_min_tx; /* Configured minimum TX rate. */
190 : : long long int cfg_min_rx; /* Configured required minimum RX rate. */
191 : : long long int poll_min_tx; /* Min TX negotating in a poll sequence. */
192 : : long long int poll_min_rx; /* Min RX negotating in a poll sequence. */
193 : : long long int min_tx; /* bfd.DesiredMinTxInterval. */
194 : : long long int min_rx; /* bfd.RequiredMinRxInterval. */
195 : :
196 : : long long int last_tx; /* Last TX time. */
197 : : long long int next_tx; /* Next TX time. */
198 : : long long int detect_time; /* RFC 5880 6.8.4 Detection time. */
199 : :
200 : : bool last_forwarding; /* Last calculation of forwarding flag. */
201 : : int forwarding_override; /* Manual override of 'forwarding' status. */
202 : :
203 : : atomic_bool check_tnl_key; /* Verify tunnel key of inbound packets? */
204 : : struct ovs_refcount ref_cnt;
205 : :
206 : : /* When forward_if_rx is true, bfd_forwarding() will return
207 : : * true as long as there are incoming packets received.
208 : : * Note, forwarding_override still has higher priority. */
209 : : bool forwarding_if_rx;
210 : : long long int forwarding_if_rx_detect_time;
211 : :
212 : : /* When 'bfd->forwarding_if_rx' is set, at least one bfd control packet
213 : : * is required to be received every 100 * bfd->cfg_min_rx. If bfd
214 : : * control packet is not received within this interval, even if data
215 : : * packets are received, the bfd->forwarding will still be false. */
216 : : long long int demand_rx_bfd_time;
217 : :
218 : : /* BFD decay related variables. */
219 : : bool in_decay; /* True when bfd is in decay. */
220 : : int decay_min_rx; /* min_rx is set to decay_min_rx when */
221 : : /* in decay. */
222 : : int decay_rx_ctl; /* Count bfd packets received within decay */
223 : : /* detect interval. */
224 : : uint64_t decay_rx_packets; /* Packets received by 'netdev'. */
225 : : long long int decay_detect_time; /* Decay detection time. */
226 : :
227 : : uint64_t flap_count; /* Counts bfd forwarding flaps. */
228 : :
229 : : /* True when the variables returned by bfd_get_status() are changed
230 : : * since last check. */
231 : : bool status_changed;
232 : : };
233 : :
234 : : static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
235 : : static struct hmap all_bfds__ = HMAP_INITIALIZER(&all_bfds__);
236 : : static struct hmap *const all_bfds OVS_GUARDED_BY(mutex) = &all_bfds__;
237 : :
238 : : static void bfd_lookup_ip(const char *host_name, ovs_be32 def, ovs_be32 *ip)
239 : : OVS_REQUIRES(mutex);
240 : : static bool bfd_forwarding__(struct bfd *) OVS_REQUIRES(mutex);
241 : : static bool bfd_in_poll(const struct bfd *) OVS_REQUIRES(mutex);
242 : : static void bfd_poll(struct bfd *bfd) OVS_REQUIRES(mutex);
243 : : static const char *bfd_diag_str(enum diag) OVS_REQUIRES(mutex);
244 : : static const char *bfd_state_str(enum state) OVS_REQUIRES(mutex);
245 : : static long long int bfd_min_tx(const struct bfd *) OVS_REQUIRES(mutex);
246 : : static long long int bfd_tx_interval(const struct bfd *)
247 : : OVS_REQUIRES(mutex);
248 : : static long long int bfd_rx_interval(const struct bfd *)
249 : : OVS_REQUIRES(mutex);
250 : : static void bfd_set_next_tx(struct bfd *) OVS_REQUIRES(mutex);
251 : : static void bfd_set_state(struct bfd *, enum state, enum diag)
252 : : OVS_REQUIRES(mutex);
253 : : static uint32_t generate_discriminator(void) OVS_REQUIRES(mutex);
254 : : static void bfd_put_details(struct ds *, const struct bfd *)
255 : : OVS_REQUIRES(mutex);
256 : : static uint64_t bfd_rx_packets(const struct bfd *) OVS_REQUIRES(mutex);
257 : : static void bfd_try_decay(struct bfd *) OVS_REQUIRES(mutex);
258 : : static void bfd_decay_update(struct bfd *) OVS_REQUIRES(mutex);
259 : : static void bfd_status_changed(struct bfd *) OVS_REQUIRES(mutex);
260 : :
261 : : static void bfd_forwarding_if_rx_update(struct bfd *) OVS_REQUIRES(mutex);
262 : : static void bfd_unixctl_show(struct unixctl_conn *, int argc,
263 : : const char *argv[], void *aux OVS_UNUSED);
264 : : static void bfd_unixctl_set_forwarding_override(struct unixctl_conn *,
265 : : int argc, const char *argv[],
266 : : void *aux OVS_UNUSED);
267 : : static void log_msg(enum vlog_level, const struct msg *, const char *message,
268 : : const struct bfd *) OVS_REQUIRES(mutex);
269 : :
270 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 20);
271 : :
272 : : /* Returns true if the interface on which 'bfd' is running may be used to
273 : : * forward traffic according to the BFD session state. */
274 : : bool
275 : 20186 : bfd_forwarding(struct bfd *bfd) OVS_EXCLUDED(mutex)
276 : : {
277 : : bool ret;
278 : :
279 : 20186 : ovs_mutex_lock(&mutex);
280 : 20186 : ret = bfd_forwarding__(bfd);
281 : 20186 : ovs_mutex_unlock(&mutex);
282 : 20186 : return ret;
283 : : }
284 : :
285 : : /* When forwarding_if_rx is enabled, if there are packets received,
286 : : * updates forwarding_if_rx_detect_time. */
287 : : void
288 : 1564 : bfd_account_rx(struct bfd *bfd, const struct dpif_flow_stats *stats)
289 : : {
290 [ + - ][ + + ]: 1564 : if (stats->n_packets && bfd->forwarding_if_rx) {
291 : 484 : ovs_mutex_lock(&mutex);
292 : 484 : bfd_forwarding__(bfd);
293 : 484 : bfd_forwarding_if_rx_update(bfd);
294 : 484 : bfd_forwarding__(bfd);
295 : 484 : ovs_mutex_unlock(&mutex);
296 : : }
297 : 1564 : }
298 : :
299 : : /* Returns and resets the 'bfd->status_changed'. */
300 : : bool
301 : 20164 : bfd_check_status_change(struct bfd *bfd) OVS_EXCLUDED(mutex)
302 : : {
303 : : bool ret;
304 : :
305 : 20164 : ovs_mutex_lock(&mutex);
306 : 20164 : ret = bfd->status_changed;
307 : 20164 : bfd->status_changed = false;
308 : 20164 : ovs_mutex_unlock(&mutex);
309 : :
310 : 20164 : return ret;
311 : : }
312 : :
313 : : /* Returns a 'smap' of key value pairs representing the status of 'bfd'
314 : : * intended for the OVS database. */
315 : : void
316 : 20105 : bfd_get_status(const struct bfd *bfd, struct smap *smap)
317 : : OVS_EXCLUDED(mutex)
318 : : {
319 : 20105 : ovs_mutex_lock(&mutex);
320 [ + + ]: 20105 : smap_add(smap, "forwarding",
321 : 20105 : bfd_forwarding__(CONST_CAST(struct bfd *, bfd))
322 : : ? "true" : "false");
323 : 20105 : smap_add(smap, "state", bfd_state_str(bfd->state));
324 : 20105 : smap_add(smap, "diagnostic", bfd_diag_str(bfd->diag));
325 : 20105 : smap_add_format(smap, "flap_count", "%"PRIu64, bfd->flap_count);
326 : 20105 : smap_add(smap, "remote_state", bfd_state_str(bfd->rmt_state));
327 : 20105 : smap_add(smap, "remote_diagnostic", bfd_diag_str(bfd->rmt_diag));
328 : 20105 : ovs_mutex_unlock(&mutex);
329 : 20105 : }
330 : :
331 : : void
332 : 617 : bfd_init(void)
333 : : {
334 : 617 : unixctl_command_register("bfd/show", "[interface]", 0, 1,
335 : : bfd_unixctl_show, NULL);
336 : 617 : unixctl_command_register("bfd/set-forwarding",
337 : : "[interface] normal|false|true", 1, 2,
338 : : bfd_unixctl_set_forwarding_override, NULL);
339 : 617 : }
340 : :
341 : : /* Initializes, destroys, or reconfigures the BFD session 'bfd' (named 'name'),
342 : : * according to the database configuration contained in 'cfg'. Takes ownership
343 : : * of 'bfd', which may be NULL. Returns a BFD object which may be used as a
344 : : * handle for the session, or NULL if BFD is not enabled according to 'cfg'.
345 : : * Also returns NULL if cfg is NULL. */
346 : : struct bfd *
347 : 34526 : bfd_configure(struct bfd *bfd, const char *name, const struct smap *cfg,
348 : : struct netdev *netdev) OVS_EXCLUDED(mutex)
349 : : {
350 : : static atomic_count udp_src = ATOMIC_COUNT_INIT(0);
351 : :
352 : : int decay_min_rx;
353 : : long long int min_tx, min_rx;
354 : 34526 : bool need_poll = false;
355 : 34526 : bool cfg_min_rx_changed = false;
356 : : bool cpath_down, forwarding_if_rx;
357 : :
358 [ + + ][ + + ]: 34526 : if (!cfg || !smap_get_bool(cfg, "enable", false)) {
359 : 14530 : bfd_unref(bfd);
360 : 14530 : return NULL;
361 : : }
362 : :
363 : 19996 : ovs_mutex_lock(&mutex);
364 [ + + ]: 19996 : if (!bfd) {
365 : 234 : bfd = xzalloc(sizeof *bfd);
366 : 234 : bfd->name = xstrdup(name);
367 : 234 : bfd->forwarding_override = -1;
368 : 234 : bfd->disc = generate_discriminator();
369 : 234 : hmap_insert(all_bfds, &bfd->node, bfd->disc);
370 : :
371 : 234 : bfd->diag = DIAG_NONE;
372 : 234 : bfd->min_tx = 1000;
373 : 234 : bfd->mult = 3;
374 : 234 : ovs_refcount_init(&bfd->ref_cnt);
375 : 234 : bfd->netdev = netdev_ref(netdev);
376 : 234 : bfd->rx_packets = bfd_rx_packets(bfd);
377 : 234 : bfd->in_decay = false;
378 : 234 : bfd->flap_count = 0;
379 : :
380 : : /* RFC 5881 section 4
381 : : * The source port MUST be in the range 49152 through 65535. The same
382 : : * UDP source port number MUST be used for all BFD Control packets
383 : : * associated with a particular session. The source port number SHOULD
384 : : * be unique among all BFD sessions on the system. */
385 : 234 : bfd->udp_src = (atomic_count_inc(&udp_src) % 16384) + 49152;
386 : :
387 : 234 : bfd_set_state(bfd, STATE_DOWN, DIAG_NONE);
388 : :
389 : 234 : bfd_status_changed(bfd);
390 : : }
391 : :
392 : 19996 : bfd->oam = smap_get_bool(cfg, "oam", false);
393 : :
394 : 19996 : atomic_store_relaxed(&bfd->check_tnl_key,
395 : : smap_get_bool(cfg, "check_tnl_key", false));
396 : 19996 : min_tx = smap_get_int(cfg, "min_tx", 100);
397 : 19996 : min_tx = MAX(min_tx, 1);
398 [ + + ]: 19996 : if (bfd->cfg_min_tx != min_tx) {
399 : 237 : bfd->cfg_min_tx = min_tx;
400 [ + + ]: 237 : if (bfd->state != STATE_UP
401 [ + - ][ + + ]: 3 : || (!bfd_in_poll(bfd) && bfd->cfg_min_tx < bfd->min_tx)) {
402 : 235 : bfd->min_tx = bfd->cfg_min_tx;
403 : : }
404 : 237 : need_poll = true;
405 : : }
406 : :
407 : 19996 : min_rx = smap_get_int(cfg, "min_rx", 1000);
408 : 19996 : min_rx = MAX(min_rx, 1);
409 [ + + ]: 19996 : if (bfd->cfg_min_rx != min_rx) {
410 : 235 : bfd->cfg_min_rx = min_rx;
411 [ + + ]: 235 : if (bfd->state != STATE_UP
412 [ + - ][ - + ]: 1 : || (!bfd_in_poll(bfd) && bfd->cfg_min_rx > bfd->min_rx)) {
413 : 234 : bfd->min_rx = bfd->cfg_min_rx;
414 : : }
415 : 235 : cfg_min_rx_changed = true;
416 : 235 : need_poll = true;
417 : : }
418 : :
419 : 19996 : decay_min_rx = smap_get_int(cfg, "decay_min_rx", 0);
420 [ + + ][ + + ]: 19996 : if (bfd->decay_min_rx != decay_min_rx || cfg_min_rx_changed) {
421 [ + + ][ - + ]: 240 : if (decay_min_rx > 0 && decay_min_rx < bfd->cfg_min_rx) {
422 [ # # ]: 0 : VLOG_WARN("%s: decay_min_rx cannot be less than %lld ms",
423 : : bfd->name, bfd->cfg_min_rx);
424 : 0 : bfd->decay_min_rx = 0;
425 : : } else {
426 : 240 : bfd->decay_min_rx = decay_min_rx;
427 : : }
428 : : /* Resets decay. */
429 : 240 : bfd->in_decay = false;
430 : 240 : bfd_decay_update(bfd);
431 : 240 : need_poll = true;
432 : : }
433 : :
434 : 19996 : cpath_down = smap_get_bool(cfg, "cpath_down", false);
435 [ + + ]: 19996 : if (bfd->cpath_down != cpath_down) {
436 : 1 : bfd->cpath_down = cpath_down;
437 : 1 : bfd_set_state(bfd, bfd->state, DIAG_NONE);
438 : 1 : need_poll = true;
439 : : }
440 : :
441 : 19996 : eth_addr_from_string(smap_get_def(cfg, "bfd_local_src_mac", ""),
442 : : &bfd->local_eth_src);
443 : 19996 : eth_addr_from_string(smap_get_def(cfg, "bfd_local_dst_mac", ""),
444 : : &bfd->local_eth_dst);
445 : 19996 : eth_addr_from_string(smap_get_def(cfg, "bfd_remote_dst_mac", ""),
446 : : &bfd->rmt_eth_dst);
447 : :
448 : 19996 : bfd_lookup_ip(smap_get_def(cfg, "bfd_src_ip", ""),
449 : : htonl(0xA9FE0101) /* 169.254.1.1 */, &bfd->ip_src);
450 : 19996 : bfd_lookup_ip(smap_get_def(cfg, "bfd_dst_ip", ""),
451 : : htonl(0xA9FE0100) /* 169.254.1.0 */, &bfd->ip_dst);
452 : :
453 : 19996 : forwarding_if_rx = smap_get_bool(cfg, "forwarding_if_rx", false);
454 [ + + ]: 19996 : if (bfd->forwarding_if_rx != forwarding_if_rx) {
455 : 8 : bfd->forwarding_if_rx = forwarding_if_rx;
456 [ + + ][ + - ]: 8 : if (bfd->state == STATE_UP && bfd->forwarding_if_rx) {
457 : 3 : bfd_forwarding_if_rx_update(bfd);
458 : : } else {
459 : 5 : bfd->forwarding_if_rx_detect_time = 0;
460 : : }
461 : : }
462 : :
463 [ + + ]: 19996 : if (need_poll) {
464 : 244 : bfd_poll(bfd);
465 : : }
466 : 19996 : ovs_mutex_unlock(&mutex);
467 : 19996 : return bfd;
468 : : }
469 : :
470 : : struct bfd *
471 : 117274 : bfd_ref(const struct bfd *bfd_)
472 : : {
473 : 117274 : struct bfd *bfd = CONST_CAST(struct bfd *, bfd_);
474 [ + + ]: 117274 : if (bfd) {
475 : 80660 : ovs_refcount_ref(&bfd->ref_cnt);
476 : : }
477 : 117274 : return bfd;
478 : : }
479 : :
480 : : void
481 : 341936 : bfd_unref(struct bfd *bfd) OVS_EXCLUDED(mutex)
482 : : {
483 [ + + ][ + + ]: 341936 : if (bfd && ovs_refcount_unref_relaxed(&bfd->ref_cnt) == 1) {
484 : 234 : ovs_mutex_lock(&mutex);
485 : 234 : bfd_status_changed(bfd);
486 : 234 : hmap_remove(all_bfds, &bfd->node);
487 : 234 : netdev_close(bfd->netdev);
488 : 234 : free(bfd->name);
489 : 234 : free(bfd);
490 : 234 : ovs_mutex_unlock(&mutex);
491 : : }
492 : 341936 : }
493 : :
494 : : long long int
495 : 25410 : bfd_wait(const struct bfd *bfd) OVS_EXCLUDED(mutex)
496 : : {
497 : 25410 : long long int wake_time = bfd_wake_time(bfd);
498 : 25410 : poll_timer_wait_until(wake_time);
499 : 25410 : return wake_time;
500 : : }
501 : :
502 : : /* Returns the next wake up time. */
503 : : long long int
504 : 25410 : bfd_wake_time(const struct bfd *bfd) OVS_EXCLUDED(mutex)
505 : : {
506 : : long long int retval;
507 : :
508 [ - + ]: 25410 : if (!bfd) {
509 : 0 : return LLONG_MAX;
510 : : }
511 : :
512 : 25410 : ovs_mutex_lock(&mutex);
513 [ - + ]: 25410 : if (bfd->flags & FLAG_FINAL) {
514 : 0 : retval = 0;
515 : : } else {
516 : 25410 : retval = bfd->next_tx;
517 [ + + ]: 25410 : if (bfd->state > STATE_DOWN) {
518 : 1641 : retval = MIN(bfd->detect_time, retval);
519 : : }
520 : : }
521 : 25410 : ovs_mutex_unlock(&mutex);
522 : 25410 : return retval;
523 : : }
524 : :
525 : : void
526 : 25410 : bfd_run(struct bfd *bfd) OVS_EXCLUDED(mutex)
527 : : {
528 : : long long int now;
529 : : bool old_in_decay;
530 : :
531 : 25410 : ovs_mutex_lock(&mutex);
532 : 25410 : now = time_msec();
533 : 25410 : old_in_decay = bfd->in_decay;
534 : :
535 [ + + ][ + + ]: 25410 : if (bfd->state > STATE_DOWN && now >= bfd->detect_time) {
536 : 16 : bfd_set_state(bfd, STATE_DOWN, DIAG_EXPIRED);
537 : : }
538 : 25410 : bfd_forwarding__(bfd);
539 : :
540 : : /* Decay may only happen when state is STATE_UP, bfd->decay_min_rx is
541 : : * configured, and decay_detect_time is reached. */
542 [ + + ][ + + ]: 25410 : if (bfd->state == STATE_UP && bfd->decay_min_rx > 0
543 [ + + ]: 249 : && now >= bfd->decay_detect_time) {
544 : 38 : bfd_try_decay(bfd);
545 : : }
546 : :
547 [ + + ]: 25410 : if (bfd->min_tx != bfd->cfg_min_tx
548 [ + + ][ + + ]: 25406 : || (bfd->min_rx != bfd->cfg_min_rx && bfd->min_rx != bfd->decay_min_rx)
549 [ + + ]: 25403 : || bfd->in_decay != old_in_decay) {
550 : 17 : bfd_poll(bfd);
551 : : }
552 : 25410 : ovs_mutex_unlock(&mutex);
553 : 25410 : }
554 : :
555 : : bool
556 : 26538 : bfd_should_send_packet(const struct bfd *bfd) OVS_EXCLUDED(mutex)
557 : : {
558 : : bool ret;
559 : 26538 : ovs_mutex_lock(&mutex);
560 [ + + ][ + + ]: 26538 : ret = bfd->flags & FLAG_FINAL || time_msec() >= bfd->next_tx;
561 : 26538 : ovs_mutex_unlock(&mutex);
562 : 26538 : return ret;
563 : : }
564 : :
565 : : void
566 : 5198 : bfd_put_packet(struct bfd *bfd, struct dp_packet *p,
567 : : const struct eth_addr eth_src, bool *oam) OVS_EXCLUDED(mutex)
568 : : {
569 : : long long int min_tx, min_rx;
570 : : struct udp_header *udp;
571 : : struct eth_header *eth;
572 : : struct ip_header *ip;
573 : : struct msg *msg;
574 : :
575 : 5198 : ovs_mutex_lock(&mutex);
576 [ + + ]: 5198 : if (bfd->next_tx) {
577 : 4945 : long long int delay = time_msec() - bfd->next_tx;
578 : 4945 : long long int interval = bfd_tx_interval(bfd);
579 [ + + ]: 4945 : if (delay > interval * 3 / 2) {
580 [ + - ]: 4 : VLOG_INFO("%s: long delay of %lldms (expected %lldms) sending BFD"
581 : : " control message", bfd->name, delay, interval);
582 : : }
583 : : }
584 : :
585 : : /* RFC 5880 Section 6.5
586 : : * A BFD Control packet MUST NOT have both the Poll (P) and Final (F) bits
587 : : * set. */
588 [ + + ][ - + ]: 5198 : ovs_assert(!(bfd->flags & FLAG_POLL) || !(bfd->flags & FLAG_FINAL));
589 : :
590 : 5198 : dp_packet_reserve(p, 2); /* Properly align after the ethernet header. */
591 : 5198 : eth = dp_packet_put_uninit(p, sizeof *eth);
592 [ + - ]: 5198 : eth->eth_src = eth_addr_is_zero(bfd->local_eth_src)
593 : : ? eth_src : bfd->local_eth_src;
594 [ + - ]: 5198 : eth->eth_dst = eth_addr_is_zero(bfd->local_eth_dst)
595 : : ? eth_addr_bfd : bfd->local_eth_dst;
596 : 5198 : eth->eth_type = htons(ETH_TYPE_IP);
597 : :
598 : 5198 : ip = dp_packet_put_zeros(p, sizeof *ip);
599 : 5198 : ip->ip_ihl_ver = IP_IHL_VER(5, 4);
600 : 5198 : ip->ip_tot_len = htons(sizeof *ip + sizeof *udp + sizeof *msg);
601 : 5198 : ip->ip_ttl = MAXTTL;
602 : 5198 : ip->ip_tos = IPTOS_LOWDELAY | IPTOS_THROUGHPUT;
603 : 5198 : ip->ip_proto = IPPROTO_UDP;
604 : 5198 : put_16aligned_be32(&ip->ip_src, bfd->ip_src);
605 : 5198 : put_16aligned_be32(&ip->ip_dst, bfd->ip_dst);
606 : : /* Checksum has already been zeroed by put_zeros call. */
607 : 5198 : ip->ip_csum = csum(ip, sizeof *ip);
608 : :
609 : 5198 : udp = dp_packet_put_zeros(p, sizeof *udp);
610 : 5198 : udp->udp_src = htons(bfd->udp_src);
611 : 5198 : udp->udp_dst = htons(BFD_DEST_PORT);
612 : 5198 : udp->udp_len = htons(sizeof *udp + sizeof *msg);
613 : :
614 : 5198 : msg = dp_packet_put_uninit(p, sizeof *msg);
615 : 5198 : msg->vers_diag = (BFD_VERSION << 5) | bfd->diag;
616 : 5198 : msg->flags = (bfd->state & STATE_MASK) | bfd->flags;
617 : :
618 : 5198 : msg->mult = bfd->mult;
619 : 5198 : msg->length = BFD_PACKET_LEN;
620 : 5198 : msg->my_disc = htonl(bfd->disc);
621 : 5198 : msg->your_disc = htonl(bfd->rmt_disc);
622 : 5198 : msg->min_rx_echo = htonl(0);
623 : :
624 [ + + ]: 5198 : if (bfd_in_poll(bfd)) {
625 : 18 : min_tx = bfd->poll_min_tx;
626 : 18 : min_rx = bfd->poll_min_rx;
627 : : } else {
628 : 5180 : min_tx = bfd_min_tx(bfd);
629 : 5180 : min_rx = bfd->min_rx;
630 : : }
631 : :
632 : 5198 : msg->min_tx = htonl(min_tx * 1000);
633 : 5198 : msg->min_rx = htonl(min_rx * 1000);
634 : :
635 : 5198 : bfd->flags &= ~FLAG_FINAL;
636 : 5198 : *oam = bfd->oam;
637 : :
638 : 5198 : log_msg(VLL_DBG, msg, "Sending BFD Message", bfd);
639 : :
640 : 5198 : bfd->last_tx = time_msec();
641 : 5198 : bfd_set_next_tx(bfd);
642 : 5198 : ovs_mutex_unlock(&mutex);
643 : 5198 : }
644 : :
645 : : bool
646 : 1567 : bfd_should_process_flow(const struct bfd *bfd_, const struct flow *flow,
647 : : struct flow_wildcards *wc)
648 : : {
649 : 1567 : struct bfd *bfd = CONST_CAST(struct bfd *, bfd_);
650 : :
651 [ - + ]: 1567 : if (!eth_addr_is_zero(bfd->rmt_eth_dst)) {
652 : 0 : memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
653 : :
654 [ # # ]: 0 : if (!eth_addr_equals(bfd->rmt_eth_dst, flow->dl_dst)) {
655 : 0 : return false;
656 : : }
657 : : }
658 : :
659 [ + + ]: 1567 : if (flow->dl_type == htons(ETH_TYPE_IP)) {
660 : 1129 : memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
661 [ + - ]: 1129 : if (flow->nw_proto == IPPROTO_UDP) {
662 : 1129 : memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
663 [ + - ]: 1129 : if (flow->tp_dst == htons(BFD_DEST_PORT)) {
664 : : bool check_tnl_key;
665 : :
666 : 1129 : atomic_read_relaxed(&bfd->check_tnl_key, &check_tnl_key);
667 [ + + ]: 1129 : if (check_tnl_key) {
668 : 2 : memset(&wc->masks.tunnel.tun_id, 0xff,
669 : : sizeof wc->masks.tunnel.tun_id);
670 : 2 : return flow->tunnel.tun_id == htonll(0);
671 : : }
672 : 1129 : return true;
673 : : }
674 : : }
675 : : }
676 : 438 : return false;
677 : : }
678 : :
679 : : void
680 : 1128 : bfd_process_packet(struct bfd *bfd, const struct flow *flow,
681 : : const struct dp_packet *p) OVS_EXCLUDED(mutex)
682 : : {
683 : : uint32_t rmt_min_rx, pkt_your_disc;
684 : : enum state rmt_state;
685 : : enum flags flags;
686 : : uint8_t version;
687 : : struct msg *msg;
688 : 1128 : const uint8_t *l7 = dp_packet_get_udp_payload(p);
689 : :
690 [ - + ]: 1128 : if (!l7) {
691 : 0 : return; /* No UDP payload. */
692 : : }
693 : :
694 : : /* This function is designed to follow section RFC 5880 6.8.6 closely. */
695 : :
696 : 1128 : ovs_mutex_lock(&mutex);
697 : : /* Increments the decay rx counter. */
698 : 1128 : bfd->decay_rx_ctl++;
699 : :
700 : 1128 : bfd_forwarding__(bfd);
701 : :
702 [ - + ]: 1128 : if (flow->nw_ttl != 255) {
703 : : /* XXX Should drop in the kernel to prevent DOS. */
704 : 0 : goto out;
705 : : }
706 : :
707 : 1128 : msg = dp_packet_at(p, l7 - (uint8_t *)dp_packet_data(p), BFD_PACKET_LEN);
708 [ + + ]: 1128 : if (!msg) {
709 [ + - ]: 2 : VLOG_INFO_RL(&rl, "%s: Received too-short BFD control message (only "
710 : : "%"PRIdPTR" bytes long, at least %d required).",
711 : : bfd->name, (uint8_t *) dp_packet_tail(p) - l7,
712 : : BFD_PACKET_LEN);
713 : 2 : goto out;
714 : : }
715 : :
716 : : /* RFC 5880 Section 6.8.6
717 : : * If the Length field is greater than the payload of the encapsulating
718 : : * protocol, the packet MUST be discarded.
719 : : *
720 : : * Note that we make this check implicitly. Above we use dp_packet_at() to
721 : : * ensure that there are at least BFD_PACKET_LEN bytes in the payload of
722 : : * the encapsulating protocol. Below we require msg->length to be exactly
723 : : * BFD_PACKET_LEN bytes. */
724 : :
725 : 1126 : flags = msg->flags & FLAGS_MASK;
726 : 1126 : rmt_state = msg->flags & STATE_MASK;
727 : 1126 : version = msg->vers_diag >> VERS_SHIFT;
728 : :
729 : 1126 : log_msg(VLL_DBG, msg, "Received BFD control message", bfd);
730 : :
731 [ - + ]: 1126 : if (version != BFD_VERSION) {
732 : 0 : log_msg(VLL_WARN, msg, "Incorrect version", bfd);
733 : 0 : goto out;
734 : : }
735 : :
736 : : /* Technically this should happen after the length check. We don't support
737 : : * authentication however, so it's simpler to do the check first. */
738 [ - + ]: 1126 : if (flags & FLAG_AUTH) {
739 : 0 : log_msg(VLL_WARN, msg, "Authenticated control message with"
740 : : " authentication disabled", bfd);
741 : 0 : goto out;
742 : : }
743 : :
744 [ - + ]: 1126 : if (msg->length != BFD_PACKET_LEN) {
745 : 0 : log_msg(VLL_WARN, msg, "Unexpected length", bfd);
746 [ # # ]: 0 : if (msg->length < BFD_PACKET_LEN) {
747 : 0 : goto out;
748 : : }
749 : : }
750 : :
751 [ - + ]: 1126 : if (!msg->mult) {
752 : 0 : log_msg(VLL_WARN, msg, "Zero multiplier", bfd);
753 : 0 : goto out;
754 : : }
755 : :
756 [ - + ]: 1126 : if (flags & FLAG_MULTIPOINT) {
757 : 0 : log_msg(VLL_WARN, msg, "Unsupported multipoint flag", bfd);
758 : 0 : goto out;
759 : : }
760 : :
761 [ - + ]: 1126 : if (!msg->my_disc) {
762 : 0 : log_msg(VLL_WARN, msg, "NULL my_disc", bfd);
763 : 0 : goto out;
764 : : }
765 : :
766 : 1126 : pkt_your_disc = ntohl(msg->your_disc);
767 [ + + ]: 1126 : if (pkt_your_disc) {
768 : : /* Technically, we should use the your discriminator field to figure
769 : : * out which 'struct bfd' this packet is destined towards. That way a
770 : : * bfd session could migrate from one interface to another
771 : : * transparently. This doesn't fit in with the OVS structure very
772 : : * well, so in this respect, we are not compliant. */
773 [ - + ]: 1091 : if (pkt_your_disc != bfd->disc) {
774 : 0 : log_msg(VLL_WARN, msg, "Incorrect your_disc", bfd);
775 : 0 : goto out;
776 : : }
777 [ - + ]: 35 : } else if (rmt_state > STATE_DOWN) {
778 : 0 : log_msg(VLL_WARN, msg, "Null your_disc", bfd);
779 : 0 : goto out;
780 : : }
781 : :
782 [ + + ]: 1126 : if (bfd->rmt_state != rmt_state) {
783 : 64 : bfd_status_changed(bfd);
784 : : }
785 : :
786 : 1126 : bfd->rmt_disc = ntohl(msg->my_disc);
787 : 1126 : bfd->rmt_state = rmt_state;
788 : 1126 : bfd->rmt_flags = flags;
789 : 1126 : bfd->rmt_diag = msg->vers_diag & DIAG_MASK;
790 : :
791 [ + + ][ + - ]: 1126 : if (flags & FLAG_FINAL && bfd_in_poll(bfd)) {
792 : 18 : bfd->min_tx = bfd->poll_min_tx;
793 : 18 : bfd->min_rx = bfd->poll_min_rx;
794 : 18 : bfd->flags &= ~FLAG_POLL;
795 : 18 : log_msg(VLL_INFO, msg, "Poll sequence terminated", bfd);
796 : : }
797 : :
798 [ + + ]: 1126 : if (flags & FLAG_POLL) {
799 : : /* RFC 5880 Section 6.5
800 : : * When the other system receives a Poll, it immediately transmits a
801 : : * BFD Control packet with the Final (F) bit set, independent of any
802 : : * periodic BFD Control packets it may be sending
803 : : * (see section 6.8.7). */
804 : 18 : bfd->flags &= ~FLAG_POLL;
805 : 18 : bfd->flags |= FLAG_FINAL;
806 : : }
807 : :
808 [ + - ]: 1126 : rmt_min_rx = MAX(ntohl(msg->min_rx) / 1000, 1);
809 [ + + ]: 1126 : if (bfd->rmt_min_rx != rmt_min_rx) {
810 : 56 : bfd->rmt_min_rx = rmt_min_rx;
811 [ + + ]: 56 : if (bfd->next_tx) {
812 : 55 : bfd_set_next_tx(bfd);
813 : : }
814 : 56 : log_msg(VLL_INFO, msg, "New remote min_rx", bfd);
815 : : }
816 : :
817 [ + - ]: 1126 : bfd->rmt_min_tx = MAX(ntohl(msg->min_tx) / 1000, 1);
818 : 1126 : bfd->detect_time = bfd_rx_interval(bfd) * bfd->mult + time_msec();
819 : :
820 [ - + ]: 1126 : if (bfd->state == STATE_ADMIN_DOWN) {
821 [ # # ]: 0 : VLOG_DBG_RL(&rl, "Administratively down, dropping control message.");
822 : 0 : goto out;
823 : : }
824 : :
825 [ - + ]: 1126 : if (rmt_state == STATE_ADMIN_DOWN) {
826 [ # # ]: 0 : if (bfd->state != STATE_DOWN) {
827 : 0 : bfd_set_state(bfd, STATE_DOWN, DIAG_RMT_DOWN);
828 : : }
829 : : } else {
830 [ + + + - ]: 1126 : switch (bfd->state) {
831 : : case STATE_DOWN:
832 [ + + ]: 45 : if (rmt_state == STATE_DOWN) {
833 : 23 : bfd_set_state(bfd, STATE_INIT, bfd->diag);
834 [ + - ]: 22 : } else if (rmt_state == STATE_INIT) {
835 : 22 : bfd_set_state(bfd, STATE_UP, bfd->diag);
836 : : }
837 : 45 : break;
838 : : case STATE_INIT:
839 [ + + ]: 30 : if (rmt_state > STATE_DOWN) {
840 : 20 : bfd_set_state(bfd, STATE_UP, bfd->diag);
841 : : }
842 : 30 : break;
843 : : case STATE_UP:
844 [ + + ]: 1051 : if (rmt_state <= STATE_DOWN) {
845 : 2 : bfd_set_state(bfd, STATE_DOWN, DIAG_RMT_DOWN);
846 : 2 : log_msg(VLL_INFO, msg, "Remote signaled STATE_DOWN", bfd);
847 : : }
848 : 1051 : break;
849 : : case STATE_ADMIN_DOWN:
850 : : default:
851 : 0 : OVS_NOT_REACHED();
852 : : }
853 : : }
854 : : /* XXX: RFC 5880 Section 6.8.6 Demand mode related calculations here. */
855 : :
856 [ + + ]: 1126 : if (bfd->forwarding_if_rx) {
857 : 106 : bfd->demand_rx_bfd_time = time_msec() + 100 * bfd->cfg_min_rx;
858 : : }
859 : :
860 : : out:
861 : 1128 : bfd_forwarding__(bfd);
862 : 1128 : ovs_mutex_unlock(&mutex);
863 : : }
864 : :
865 : : /* Must be called when the netdev owned by 'bfd' should change. */
866 : : void
867 : 1 : bfd_set_netdev(struct bfd *bfd, const struct netdev *netdev)
868 : : OVS_EXCLUDED(mutex)
869 : : {
870 : 1 : ovs_mutex_lock(&mutex);
871 [ - + ]: 1 : if (bfd->netdev != netdev) {
872 : 0 : netdev_close(bfd->netdev);
873 : 0 : bfd->netdev = netdev_ref(netdev);
874 [ # # ][ # # ]: 0 : if (bfd->decay_min_rx && bfd->state == STATE_UP) {
875 : 0 : bfd_decay_update(bfd);
876 : : }
877 [ # # ][ # # ]: 0 : if (bfd->forwarding_if_rx && bfd->state == STATE_UP) {
878 : 0 : bfd_forwarding_if_rx_update(bfd);
879 : : }
880 : 0 : bfd->rx_packets = bfd_rx_packets(bfd);
881 : : }
882 : 1 : ovs_mutex_unlock(&mutex);
883 : 1 : }
884 : :
885 : :
886 : : /* Updates the forwarding flag. If override is not configured and
887 : : * the forwarding flag value changes, increments the flap count.
888 : : *
889 : : * Note this function may be called multiple times in a function
890 : : * (e.g. bfd_account_rx) before and after the bfd state or status
891 : : * change. This is to capture any forwarding flag flap. */
892 : : static bool
893 : 69450 : bfd_forwarding__(struct bfd *bfd) OVS_REQUIRES(mutex)
894 : : {
895 : 69450 : long long int now = time_msec();
896 : : bool forwarding_if_rx;
897 : 69450 : bool last_forwarding = bfd->last_forwarding;
898 : :
899 [ + + ]: 69450 : if (bfd->forwarding_override != -1) {
900 : 174 : return bfd->forwarding_override == 1;
901 : : }
902 : :
903 : 138552 : forwarding_if_rx = bfd->forwarding_if_rx
904 [ + + ]: 1620 : && bfd->forwarding_if_rx_detect_time > now
905 [ + + ][ + + ]: 70896 : && bfd->demand_rx_bfd_time > now;
906 : :
907 [ + + ]: 64884 : bfd->last_forwarding = (bfd->state == STATE_UP || forwarding_if_rx)
908 [ + - ]: 4986 : && bfd->rmt_diag != DIAG_PATH_DOWN
909 [ + + ]: 4986 : && bfd->rmt_diag != DIAG_CPATH_DOWN
910 [ + + ][ + - ]: 138552 : && bfd->rmt_diag != DIAG_RCPATH_DOWN;
911 [ + + ]: 69276 : if (bfd->last_forwarding != last_forwarding) {
912 : 58 : bfd->flap_count++;
913 : 58 : bfd_status_changed(bfd);
914 : : }
915 : 69276 : return bfd->last_forwarding;
916 : : }
917 : :
918 : : /* Helpers. */
919 : : static void
920 : 39992 : bfd_lookup_ip(const char *host_name, ovs_be32 def, ovs_be32 *addr)
921 : : {
922 [ - + ]: 39992 : if (host_name[0]) {
923 [ # # ]: 0 : if (ip_parse(host_name, addr)) {
924 : 0 : return;
925 : : }
926 [ # # ]: 0 : VLOG_ERR_RL(&rl, "\"%s\" is not a valid IP address", host_name);
927 : : }
928 : 39992 : *addr = def;
929 : : }
930 : :
931 : : static bool
932 : 5245 : bfd_in_poll(const struct bfd *bfd) OVS_REQUIRES(mutex)
933 : : {
934 : 5245 : return (bfd->flags & FLAG_POLL) != 0;
935 : : }
936 : :
937 : : static void
938 : 261 : bfd_poll(struct bfd *bfd) OVS_REQUIRES(mutex)
939 : : {
940 [ + + ][ + + ]: 261 : if (bfd->state > STATE_DOWN && !bfd_in_poll(bfd)
941 [ + - ]: 19 : && !(bfd->flags & FLAG_FINAL)) {
942 : 19 : bfd->poll_min_tx = bfd->cfg_min_tx;
943 [ + + ]: 19 : bfd->poll_min_rx = bfd->in_decay ? bfd->decay_min_rx : bfd->cfg_min_rx;
944 : 19 : bfd->flags |= FLAG_POLL;
945 : 19 : bfd->next_tx = 0;
946 [ + - ]: 19 : VLOG_INFO_RL(&rl, "%s: Initiating poll sequence", bfd->name);
947 : : }
948 : 261 : }
949 : :
950 : : static long long int
951 : 16428 : bfd_min_tx(const struct bfd *bfd) OVS_REQUIRES(mutex)
952 : : {
953 : : /* RFC 5880 Section 6.8.3
954 : : * When bfd.SessionState is not Up, the system MUST set
955 : : * bfd.DesiredMinTxInterval to a value of not less than one second
956 : : * (1,000,000 microseconds). This is intended to ensure that the
957 : : * bandwidth consumed by BFD sessions that are not Up is negligible,
958 : : * particularly in the case where a neighbor may not be running BFD. */
959 [ + + ]: 16428 : return (bfd->state == STATE_UP ? bfd->min_tx : MAX(bfd->min_tx, 1000));
960 : : }
961 : :
962 : : static long long int
963 : 10723 : bfd_tx_interval(const struct bfd *bfd) OVS_REQUIRES(mutex)
964 : : {
965 : 10723 : long long int interval = bfd_min_tx(bfd);
966 : 10723 : return MAX(interval, bfd->rmt_min_rx);
967 : : }
968 : :
969 : : static long long int
970 : 2138 : bfd_rx_interval(const struct bfd *bfd) OVS_REQUIRES(mutex)
971 : : {
972 : 2138 : return MAX(bfd->min_rx, bfd->rmt_min_tx);
973 : : }
974 : :
975 : : static void
976 : 5253 : bfd_set_next_tx(struct bfd *bfd) OVS_REQUIRES(mutex)
977 : : {
978 : 5253 : long long int interval = bfd_tx_interval(bfd);
979 : 5253 : interval -= interval * random_range(26) / 100;
980 : 5253 : bfd->next_tx = bfd->last_tx + interval;
981 : 5253 : }
982 : :
983 : : static const char *
984 : 1126 : bfd_flag_str(enum flags flags)
985 : : {
986 : 1126 : struct ds ds = DS_EMPTY_INITIALIZER;
987 : : static char flag_str[128];
988 : :
989 [ + + ]: 1126 : if (!flags) {
990 : 1055 : return "none";
991 : : }
992 : :
993 [ - + ]: 71 : if (flags & FLAG_MULTIPOINT) {
994 : 0 : ds_put_cstr(&ds, "multipoint ");
995 : : }
996 : :
997 [ - + ]: 71 : if (flags & FLAG_DEMAND) {
998 : 0 : ds_put_cstr(&ds, "demand ");
999 : : }
1000 : :
1001 [ - + ]: 71 : if (flags & FLAG_AUTH) {
1002 : 0 : ds_put_cstr(&ds, "auth ");
1003 : : }
1004 : :
1005 [ - + ]: 71 : if (flags & FLAG_CTL) {
1006 : 0 : ds_put_cstr(&ds, "ctl ");
1007 : : }
1008 : :
1009 [ + + ]: 71 : if (flags & FLAG_FINAL) {
1010 : 49 : ds_put_cstr(&ds, "final ");
1011 : : }
1012 : :
1013 [ + + ]: 71 : if (flags & FLAG_POLL) {
1014 : 22 : ds_put_cstr(&ds, "poll ");
1015 : : }
1016 : :
1017 : : /* Do not copy the trailing whitespace. */
1018 : 71 : ds_chomp(&ds, ' ');
1019 : 71 : ovs_strlcpy(flag_str, ds_cstr(&ds), sizeof flag_str);
1020 : 71 : ds_destroy(&ds);
1021 : 1126 : return flag_str;
1022 : : }
1023 : :
1024 : : static const char *
1025 : 41634 : bfd_state_str(enum state state)
1026 : : {
1027 [ + + + + : 41634 : switch (state) {
- ]
1028 : 195 : case STATE_ADMIN_DOWN: return "admin_down";
1029 : 40362 : case STATE_DOWN: return "down";
1030 : 179 : case STATE_INIT: return "init";
1031 : 898 : case STATE_UP: return "up";
1032 : 0 : default: return "invalid";
1033 : : }
1034 : : }
1035 : :
1036 : : static const char *
1037 : 41634 : bfd_diag_str(enum diag diag) {
1038 [ + + - + : 41634 : switch (diag) {
- - + - -
- ]
1039 : 41285 : case DIAG_NONE: return "No Diagnostic";
1040 : 320 : case DIAG_EXPIRED: return "Control Detection Time Expired";
1041 : 0 : case DIAG_ECHO_FAILED: return "Echo Function Failed";
1042 : 24 : case DIAG_RMT_DOWN: return "Neighbor Signaled Session Down";
1043 : 0 : case DIAG_FWD_RESET: return "Forwarding Plane Reset";
1044 : 0 : case DIAG_PATH_DOWN: return "Path Down";
1045 : 5 : case DIAG_CPATH_DOWN: return "Concatenated Path Down";
1046 : 0 : case DIAG_ADMIN_DOWN: return "Administratively Down";
1047 : 0 : case DIAG_RCPATH_DOWN: return "Reverse Concatenated Path Down";
1048 : 0 : default: return "Invalid Diagnostic";
1049 : : }
1050 : : };
1051 : :
1052 : : static void
1053 : 6400 : log_msg(enum vlog_level level, const struct msg *p, const char *message,
1054 : : const struct bfd *bfd) OVS_REQUIRES(mutex)
1055 : : {
1056 : 6400 : struct ds ds = DS_EMPTY_INITIALIZER;
1057 : :
1058 [ + + ]: 6400 : if (vlog_should_drop(&this_module, level, &rl)) {
1059 : 6324 : return;
1060 : : }
1061 : :
1062 : 228 : ds_put_format(&ds,
1063 : : "%s: %s."
1064 : : "\n\tvers:%"PRIu8" diag:\"%s\" state:%s mult:%"PRIu8
1065 : : " length:%"PRIu8
1066 : : "\n\tflags: %s"
1067 : : "\n\tmy_disc:0x%"PRIx32" your_disc:0x%"PRIx32
1068 : : "\n\tmin_tx:%"PRIu32"us (%"PRIu32"ms)"
1069 : : "\n\tmin_rx:%"PRIu32"us (%"PRIu32"ms)"
1070 : : "\n\tmin_rx_echo:%"PRIu32"us (%"PRIu32"ms)",
1071 : 76 : bfd->name, message, p->vers_diag >> VERS_SHIFT,
1072 : 76 : bfd_diag_str(p->vers_diag & DIAG_MASK),
1073 : 76 : bfd_state_str(p->flags & STATE_MASK),
1074 : 228 : p->mult, p->length, bfd_flag_str(p->flags & FLAGS_MASK),
1075 : : ntohl(p->my_disc), ntohl(p->your_disc),
1076 : 76 : ntohl(p->min_tx), ntohl(p->min_tx) / 1000,
1077 : 76 : ntohl(p->min_rx), ntohl(p->min_rx) / 1000,
1078 : 76 : ntohl(p->min_rx_echo), ntohl(p->min_rx_echo) / 1000);
1079 : 76 : bfd_put_details(&ds, bfd);
1080 [ + - ]: 76 : VLOG(level, "%s", ds_cstr(&ds));
1081 : 76 : ds_destroy(&ds);
1082 : : }
1083 : :
1084 : : static void
1085 : 318 : bfd_set_state(struct bfd *bfd, enum state state, enum diag diag)
1086 : : OVS_REQUIRES(mutex)
1087 : : {
1088 [ + + ]: 318 : if (bfd->cpath_down) {
1089 : 1 : diag = DIAG_CPATH_DOWN;
1090 : : }
1091 : :
1092 [ + + ][ + - ]: 318 : if (bfd->state != state || bfd->diag != diag) {
1093 [ + + ]: 318 : if (!VLOG_DROP_INFO(&rl)) {
1094 : 149 : struct ds ds = DS_EMPTY_INITIALIZER;
1095 : :
1096 : 149 : ds_put_format(&ds, "%s: BFD state change: %s->%s"
1097 : : " \"%s\"->\"%s\".\n",
1098 : : bfd->name, bfd_state_str(bfd->state),
1099 : : bfd_state_str(state), bfd_diag_str(bfd->diag),
1100 : : bfd_diag_str(diag));
1101 : 149 : bfd_put_details(&ds, bfd);
1102 [ + - ]: 149 : VLOG_INFO("%s", ds_cstr(&ds));
1103 : 149 : ds_destroy(&ds);
1104 : : }
1105 : :
1106 : 318 : bfd->state = state;
1107 : 318 : bfd->diag = diag;
1108 : :
1109 [ + + ]: 318 : if (bfd->state <= STATE_DOWN) {
1110 : 252 : bfd->rmt_state = STATE_DOWN;
1111 : 252 : bfd->rmt_diag = DIAG_NONE;
1112 : 252 : bfd->rmt_min_rx = 1;
1113 : 252 : bfd->rmt_flags = 0;
1114 : 252 : bfd->rmt_disc = 0;
1115 : 252 : bfd->rmt_min_tx = 0;
1116 : : /* Resets the min_rx if in_decay. */
1117 [ + + ]: 252 : if (bfd->in_decay) {
1118 : 2 : bfd->min_rx = bfd->cfg_min_rx;
1119 : 2 : bfd->in_decay = false;
1120 : : }
1121 : : }
1122 : : /* Resets the decay when state changes to STATE_UP
1123 : : * and decay_min_rx is configured. */
1124 [ + + ][ + + ]: 318 : if (bfd->state == STATE_UP && bfd->decay_min_rx) {
1125 : 3 : bfd_decay_update(bfd);
1126 : : }
1127 : :
1128 : 318 : bfd_status_changed(bfd);
1129 : : }
1130 : 318 : }
1131 : :
1132 : : static uint64_t
1133 : 553 : bfd_rx_packets(const struct bfd *bfd) OVS_REQUIRES(mutex)
1134 : : {
1135 : : struct netdev_stats stats;
1136 : :
1137 [ + - ]: 553 : if (!netdev_get_stats(bfd->netdev, &stats)) {
1138 : 553 : return stats.rx_packets;
1139 : : } else {
1140 : 553 : return 0;
1141 : : }
1142 : : }
1143 : :
1144 : : /* Decays the bfd->min_rx to bfd->decay_min_rx when 'diff' is less than
1145 : : * the 'expect' value. */
1146 : : static void
1147 : 38 : bfd_try_decay(struct bfd *bfd) OVS_REQUIRES(mutex)
1148 : : {
1149 : : int64_t diff, expect;
1150 : :
1151 : : /* The 'diff' is the difference between current interface rx_packets
1152 : : * stats and last-time check. The 'expect' is the recorded number of
1153 : : * bfd control packets received within an approximately decay_min_rx
1154 : : * (2000 ms if decay_min_rx is less than 2000 ms) interval.
1155 : : *
1156 : : * Since the update of rx_packets stats at interface happens
1157 : : * asynchronously to the bfd_rx_packets() function, the 'diff' value
1158 : : * can be jittered. Thusly, we double the decay_rx_ctl to provide
1159 : : * more wiggle room. */
1160 : 38 : diff = bfd_rx_packets(bfd) - bfd->decay_rx_packets;
1161 : 38 : expect = 2 * MAX(bfd->decay_rx_ctl, 1);
1162 : 38 : bfd->in_decay = diff <= expect ? true : false;
1163 : 38 : bfd_decay_update(bfd);
1164 : 38 : }
1165 : :
1166 : : /* Updates the rx_packets, decay_rx_ctl and decay_detect_time. */
1167 : : static void
1168 : 281 : bfd_decay_update(struct bfd * bfd) OVS_REQUIRES(mutex)
1169 : : {
1170 : 281 : bfd->decay_rx_packets = bfd_rx_packets(bfd);
1171 : 281 : bfd->decay_rx_ctl = 0;
1172 : 281 : bfd->decay_detect_time = MAX(bfd->decay_min_rx, 2000) + time_msec();
1173 : 281 : }
1174 : :
1175 : : /* Records the status change and changes the global connectivity seq. */
1176 : : static void
1177 : 910 : bfd_status_changed(struct bfd *bfd) OVS_REQUIRES(mutex)
1178 : : {
1179 : 910 : seq_change(connectivity_seq_get());
1180 : 910 : bfd->status_changed = true;
1181 : 910 : }
1182 : :
1183 : : static void
1184 : 487 : bfd_forwarding_if_rx_update(struct bfd *bfd) OVS_REQUIRES(mutex)
1185 : : {
1186 : 487 : int64_t incr = bfd_rx_interval(bfd) * bfd->mult;
1187 : 487 : bfd->forwarding_if_rx_detect_time = MAX(incr, 2000) + time_msec();
1188 : 487 : }
1189 : :
1190 : : static uint32_t
1191 : 234 : generate_discriminator(void)
1192 : : {
1193 : 234 : uint32_t disc = 0;
1194 : :
1195 : : /* RFC 5880 Section 6.8.1
1196 : : * It SHOULD be set to a random (but still unique) value to improve
1197 : : * security. The value is otherwise outside the scope of this
1198 : : * specification. */
1199 : :
1200 [ + + ]: 468 : while (!disc) {
1201 : : struct bfd *bfd;
1202 : :
1203 : : /* 'disc' is by definition random, so there's no reason to waste time
1204 : : * hashing it. */
1205 : 234 : disc = random_uint32();
1206 [ + + ][ - + ]: 522 : HMAP_FOR_EACH_IN_BUCKET (bfd, node, disc, all_bfds) {
1207 [ - + ]: 288 : if (bfd->disc == disc) {
1208 : 0 : disc = 0;
1209 : 0 : break;
1210 : : }
1211 : : }
1212 : : }
1213 : :
1214 : 234 : return disc;
1215 : : }
1216 : :
1217 : : static struct bfd *
1218 : 300 : bfd_find_by_name(const char *name) OVS_REQUIRES(mutex)
1219 : : {
1220 : : struct bfd *bfd;
1221 : :
1222 [ + + ][ - + ]: 383 : HMAP_FOR_EACH (bfd, node, all_bfds) {
1223 [ + + ]: 381 : if (!strcmp(bfd->name, name)) {
1224 : 298 : return bfd;
1225 : : }
1226 : : }
1227 : 2 : return NULL;
1228 : : }
1229 : :
1230 : : static void
1231 : 525 : bfd_put_details(struct ds *ds, const struct bfd *bfd) OVS_REQUIRES(mutex)
1232 : : {
1233 [ + + ]: 525 : ds_put_format(ds, "\tForwarding: %s\n",
1234 : 525 : bfd_forwarding__(CONST_CAST(struct bfd *, bfd))
1235 : : ? "true" : "false");
1236 : 525 : ds_put_format(ds, "\tDetect Multiplier: %d\n", bfd->mult);
1237 [ + + ]: 525 : ds_put_format(ds, "\tConcatenated Path Down: %s\n",
1238 : 525 : bfd->cpath_down ? "true" : "false");
1239 : 525 : ds_put_format(ds, "\tTX Interval: Approx %lldms\n", bfd_tx_interval(bfd));
1240 : 525 : ds_put_format(ds, "\tRX Interval: Approx %lldms\n", bfd_rx_interval(bfd));
1241 : 525 : ds_put_format(ds, "\tDetect Time: now %+lldms\n",
1242 : 525 : time_msec() - bfd->detect_time);
1243 : 525 : ds_put_format(ds, "\tNext TX Time: now %+lldms\n",
1244 : 525 : time_msec() - bfd->next_tx);
1245 : 525 : ds_put_format(ds, "\tLast TX Time: now %+lldms\n",
1246 : 525 : time_msec() - bfd->last_tx);
1247 : :
1248 : 525 : ds_put_cstr(ds, "\n");
1249 : :
1250 : 525 : ds_put_format(ds, "\tLocal Flags: %s\n", bfd_flag_str(bfd->flags));
1251 : 525 : ds_put_format(ds, "\tLocal Session State: %s\n",
1252 : : bfd_state_str(bfd->state));
1253 : 525 : ds_put_format(ds, "\tLocal Diagnostic: %s\n", bfd_diag_str(bfd->diag));
1254 : 525 : ds_put_format(ds, "\tLocal Discriminator: 0x%"PRIx32"\n", bfd->disc);
1255 : 525 : ds_put_format(ds, "\tLocal Minimum TX Interval: %lldms\n",
1256 : : bfd_min_tx(bfd));
1257 : 525 : ds_put_format(ds, "\tLocal Minimum RX Interval: %lldms\n", bfd->min_rx);
1258 : :
1259 : 525 : ds_put_cstr(ds, "\n");
1260 : :
1261 : 525 : ds_put_format(ds, "\tRemote Flags: %s\n", bfd_flag_str(bfd->rmt_flags));
1262 : 525 : ds_put_format(ds, "\tRemote Session State: %s\n",
1263 : : bfd_state_str(bfd->rmt_state));
1264 : 525 : ds_put_format(ds, "\tRemote Diagnostic: %s\n",
1265 : : bfd_diag_str(bfd->rmt_diag));
1266 : 525 : ds_put_format(ds, "\tRemote Discriminator: 0x%"PRIx32"\n", bfd->rmt_disc);
1267 : 525 : ds_put_format(ds, "\tRemote Minimum TX Interval: %lldms\n",
1268 : : bfd->rmt_min_tx);
1269 : 525 : ds_put_format(ds, "\tRemote Minimum RX Interval: %lldms\n",
1270 : : bfd->rmt_min_rx);
1271 : 525 : }
1272 : :
1273 : : static void
1274 : 301 : bfd_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[],
1275 : : void *aux OVS_UNUSED) OVS_EXCLUDED(mutex)
1276 : : {
1277 : 301 : struct ds ds = DS_EMPTY_INITIALIZER;
1278 : : struct bfd *bfd;
1279 : :
1280 : 301 : ovs_mutex_lock(&mutex);
1281 [ + + ]: 301 : if (argc > 1) {
1282 : 298 : bfd = bfd_find_by_name(argv[1]);
1283 [ + + ]: 298 : if (!bfd) {
1284 : 2 : unixctl_command_reply_error(conn, "no such bfd object");
1285 : 2 : goto out;
1286 : : }
1287 : 296 : bfd_put_details(&ds, bfd);
1288 : : } else {
1289 [ + + ][ - + ]: 7 : HMAP_FOR_EACH (bfd, node, all_bfds) {
1290 : 4 : ds_put_format(&ds, "---- %s ----\n", bfd->name);
1291 : 4 : bfd_put_details(&ds, bfd);
1292 : : }
1293 : : }
1294 : 299 : unixctl_command_reply(conn, ds_cstr(&ds));
1295 : 299 : ds_destroy(&ds);
1296 : :
1297 : : out:
1298 : 301 : ovs_mutex_unlock(&mutex);
1299 : 301 : }
1300 : :
1301 : :
1302 : : static void
1303 : 2 : bfd_unixctl_set_forwarding_override(struct unixctl_conn *conn, int argc,
1304 : : const char *argv[], void *aux OVS_UNUSED)
1305 : : OVS_EXCLUDED(mutex)
1306 : : {
1307 : 2 : const char *forward_str = argv[argc - 1];
1308 : : int forwarding_override;
1309 : : struct bfd *bfd;
1310 : :
1311 : 2 : ovs_mutex_lock(&mutex);
1312 [ + + ]: 2 : if (!strcasecmp("true", forward_str)) {
1313 : 1 : forwarding_override = 1;
1314 [ - + ]: 1 : } else if (!strcasecmp("false", forward_str)) {
1315 : 0 : forwarding_override = 0;
1316 [ + - ]: 1 : } else if (!strcasecmp("normal", forward_str)) {
1317 : 1 : forwarding_override = -1;
1318 : : } else {
1319 : 0 : unixctl_command_reply_error(conn, "unknown fault string");
1320 : 0 : goto out;
1321 : : }
1322 : :
1323 [ + - ]: 2 : if (argc > 2) {
1324 : 2 : bfd = bfd_find_by_name(argv[1]);
1325 [ - + ]: 2 : if (!bfd) {
1326 : 0 : unixctl_command_reply_error(conn, "no such BFD object");
1327 : 0 : goto out;
1328 : : }
1329 : 2 : bfd->forwarding_override = forwarding_override;
1330 : 2 : bfd_status_changed(bfd);
1331 : : } else {
1332 [ # # ][ # # ]: 0 : HMAP_FOR_EACH (bfd, node, all_bfds) {
1333 : 0 : bfd->forwarding_override = forwarding_override;
1334 : 0 : bfd_status_changed(bfd);
1335 : : }
1336 : : }
1337 : :
1338 : 2 : unixctl_command_reply(conn, "OK");
1339 : :
1340 : : out:
1341 : 2 : ovs_mutex_unlock(&mutex);
1342 : 2 : }
|