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