blob: 62be1842723e0afd63689e4558ad9cac79163e48 [file] [log] [blame]
Klement Sekera0e3c0de2016-09-29 14:43:44 +02001/*
2 * Copyright (c) 2011-2016 Cisco and/or its affiliates.
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/**
16 * @file
17 * @brief BFD nodes implementation
18 */
19
20#include <vppinfra/random.h>
21#include <vppinfra/error.h>
22#include <vppinfra/hash.h>
23#include <vnet/ethernet/ethernet.h>
24#include <vnet/ethernet/packet.h>
25#include <vnet/bfd/bfd_debug.h>
26#include <vnet/bfd/bfd_protocol.h>
27#include <vnet/bfd/bfd_main.h>
28
29static u64
30bfd_us_to_clocks (bfd_main_t * bm, u64 us)
31{
32 return bm->cpu_cps * ((f64) us / USEC_PER_SECOND);
33}
34
35static vlib_node_registration_t bfd_process_node;
36
37typedef enum
38{
39#define F(t, n) BFD_OUTPUT_##t,
40 foreach_bfd_transport (F)
41#undef F
42 BFD_OUTPUT_N_NEXT,
43} bfd_output_next_t;
44
45static u32 bfd_next_index_by_transport[] = {
46#define F(t, n) [BFD_TRANSPORT_##t] = BFD_OUTPUT_##t,
47 foreach_bfd_transport (F)
48#undef F
49};
50
51/*
52 * We actually send all bfd pkts to the "error" node after scanning
53 * them, so the graph node has only one next-index. The "error-drop"
54 * node automatically bumps our per-node packet counters for us.
55 */
56typedef enum
57{
58 BFD_INPUT_NEXT_NORMAL,
59 BFD_INPUT_N_NEXT,
60} bfd_input_next_t;
61
Klement Sekerae4504c62016-12-08 10:16:41 +010062static void bfd_on_state_change (bfd_main_t * bm, bfd_session_t * bs, u64 now,
63 int handling_wakeup);
Klement Sekera0e3c0de2016-09-29 14:43:44 +020064
65static void
66bfd_set_defaults (bfd_main_t * bm, bfd_session_t * bs)
67{
68 bs->local_state = BFD_STATE_down;
69 bs->local_diag = BFD_DIAG_CODE_no_diag;
70 bs->remote_state = BFD_STATE_down;
71 bs->local_demand = 0;
72 bs->remote_discr = 0;
73 bs->desired_min_tx_us = BFD_DEFAULT_DESIRED_MIN_TX_US;
74 bs->desired_min_tx_clocks = bfd_us_to_clocks (bm, bs->desired_min_tx_us);
75 bs->remote_min_rx_us = 1;
76 bs->remote_demand = 0;
77}
78
79static void
80bfd_set_diag (bfd_session_t * bs, bfd_diag_code_e code)
81{
82 if (bs->local_diag != code)
83 {
84 BFD_DBG ("set local_diag, bs_idx=%d: '%d:%s'", bs->bs_idx, code,
85 bfd_diag_code_string (code));
86 bs->local_diag = code;
87 }
88}
89
90static void
Klement Sekerae4504c62016-12-08 10:16:41 +010091bfd_set_state (bfd_main_t * bm, bfd_session_t * bs,
92 bfd_state_e new_state, int handling_wakeup)
Klement Sekera0e3c0de2016-09-29 14:43:44 +020093{
94 if (bs->local_state != new_state)
95 {
96 BFD_DBG ("Change state, bs_idx=%d: %s->%s", bs->bs_idx,
97 bfd_state_string (bs->local_state),
98 bfd_state_string (new_state));
99 bs->local_state = new_state;
Klement Sekerae4504c62016-12-08 10:16:41 +0100100 bfd_on_state_change (bm, bs, clib_cpu_time_now (), handling_wakeup);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200101 }
102}
103
104static void
105bfd_recalc_tx_interval (bfd_main_t * bm, bfd_session_t * bs)
106{
107 if (!bs->local_demand)
108 {
109 bs->transmit_interval_clocks =
110 clib_max (bs->desired_min_tx_clocks, bs->remote_min_rx_clocks);
111 }
112 else
113 {
114 /* TODO */
115 }
116 BFD_DBG ("Recalculated transmit interval %lu clocks/%.2fs",
117 bs->transmit_interval_clocks,
118 bs->transmit_interval_clocks / bm->cpu_cps);
119}
120
121static void
122bfd_calc_next_tx (bfd_main_t * bm, bfd_session_t * bs, u64 now)
123{
124 if (!bs->local_demand)
125 {
126 if (bs->local_detect_mult > 1)
127 {
128 /* common case - 75-100% of transmit interval */
Klement Sekera3e0a3562016-12-19 09:05:21 +0100129 bs->tx_timeout_clocks = bs->last_tx_clocks +
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200130 (1 - .25 * (random_f64 (&bm->random_seed))) *
131 bs->transmit_interval_clocks;
Klement Sekera637b9c42016-12-08 05:19:14 +0100132 if (bs->tx_timeout_clocks < now)
133 {
Klement Sekera3e0a3562016-12-19 09:05:21 +0100134 /* huh, we've missed it already, transmit now */
135 BFD_DBG ("Missed %lu transmit events (now is %lu, calc "
136 "tx_timeout is %lu)",
137 (now - bs->tx_timeout_clocks) /
138 bs->transmit_interval_clocks,
139 now, bs->tx_timeout_clocks);
140 bs->tx_timeout_clocks = now;
Klement Sekera637b9c42016-12-08 05:19:14 +0100141 }
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200142 }
143 else
144 {
145 /* special case - 75-90% of transmit interval */
146 bs->tx_timeout_clocks =
Klement Sekera3e0a3562016-12-19 09:05:21 +0100147 bs->last_tx_clocks +
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200148 (.9 - .15 * (random_f64 (&bm->random_seed))) *
149 bs->transmit_interval_clocks;
Klement Sekera637b9c42016-12-08 05:19:14 +0100150 if (bs->tx_timeout_clocks < now)
151 {
Klement Sekera3e0a3562016-12-19 09:05:21 +0100152 /* huh, we've missed it already, transmit now */
153 BFD_DBG ("Missed %lu transmit events (now is %lu, calc "
154 "tx_timeout is %lu)",
155 (now - bs->tx_timeout_clocks) /
156 bs->transmit_interval_clocks,
157 now, bs->tx_timeout_clocks);
158 bs->tx_timeout_clocks = now;
Klement Sekera637b9c42016-12-08 05:19:14 +0100159 }
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200160 }
161 }
162 else
163 {
164 /* TODO */
165 }
166 if (bs->tx_timeout_clocks)
167 {
168 BFD_DBG ("Next transmit in %lu clocks/%.02fs@%lu",
169 bs->tx_timeout_clocks - now,
170 (bs->tx_timeout_clocks - now) / bm->cpu_cps,
171 bs->tx_timeout_clocks);
172 }
173}
174
175static void
176bfd_recalc_detection_time (bfd_main_t * bm, bfd_session_t * bs)
177{
178 if (!bs->local_demand)
179 {
180 bs->detection_time_clocks =
181 bs->remote_detect_mult *
182 bfd_us_to_clocks (bm, clib_max (bs->required_min_rx_us,
183 bs->remote_desired_min_tx_us));
184 }
185 else
186 {
187 bs->detection_time_clocks =
188 bs->local_detect_mult *
189 bfd_us_to_clocks (bm,
190 clib_max (bs->desired_min_tx_us,
191 bs->remote_min_rx_us));
192 }
193 BFD_DBG ("Recalculated detection time %lu clocks/%.2fs",
194 bs->detection_time_clocks,
195 bs->detection_time_clocks / bm->cpu_cps);
196}
197
198static void
Klement Sekerae4504c62016-12-08 10:16:41 +0100199bfd_set_timer (bfd_main_t * bm, bfd_session_t * bs, u64 now,
200 int handling_wakeup)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200201{
202 u64 next = 0;
203 u64 rx_timeout = 0;
204 if (BFD_STATE_up == bs->local_state)
205 {
206 rx_timeout = bs->last_rx_clocks + bs->detection_time_clocks;
207 }
208 if (bs->tx_timeout_clocks && rx_timeout)
209 {
210 next = clib_min (bs->tx_timeout_clocks, rx_timeout);
211 }
212 else if (bs->tx_timeout_clocks)
213 {
214 next = bs->tx_timeout_clocks;
215 }
216 else if (rx_timeout)
217 {
218 next = rx_timeout;
219 }
220 BFD_DBG ("bs_idx=%u, tx_timeout=%lu, rx_timeout=%lu, next=%s", bs->bs_idx,
221 bs->tx_timeout_clocks, rx_timeout,
222 next == bs->tx_timeout_clocks ? "tx" : "rx");
Klement Sekera637b9c42016-12-08 05:19:14 +0100223 /* sometimes the wheel expires an event a bit sooner than requested, account
224 for that here */
225 if (next && (now + bm->wheel_inaccuracy > bs->wheel_time_clocks ||
226 next < bs->wheel_time_clocks || !bs->wheel_time_clocks))
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200227 {
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200228 bs->wheel_time_clocks = next;
229 BFD_DBG ("timing_wheel_insert(%p, %lu (%ld clocks/%.2fs in the "
230 "future), %u);",
231 &bm->wheel, bs->wheel_time_clocks,
232 (i64) bs->wheel_time_clocks - clib_cpu_time_now (),
233 (i64) (bs->wheel_time_clocks - clib_cpu_time_now ()) /
234 bm->cpu_cps, bs->bs_idx);
235 timing_wheel_insert (&bm->wheel, bs->wheel_time_clocks, bs->bs_idx);
Klement Sekerae4504c62016-12-08 10:16:41 +0100236 if (!handling_wakeup)
237 {
238 vlib_process_signal_event (bm->vlib_main,
239 bm->bfd_process_node_index,
240 BFD_EVENT_RESCHEDULE, bs->bs_idx);
241 }
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200242 }
243}
244
245static void
246bfd_set_desired_min_tx (bfd_main_t * bm, bfd_session_t * bs, u64 now,
Klement Sekerae4504c62016-12-08 10:16:41 +0100247 u32 desired_min_tx_us, int handling_wakeup)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200248{
249 bs->desired_min_tx_us = desired_min_tx_us;
250 bs->desired_min_tx_clocks = bfd_us_to_clocks (bm, bs->desired_min_tx_us);
251 BFD_DBG ("Set desired min tx to %uus/%lu clocks/%.2fs",
252 bs->desired_min_tx_us, bs->desired_min_tx_clocks,
253 bs->desired_min_tx_clocks / bm->cpu_cps);
254 bfd_recalc_detection_time (bm, bs);
255 bfd_recalc_tx_interval (bm, bs);
256 bfd_calc_next_tx (bm, bs, now);
Klement Sekerae4504c62016-12-08 10:16:41 +0100257 bfd_set_timer (bm, bs, now, handling_wakeup);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200258}
259
Klement Sekera637b9c42016-12-08 05:19:14 +0100260static void
261bfd_set_remote_required_min_rx (bfd_main_t * bm, bfd_session_t * bs,
262 u64 now,
263 u32 remote_required_min_rx_us,
264 int handling_wakeup)
265{
266 bs->remote_min_rx_us = remote_required_min_rx_us;
267 bs->remote_min_rx_clocks = bfd_us_to_clocks (bm, bs->remote_min_rx_us);
268 BFD_DBG ("Set remote min rx to %uus/%lu clocks/%.2fs", bs->remote_min_rx_us,
269 bs->remote_min_rx_clocks, bs->remote_min_rx_clocks / bm->cpu_cps);
270 bfd_recalc_detection_time (bm, bs);
271 bfd_recalc_tx_interval (bm, bs);
272 bfd_calc_next_tx (bm, bs, now);
273 bfd_set_timer (bm, bs, now, handling_wakeup);
274}
275
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200276void
277bfd_session_start (bfd_main_t * bm, bfd_session_t * bs)
278{
279 BFD_DBG ("%U", format_bfd_session, bs);
280 bfd_recalc_tx_interval (bm, bs);
281 vlib_process_signal_event (bm->vlib_main, bm->bfd_process_node_index,
282 BFD_EVENT_NEW_SESSION, bs->bs_idx);
283}
284
285vnet_api_error_t
286bfd_del_session (uword bs_idx)
287{
288 const bfd_main_t *bm = &bfd_main;
289 if (!pool_is_free_index (bm->sessions, bs_idx))
290 {
291 bfd_session_t *bs = pool_elt_at_index (bm->sessions, bs_idx);
292 pool_put (bm->sessions, bs);
293 return 0;
294 }
295 else
296 {
297 BFD_ERR ("no such session");
298 return VNET_API_ERROR_BFD_NOENT;
299 }
300 return 0;
301}
302
303const char *
304bfd_diag_code_string (bfd_diag_code_e diag)
305{
306#define F(n, t, s) \
307 case BFD_DIAG_CODE_NAME (t): \
308 return s;
309 switch (diag)
310 {
311 foreach_bfd_diag_code (F)}
312 return "UNKNOWN";
313#undef F
314}
315
316const char *
317bfd_state_string (bfd_state_e state)
318{
319#define F(n, t, s) \
320 case BFD_STATE_NAME (t): \
321 return s;
322 switch (state)
323 {
324 foreach_bfd_state (F)}
325 return "UNKNOWN";
326#undef F
327}
328
329vnet_api_error_t
330bfd_session_set_flags (u32 bs_idx, u8 admin_up_down)
331{
332 bfd_main_t *bm = &bfd_main;
333 if (pool_is_free_index (bm->sessions, bs_idx))
334 {
335 BFD_ERR ("invalid bs_idx=%u", bs_idx);
336 return VNET_API_ERROR_BFD_NOENT;
337 }
338 bfd_session_t *bs = pool_elt_at_index (bm->sessions, bs_idx);
339 if (admin_up_down)
340 {
Klement Sekerae4504c62016-12-08 10:16:41 +0100341 bfd_set_state (bm, bs, BFD_STATE_down, 0);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200342 }
343 else
344 {
345 bfd_set_diag (bs, BFD_DIAG_CODE_neighbor_sig_down);
Klement Sekerae4504c62016-12-08 10:16:41 +0100346 bfd_set_state (bm, bs, BFD_STATE_admin_down, 0);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200347 }
348 return 0;
349}
350
351u8 *
352bfd_input_format_trace (u8 * s, va_list * args)
353{
354 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
355 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
356 const bfd_input_trace_t *t = va_arg (*args, bfd_input_trace_t *);
357 const bfd_pkt_t *pkt = (bfd_pkt_t *) t->data;
358 if (t->len > STRUCT_SIZE_OF (bfd_pkt_t, head))
359 {
360 s = format (s, "BFD v%u, diag=%u(%s), state=%u(%s),\n"
361 " flags=(P:%u, F:%u, C:%u, A:%u, D:%u, M:%u), detect_mult=%u, "
362 "length=%u\n",
363 bfd_pkt_get_version (pkt), bfd_pkt_get_diag_code (pkt),
364 bfd_diag_code_string (bfd_pkt_get_diag_code (pkt)),
365 bfd_pkt_get_state (pkt),
366 bfd_state_string (bfd_pkt_get_state (pkt)),
367 bfd_pkt_get_poll (pkt), bfd_pkt_get_final (pkt),
368 bfd_pkt_get_control_plane_independent (pkt),
369 bfd_pkt_get_auth_present (pkt), bfd_pkt_get_demand (pkt),
370 bfd_pkt_get_multipoint (pkt), pkt->head.detect_mult,
371 pkt->head.length);
372 if (t->len >= sizeof (bfd_pkt_t)
373 && pkt->head.length >= sizeof (bfd_pkt_t))
374 {
375 s = format (s, " my discriminator: %u\n", pkt->my_disc);
376 s = format (s, " your discriminator: %u\n", pkt->your_disc);
377 s = format (s, " desired min tx interval: %u\n",
378 clib_net_to_host_u32 (pkt->des_min_tx));
379 s = format (s, " required min rx interval: %u\n",
380 clib_net_to_host_u32 (pkt->req_min_rx));
381 s = format (s, " required min echo rx interval: %u\n",
382 clib_net_to_host_u32 (pkt->req_min_echo_rx));
383 }
384 }
385
386 return s;
387}
388
389static void
Klement Sekerae4504c62016-12-08 10:16:41 +0100390bfd_on_state_change (bfd_main_t * bm, bfd_session_t * bs, u64 now,
391 int handling_wakeup)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200392{
393 BFD_DBG ("State changed: %U", format_bfd_session, bs);
394 bfd_event (bm, bs);
395 switch (bs->local_state)
396 {
397 case BFD_STATE_admin_down:
398 bfd_set_desired_min_tx (bm, bs, now,
399 clib_max (bs->config_desired_min_tx_us,
Klement Sekerae4504c62016-12-08 10:16:41 +0100400 BFD_DEFAULT_DESIRED_MIN_TX_US),
401 handling_wakeup);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200402 break;
403 case BFD_STATE_down:
404 bfd_set_desired_min_tx (bm, bs, now,
405 clib_max (bs->config_desired_min_tx_us,
Klement Sekerae4504c62016-12-08 10:16:41 +0100406 BFD_DEFAULT_DESIRED_MIN_TX_US),
407 handling_wakeup);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200408 break;
409 case BFD_STATE_init:
410 bfd_set_desired_min_tx (bm, bs, now,
411 clib_max (bs->config_desired_min_tx_us,
Klement Sekerae4504c62016-12-08 10:16:41 +0100412 BFD_DEFAULT_DESIRED_MIN_TX_US),
413 handling_wakeup);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200414 break;
415 case BFD_STATE_up:
Klement Sekerae4504c62016-12-08 10:16:41 +0100416 bfd_set_desired_min_tx (bm, bs, now, bs->config_desired_min_tx_us,
417 handling_wakeup);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200418 break;
419 }
420}
421
422static void
423bfd_add_transport_layer (vlib_main_t * vm, vlib_buffer_t * b,
424 bfd_session_t * bs)
425{
426 switch (bs->transport)
427 {
428 case BFD_TRANSPORT_UDP4:
429 /* fallthrough */
430 case BFD_TRANSPORT_UDP6:
431 BFD_DBG ("Transport bfd via udp, bs_idx=%u", bs->bs_idx);
432 bfd_add_udp_transport (vm, b, &bs->udp);
433 break;
434 }
435}
436
437static vlib_buffer_t *
438bfd_create_frame (vlib_main_t * vm, vlib_node_runtime_t * rt,
439 bfd_session_t * bs)
440{
441 u32 bi;
442 if (vlib_buffer_alloc (vm, &bi, 1) != 1)
443 {
444 clib_warning ("buffer allocation failure");
445 return NULL;
446 }
447
448 vlib_buffer_t *b = vlib_get_buffer (vm, bi);
449 ASSERT (b->current_data == 0);
450
451 u32 *to_next;
452 u32 n_left_to_next;
453
454 vlib_get_next_frame (vm, rt, bfd_next_index_by_transport[bs->transport],
455 to_next, n_left_to_next);
456
457 to_next[0] = bi;
458 n_left_to_next -= 1;
459
460 vlib_put_next_frame (vm, rt, bfd_next_index_by_transport[bs->transport],
461 n_left_to_next);
462 return b;
463}
464
465static void
466bfd_init_control_frame (vlib_buffer_t * b, bfd_session_t * bs)
467{
468 bfd_pkt_t *pkt = vlib_buffer_get_current (b);
469 const u32 bfd_length = 24;
470 memset (pkt, 0, sizeof (*pkt));
471
472 bfd_pkt_set_version (pkt, 1);
473 bfd_pkt_set_diag_code (pkt, bs->local_diag);
474 bfd_pkt_set_state (pkt, bs->local_state);
475 if (bs->local_demand && BFD_STATE_up == bs->local_state &&
476 BFD_STATE_up == bs->remote_state)
477 {
478 bfd_pkt_set_demand (pkt);
479 }
480 pkt->head.detect_mult = bs->local_detect_mult;
481 pkt->head.length = clib_host_to_net_u32 (bfd_length);
482 pkt->my_disc = bs->local_discr;
483 pkt->your_disc = bs->remote_discr;
484 pkt->des_min_tx = clib_host_to_net_u32 (bs->desired_min_tx_us);
485 pkt->req_min_rx = clib_host_to_net_u32 (bs->required_min_rx_us);
Klement Sekera3e0a3562016-12-19 09:05:21 +0100486 pkt->req_min_echo_rx = clib_host_to_net_u32 (bs->required_min_echo_rx_us);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200487 b->current_length = bfd_length;
488}
489
490static void
491bfd_send_periodic (vlib_main_t * vm, vlib_node_runtime_t * rt,
Klement Sekerae4504c62016-12-08 10:16:41 +0100492 bfd_main_t * bm, bfd_session_t * bs, u64 now,
493 int handling_wakeup)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200494{
495 if (!bs->remote_min_rx_us)
496 {
497 BFD_DBG
498 ("bfd.RemoteMinRxInterval is zero, not sending periodic control "
499 "frame");
500 return;
501 }
502 /* FIXME
503 A system MUST NOT periodically transmit BFD Control packets if Demand
504 mode is active on the remote system (bfd.RemoteDemandMode is 1,
505 bfd.SessionState is Up, and bfd.RemoteSessionState is Up) and a Poll
506 Sequence is not being transmitted.
507 */
Klement Sekera637b9c42016-12-08 05:19:14 +0100508 /* sometimes the wheel expires an event a bit sooner than requested, account
509 for that here */
510 if (now + bm->wheel_inaccuracy >= bs->tx_timeout_clocks)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200511 {
512 BFD_DBG ("Send periodic control frame for bs_idx=%lu", bs->bs_idx);
513 vlib_buffer_t *b = bfd_create_frame (vm, rt, bs);
514 if (!b)
515 {
516 return;
517 }
518 bfd_init_control_frame (b, bs);
519 bfd_add_transport_layer (vm, b, bs);
Klement Sekera3e0a3562016-12-19 09:05:21 +0100520 bs->last_tx_clocks = now;
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200521 bfd_calc_next_tx (bm, bs, now);
522 }
523 else
524 {
Klement Sekera637b9c42016-12-08 05:19:14 +0100525 BFD_DBG
526 ("No need to send control frame now, now is %lu, tx_timeout is %lu",
527 now, bs->tx_timeout_clocks);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200528 }
Klement Sekerae4504c62016-12-08 10:16:41 +0100529 bfd_set_timer (bm, bs, now, handling_wakeup);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200530}
531
532void
533bfd_send_final (vlib_main_t * vm, vlib_buffer_t * b, bfd_session_t * bs)
534{
535 BFD_DBG ("Send final control frame for bs_idx=%lu", bs->bs_idx);
536 bfd_init_control_frame (b, bs);
537 bfd_pkt_set_final (vlib_buffer_get_current (b));
538 bfd_add_transport_layer (vm, b, bs);
Klement Sekera3e0a3562016-12-19 09:05:21 +0100539 bs->last_tx_clocks = clib_cpu_time_now ();
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200540}
541
542static void
Klement Sekerae4504c62016-12-08 10:16:41 +0100543bfd_check_rx_timeout (bfd_main_t * bm, bfd_session_t * bs, u64 now,
544 int handling_wakeup)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200545{
Klement Sekera637b9c42016-12-08 05:19:14 +0100546 /* sometimes the wheel expires an event a bit sooner than requested, account
547 for that here */
548 if (bs->last_rx_clocks + bs->detection_time_clocks <=
549 now + bm->wheel_inaccuracy)
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200550 {
551 BFD_DBG ("Rx timeout, session goes down");
552 bfd_set_diag (bs, BFD_DIAG_CODE_det_time_exp);
Klement Sekerae4504c62016-12-08 10:16:41 +0100553 bfd_set_state (bm, bs, BFD_STATE_down, handling_wakeup);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200554 }
555}
556
557void
558bfd_on_timeout (vlib_main_t * vm, vlib_node_runtime_t * rt, bfd_main_t * bm,
559 bfd_session_t * bs, u64 now)
560{
561 BFD_DBG ("Timeout for bs_idx=%lu", bs->bs_idx);
562 switch (bs->local_state)
563 {
564 case BFD_STATE_admin_down:
565 BFD_ERR ("Unexpected timeout when in %s state",
566 bfd_state_string (bs->local_state));
567 abort ();
568 break;
569 case BFD_STATE_down:
Klement Sekerae4504c62016-12-08 10:16:41 +0100570 bfd_send_periodic (vm, rt, bm, bs, now, 1);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200571 break;
572 case BFD_STATE_init:
573 BFD_ERR ("Unexpected timeout when in %s state",
574 bfd_state_string (bs->local_state));
575 abort ();
576 break;
577 case BFD_STATE_up:
Klement Sekerae4504c62016-12-08 10:16:41 +0100578 bfd_check_rx_timeout (bm, bs, now, 1);
579 bfd_send_periodic (vm, rt, bm, bs, now, 1);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200580 break;
581 }
582}
583
584/*
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200585 * bfd process node function
586 */
587static uword
588bfd_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
589{
590 bfd_main_t *bm = &bfd_main;
591 u32 *expired = 0;
592 uword event_type, *event_data = 0;
593
594 /* So we can send events to the bfd process */
595 bm->bfd_process_node_index = bfd_process_node.index;
596
597 while (1)
598 {
599 u64 now = clib_cpu_time_now ();
600 u64 next_expire = timing_wheel_next_expiring_elt_time (&bm->wheel);
601 BFD_DBG ("timing_wheel_next_expiring_elt_time(%p) returns %lu",
602 &bm->wheel, next_expire);
603 if ((i64) next_expire < 0)
604 {
605 BFD_DBG ("wait for event without timeout");
606 (void) vlib_process_wait_for_event (vm);
Klement Sekera0c1519b2016-12-08 05:03:32 +0100607 event_type = vlib_process_get_events (vm, &event_data);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200608 }
609 else
610 {
611 f64 timeout = ((i64) next_expire - (i64) now) / bm->cpu_cps;
612 BFD_DBG ("wait for event with timeout %.02f", timeout);
Klement Sekera0c1519b2016-12-08 05:03:32 +0100613 if (timeout < 0)
614 {
615 BFD_DBG ("negative timeout, already expired, skipping wait");
616 event_type = ~0;
617 }
618 else
619 {
620 (void) vlib_process_wait_for_event_or_clock (vm, timeout);
621 event_type = vlib_process_get_events (vm, &event_data);
622 }
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200623 }
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200624 now = clib_cpu_time_now ();
625 switch (event_type)
626 {
627 case ~0: /* no events => timeout */
628 /* nothing to do here */
629 break;
630 case BFD_EVENT_RESCHEDULE:
631 /* nothing to do here - reschedule is done automatically after
632 * each event or timeout */
633 break;
634 case BFD_EVENT_NEW_SESSION:
635 do
636 {
637 bfd_session_t *bs =
638 pool_elt_at_index (bm->sessions, *event_data);
Klement Sekerae4504c62016-12-08 10:16:41 +0100639 bfd_send_periodic (vm, rt, bm, bs, now, 1);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200640 }
641 while (0);
642 break;
643 default:
644 clib_warning ("BUG: event type 0x%wx", event_type);
645 break;
646 }
647 BFD_DBG ("advancing wheel, now is %lu", now);
648 BFD_DBG ("timing_wheel_advance (%p, %lu, %p, 0);", &bm->wheel, now,
649 expired);
650 expired = timing_wheel_advance (&bm->wheel, now, expired, 0);
651 BFD_DBG ("Expired %d elements", vec_len (expired));
652 u32 *p = NULL;
653 vec_foreach (p, expired)
654 {
655 const u32 bs_idx = *p;
656 if (!pool_is_free_index (bm->sessions, bs_idx))
657 {
658 bfd_session_t *bs = pool_elt_at_index (bm->sessions, bs_idx);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200659 bfd_on_timeout (vm, rt, bm, bs, now);
660 }
661 }
662 if (expired)
663 {
664 _vec_len (expired) = 0;
665 }
666 if (event_data)
667 {
668 _vec_len (event_data) = 0;
669 }
670 }
671
672 return 0;
673}
674
675/*
676 * bfd process node declaration
677 */
678/* *INDENT-OFF* */
679VLIB_REGISTER_NODE (bfd_process_node, static) = {
680 .function = bfd_process,
681 .type = VLIB_NODE_TYPE_PROCESS,
682 .name = "bfd-process",
683 .n_next_nodes = BFD_OUTPUT_N_NEXT,
684 .next_nodes =
685 {
686#define F(t, n) [BFD_OUTPUT_##t] = n,
687 foreach_bfd_transport (F)
688#undef F
689 },
690};
691/* *INDENT-ON* */
692
693static clib_error_t *
694bfd_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
695{
696 // bfd_main_t *bm = &bfd_main;
697 // vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
698 if (!(flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
699 {
700 /* TODO */
701 }
702 return 0;
703}
704
705VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (bfd_sw_interface_up_down);
706
707static clib_error_t *
708bfd_hw_interface_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
709{
710 // bfd_main_t *bm = &bfd_main;
711 if (flags & VNET_HW_INTERFACE_FLAG_LINK_UP)
712 {
713 /* TODO */
714 }
715 return 0;
716}
717
718VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (bfd_hw_interface_up_down);
719
720/*
721 * setup function
722 */
723static clib_error_t *
724bfd_main_init (vlib_main_t * vm)
725{
726 bfd_main_t *bm = &bfd_main;
727 bm->random_seed = random_default_seed ();
728 bm->vlib_main = vm;
729 bm->vnet_main = vnet_get_main ();
730 memset (&bm->wheel, 0, sizeof (bm->wheel));
731 bm->cpu_cps = 2590000000; // vm->clib_time.clocks_per_second;
732 BFD_DBG ("cps is %.2f", bm->cpu_cps);
733 const u64 now = clib_cpu_time_now ();
734 timing_wheel_init (&bm->wheel, now, bm->cpu_cps);
Klement Sekera637b9c42016-12-08 05:19:14 +0100735 bm->wheel_inaccuracy = 2 << bm->wheel.log2_clocks_per_bin;
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200736
737 return 0;
738}
739
740VLIB_INIT_FUNCTION (bfd_main_init);
741
742bfd_session_t *
743bfd_get_session (bfd_main_t * bm, bfd_transport_t t)
744{
745 bfd_session_t *result;
746 pool_get (bm->sessions, result);
Klement Sekerae4504c62016-12-08 10:16:41 +0100747 memset (result, 0, sizeof (*result));
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200748 result->bs_idx = result - bm->sessions;
749 result->transport = t;
750 result->local_discr = random_u32 (&bm->random_seed);
751 bfd_set_defaults (bm, result);
752 hash_set (bm->session_by_disc, result->local_discr, result->bs_idx);
753 return result;
754}
755
756void
757bfd_put_session (bfd_main_t * bm, bfd_session_t * bs)
758{
759 hash_unset (bm->session_by_disc, bs->local_discr);
760 pool_put (bm->sessions, bs);
761}
762
763bfd_session_t *
764bfd_find_session_by_idx (bfd_main_t * bm, uword bs_idx)
765{
766 if (!pool_is_free_index (bm->sessions, bs_idx))
767 {
768 return pool_elt_at_index (bm->sessions, bs_idx);
769 }
770 return NULL;
771}
772
773bfd_session_t *
774bfd_find_session_by_disc (bfd_main_t * bm, u32 disc)
775{
776 uword *p = hash_get (bfd_main.session_by_disc, disc);
777 if (p)
778 {
779 return pool_elt_at_index (bfd_main.sessions, *p);
780 }
781 return NULL;
782}
783
784/**
785 * @brief verify bfd packet - common checks
786 *
787 * @param pkt
788 *
789 * @return 1 if bfd packet is valid
790 */
791int
792bfd_verify_pkt_common (const bfd_pkt_t * pkt)
793{
794 if (1 != bfd_pkt_get_version (pkt))
795 {
796 BFD_ERR ("BFD verification failed - unexpected version: '%d'",
797 bfd_pkt_get_version (pkt));
798 return 0;
799 }
800 if (pkt->head.length < sizeof (bfd_pkt_t) ||
801 (bfd_pkt_get_auth_present (pkt) &&
802 pkt->head.length < sizeof (bfd_pkt_with_auth_t)))
803 {
804 BFD_ERR ("BFD verification failed - unexpected length: '%d' (auth "
805 "present: %d)",
806 pkt->head.length, bfd_pkt_get_auth_present (pkt));
807 return 0;
808 }
809 if (!pkt->head.detect_mult)
810 {
811 BFD_ERR ("BFD verification failed - unexpected detect-mult: '%d'",
812 pkt->head.detect_mult);
813 return 0;
814 }
815 if (bfd_pkt_get_multipoint (pkt))
816 {
817 BFD_ERR ("BFD verification failed - unexpected multipoint: '%d'",
818 bfd_pkt_get_multipoint (pkt));
819 return 0;
820 }
821 if (!pkt->my_disc)
822 {
823 BFD_ERR ("BFD verification failed - unexpected my-disc: '%d'",
824 pkt->my_disc);
825 return 0;
826 }
827 if (!pkt->your_disc)
828 {
829 const u8 pkt_state = bfd_pkt_get_state (pkt);
830 if (pkt_state != BFD_STATE_down && pkt_state != BFD_STATE_admin_down)
831 {
832 BFD_ERR ("BFD verification failed - unexpected state: '%s' "
833 "(your-disc is zero)", bfd_state_string (pkt_state));
834 return 0;
835 }
836 }
837 return 1;
838}
839
840/**
841 * @brief verify bfd packet - authentication
842 *
843 * @param pkt
844 *
845 * @return 1 if bfd packet is valid
846 */
847int
848bfd_verify_pkt_session (const bfd_pkt_t * pkt, u16 pkt_size,
849 const bfd_session_t * bs)
850{
851 const bfd_pkt_with_auth_t *with_auth = (bfd_pkt_with_auth_t *) pkt;
852 if (!bfd_pkt_get_auth_present (pkt))
853 {
854 if (pkt_size > sizeof (*pkt))
855 {
856 BFD_ERR ("BFD verification failed - unexpected packet size '%d' "
857 "(auth not present)", pkt_size);
858 return 0;
859 }
860 }
861 else
862 {
863 if (!with_auth->auth.type)
864 {
865 BFD_ERR ("BFD verification failed - unexpected auth type: '%d'",
866 with_auth->auth.type);
867 return 0;
868 }
869 /* TODO FIXME - implement the actual verification */
870 }
871 return 1;
872}
873
874void
875bfd_consume_pkt (bfd_main_t * bm, const bfd_pkt_t * pkt, u32 bs_idx)
876{
877 bfd_session_t *bs = bfd_find_session_by_idx (bm, bs_idx);
878 if (!bs)
879 {
880 return;
881 }
882 BFD_DBG ("Scanning bfd packet, bs_idx=%d", bs->bs_idx);
883 bs->remote_discr = pkt->my_disc;
884 bs->remote_state = bfd_pkt_get_state (pkt);
885 bs->remote_demand = bfd_pkt_get_demand (pkt);
Klement Sekera637b9c42016-12-08 05:19:14 +0100886 u64 now = clib_cpu_time_now ();
887 bs->last_rx_clocks = now;
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200888 bs->remote_desired_min_tx_us = clib_net_to_host_u32 (pkt->des_min_tx);
889 bs->remote_detect_mult = pkt->head.detect_mult;
Klement Sekera637b9c42016-12-08 05:19:14 +0100890 bfd_set_remote_required_min_rx (bm, bs, now,
891 clib_net_to_host_u32 (pkt->req_min_rx), 0);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200892 /* FIXME
893 If the Required Min Echo RX Interval field is zero, the
894 transmission of Echo packets, if any, MUST cease.
895
896 If a Poll Sequence is being transmitted by the local system and
897 the Final (F) bit in the received packet is set, the Poll Sequence
898 MUST be terminated.
899 */
900 /* FIXME 6.8.2 */
901 /* FIXME 6.8.4 */
902 if (BFD_STATE_admin_down == bs->local_state)
903 return;
904 if (BFD_STATE_admin_down == bs->remote_state)
905 {
906 bfd_set_diag (bs, BFD_DIAG_CODE_neighbor_sig_down);
Klement Sekerae4504c62016-12-08 10:16:41 +0100907 bfd_set_state (bm, bs, BFD_STATE_down, 0);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200908 }
909 else if (BFD_STATE_down == bs->local_state)
910 {
911 if (BFD_STATE_down == bs->remote_state)
912 {
Klement Sekerae4504c62016-12-08 10:16:41 +0100913 bfd_set_state (bm, bs, BFD_STATE_init, 0);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200914 }
915 else if (BFD_STATE_init == bs->remote_state)
916 {
Klement Sekerae4504c62016-12-08 10:16:41 +0100917 bfd_set_state (bm, bs, BFD_STATE_up, 0);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200918 }
919 }
920 else if (BFD_STATE_init == bs->local_state)
921 {
922 if (BFD_STATE_up == bs->remote_state ||
923 BFD_STATE_init == bs->remote_state)
924 {
Klement Sekerae4504c62016-12-08 10:16:41 +0100925 bfd_set_state (bm, bs, BFD_STATE_up, 0);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200926 }
927 }
928 else /* BFD_STATE_up == bs->local_state */
929 {
930 if (BFD_STATE_down == bs->remote_state)
931 {
932 bfd_set_diag (bs, BFD_DIAG_CODE_neighbor_sig_down);
Klement Sekerae4504c62016-12-08 10:16:41 +0100933 bfd_set_state (bm, bs, BFD_STATE_down, 0);
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200934 }
935 }
936}
937
938u8 *
939format_bfd_session (u8 * s, va_list * args)
940{
941 const bfd_session_t *bs = va_arg (*args, bfd_session_t *);
942 return format (s, "BFD(%u): bfd.SessionState=%s, "
943 "bfd.RemoteSessionState=%s, "
944 "bfd.LocalDiscr=%u, "
945 "bfd.RemoteDiscr=%u, "
946 "bfd.LocalDiag=%s, "
947 "bfd.DesiredMinTxInterval=%u, "
948 "bfd.RequiredMinRxInterval=%u, "
Klement Sekera3e0a3562016-12-19 09:05:21 +0100949 "bfd.RequiredMinEchoRxInterval=%u, "
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200950 "bfd.RemoteMinRxInterval=%u, "
951 "bfd.DemandMode=%s, "
952 "bfd.RemoteDemandMode=%s, "
953 "bfd.DetectMult=%u, ",
954 bs->bs_idx, bfd_state_string (bs->local_state),
955 bfd_state_string (bs->remote_state), bs->local_discr,
956 bs->remote_discr, bfd_diag_code_string (bs->local_diag),
957 bs->desired_min_tx_us, bs->required_min_rx_us,
Klement Sekera3e0a3562016-12-19 09:05:21 +0100958 bs->required_min_echo_rx_us, bs->remote_min_rx_us,
959 (bs->local_demand ? "yes" : "no"),
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200960 (bs->remote_demand ? "yes" : "no"), bs->local_detect_mult);
961}
962
963bfd_main_t bfd_main;
964
965/*
966 * fd.io coding-style-patch-verification: ON
967 *
968 * Local Variables:
969 * eval: (c-set-style "gnu")
970 * End:
971 */