]> git.ipfire.org Git - thirdparty/bird.git/blame - proto/bfd/bfd.c
BFD work in progress.
[thirdparty/bird.git] / proto / bfd / bfd.c
CommitLineData
6a8d3f1c
OZ
1/*
2 * BIRD -- Bidirectional Forwarding Detection (BFD)
3 *
4 * Can be freely distributed and used under the terms of the GNU GPL.
5 */
bf139664
OZ
6
7#include "bfd.h"
8
9
6a8d3f1c
OZ
10#define HASH_ID_KEY(n) n->loc_id
11#define HASH_ID_NEXT(n) n->next_id
12#define HASH_ID_EQ(a,b) (a == b)
13#define HASH_ID_FN(k) (k)
bf139664 14
6a8d3f1c
OZ
15#define HASH_IP_KEY(n) n->addr
16#define HASH_IP_NEXT(n) n->next_ip
17#define HASH_IP_EQ(a,b) ipa_equal(a,b)
18#define HASH_IP_FN(k) ipa_hash(k)
bf139664 19
6a8d3f1c 20static inline void bfd_notify_kick(struct bfd_proto *p);
bf139664 21
6a8d3f1c
OZ
22static void
23bfd_session_update_state(struct bfd_session *s, uint state, uint diag)
bf139664 24{
6a8d3f1c
OZ
25 struct bfd_proto *p = s->bfd;
26 int notify;
bf139664 27
6a8d3f1c
OZ
28 if (s->loc_state == state)
29 return;
bf139664 30
6a8d3f1c
OZ
31 //TRACE(D_EVENTS, "Session changed %I %d %d", s->addr, state, diag);
32 debug("STATE %I %d %d %d\n", s->addr, s->loc_state, state, diag);
33
34 bfd_lock_sessions(p);
35 s->loc_state = state;
36 s->loc_diag = diag;
bf139664 37
6a8d3f1c
OZ
38 notify = !NODE_VALID(&s->n);
39 if (notify)
40 add_tail(&p->notify_list, &s->n);
41 bfd_unlock_sessions(p);
bf139664 42
6a8d3f1c
OZ
43 if (notify)
44 bfd_notify_kick(p);
bf139664
OZ
45}
46
6a8d3f1c
OZ
47static void
48bfd_session_timeout(struct bfd_session *s)
bf139664 49{
6a8d3f1c
OZ
50 s->rem_state = BFD_STATE_DOWN;
51 s->rem_id = 0;
52 s->rem_min_tx_int = 0;
53 s->rem_min_rx_int = 1;
54 s->rem_demand_mode = 0;
55 s->rem_detect_mult = 0;
bf139664 56
6a8d3f1c 57 bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_TIMEOUT);
bf139664
OZ
58}
59
60static void
6a8d3f1c 61bfd_session_update_tx_interval(struct bfd_session *s)
bf139664 62{
6a8d3f1c
OZ
63 u32 tx_int = MAX(s->des_min_tx_int, s->rem_min_rx_int);
64 u32 tx_int_l = tx_int - (tx_int / 4); // 75 %
65 u32 tx_int_h = tx_int - (tx_int / 10); // 90 %
bf139664 66
6a8d3f1c
OZ
67 s->tx_timer->recurrent = tx_int_l;
68 s->tx_timer->randomize = tx_int_h - tx_int_l;
bf139664 69
6a8d3f1c
OZ
70 /* Do not set timer if no previous event */
71 if (!s->last_tx)
72 return;
bf139664 73
6a8d3f1c
OZ
74 /* Set timer relative to last tx_timer event */
75 tm2_set(s->tx_timer, s->last_tx + tx_int_l);
bf139664
OZ
76}
77
78static void
6a8d3f1c 79bfd_session_update_detection_time(struct bfd_session *s, int kick)
bf139664 80{
6a8d3f1c 81 btime timeout = (btime) MAX(s->req_min_rx_int, s->rem_min_tx_int) * s->rem_detect_mult;
bf139664 82
6a8d3f1c
OZ
83 if (kick)
84 s->last_rx = current_time();
bf139664 85
6a8d3f1c
OZ
86 if (!s->last_rx)
87 return;
bf139664 88
6a8d3f1c 89 tm2_set(s->hold_timer, s->last_rx + timeout);
bf139664
OZ
90}
91
92static void
93bfd_session_control_tx_timer(struct bfd_session *s)
94{
95 if (!s->opened)
96 goto stop;
97
98 if (s->passive && (s->rem_id == 0))
99 goto stop;
100
101 if (s->rem_demand_mode &&
102 !s->poll_active &&
103 (s->loc_state == BFD_STATE_UP) &&
104 (s->rem_state == BFD_STATE_UP))
105 goto stop;
106
107 if (s->rem_min_rx_int == 0)
108 goto stop;
109
110 /* So TX timer should run */
111 if (tm2_active(s->tx_timer))
112 return;
113
114 tm2_start(s->tx_timer, 0);
115 return;
116
117 stop:
118 tm2_stop(s->tx_timer);
119 s->last_tx = 0;
120}
121
122static void
bf139664
OZ
123bfd_session_request_poll(struct bfd_session *s, u8 request)
124{
125 s->poll_scheduled |= request;
126
127 if (s->poll_active)
128 return;
129
130 s->poll_active = s->poll_scheduled;
131 s->poll_scheduled = 0;
132 bfd_send_ctl(s->bfd, s, 0);
133}
134
6a8d3f1c 135static void
bf139664
OZ
136bfd_session_terminate_poll(struct bfd_session *s)
137{
138 u8 poll_done = s->poll_active & ~s->poll_scheduled;
139
140 if (poll_done & BFD_POLL_TX)
141 s->des_min_tx_int = s->des_min_tx_new;
142
143 if (poll_done & BFD_POLL_RX)
144 s->req_min_rx_int = s->req_min_rx_new;
145
146 s->poll_active = 0;
147
148 /* Timers are updated by caller - bfd_session_process_ctl() */
149
6a8d3f1c 150 // xxx_restart_poll();
bf139664
OZ
151}
152
153void
6a8d3f1c 154bfd_session_process_ctl(struct bfd_session *s, u8 flags, u32 old_tx_int, u32 old_rx_int)
bf139664
OZ
155{
156 if (s->poll_active && (flags & BFD_FLAG_FINAL))
157 bfd_session_terminate_poll(s);
158
6a8d3f1c 159 if ((s->des_min_tx_int != old_tx_int) || (s->rem_min_rx_int != old_rx_int))
bf139664
OZ
160 bfd_session_update_tx_interval(s);
161
162 bfd_session_update_detection_time(s, 1);
163
164 /* Update session state */
165 int next_state = 0;
166 int diag = BFD_DIAG_NOTHING;
167
168 switch (s->loc_state)
169 {
170 case BFD_STATE_ADMIN_DOWN:
171 return;
172
173 case BFD_STATE_DOWN:
174 if (s->rem_state == BFD_STATE_DOWN) next_state = BFD_STATE_INIT;
175 else if (s->rem_state == BFD_STATE_INIT) next_state = BFD_STATE_UP;
176 break;
177
178 case BFD_STATE_INIT:
179 if (s->rem_state == BFD_STATE_ADMIN_DOWN) next_state = BFD_STATE_DOWN, diag = BFD_DIAG_NEIGHBOR_DOWN;
180 else if (s->rem_state >= BFD_STATE_INIT) next_state = BFD_STATE_UP;
181 break;
182
183 case BFD_STATE_UP:
184 if (s->rem_state <= BFD_STATE_DOWN) next_state = BFD_STATE_DOWN, diag = BFD_DIAG_NEIGHBOR_DOWN;
185 break;
186 }
187
188 if (next_state)
189 bfd_session_update_state(s, next_state, diag);
190
191 bfd_session_control_tx_timer(s);
192
193 if (flags & BFD_FLAG_POLL)
6a8d3f1c 194 bfd_send_ctl(s->bfd, s, 1);
bf139664
OZ
195}
196
bf139664
OZ
197static void
198bfd_session_set_min_tx(struct bfd_session *s, u32 val)
199{
200 /* Note that des_min_tx_int <= des_min_tx_new */
201
202 if (val == s->des_min_tx_new)
203 return;
204
205 s->des_min_tx_new = val;
206
207 /* Postpone timer update if des_min_tx_int increases and the session is up */
208 if ((s->loc_state != BFD_STATE_UP) || (val < s->des_min_tx_int))
209 {
210 s->des_min_tx_int = val;
211 bfd_session_update_tx_interval(s);
212 }
213
214 bfd_session_request_poll(s, BFD_POLL_TX);
215}
216
217static void
218bfd_session_set_min_rx(struct bfd_session *s, u32 val)
219{
220 /* Note that req_min_rx_int >= req_min_rx_new */
221
222 if (val == s->req_min_rx_new)
223 return;
224
225 s->req_min_rx_new = val;
226
227 /* Postpone timer update if req_min_rx_int decreases and the session is up */
228 if ((s->loc_state != BFD_STATE_UP) || (val > s->req_min_rx_int))
229 {
230 s->req_min_rx_int = val;
231 bfd_session_update_detection_time(s, 0);
232 }
233
234 bfd_session_request_poll(s, BFD_POLL_RX);
235}
236
6a8d3f1c
OZ
237struct bfd_session *
238bfd_find_session_by_id(struct bfd_proto *p, u32 id)
239{
240 return HASH_FIND(p->session_hash_id, HASH_ID, id);
241}
242
243struct bfd_session *
244bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr)
245{
246 return HASH_FIND(p->session_hash_ip, HASH_IP, addr);
247}
248
249static void
250bfd_tx_timer_hook(timer2 *t)
251{
252 struct bfd_session *s = t->data;
253
254 s->last_tx = current_time();
255 // debug("TX %d\n", (s32) (s->last_tx TO_MS));
256 bfd_send_ctl(s->bfd, s, 0);
257}
258
259static void
260bfd_hold_timer_hook(timer2 *t)
261{
262 bfd_session_timeout(t->data);
263}
264
265static u32
266bfd_get_free_id(struct bfd_proto *p)
267{
268 u32 id;
269 for (id = random_u32(); 1; id++)
270 if (id && !bfd_find_session_by_id(p, id))
271 break;
272
273 return id;
274}
275
276static struct bfd_session *
277bfd_add_session(struct bfd_proto *p, ip_addr addr, struct bfd_session_config *opts)
278{
279 birdloop_enter(p->loop);
280
281 struct bfd_session *s = sl_alloc(p->session_slab);
282 bzero(s, sizeof(struct bfd_session));
283
284 s->addr = addr;
285 s->loc_id = bfd_get_free_id(p);
286 debug("XXX INS1 %d %d %u %I\n", p->session_hash_id.count, p->session_hash_ip.count, s->loc_id, s->addr);
287 HASH_INSERT(p->session_hash_id, HASH_ID, s);
288 debug("XXX INS2 %d %d\n", p->session_hash_id.count, p->session_hash_ip.count);
289 HASH_INSERT(p->session_hash_ip, HASH_IP, s);
290 debug("XXX INS3 %d %d\n", p->session_hash_id.count, p->session_hash_ip.count);
291 s->bfd = p;
292
293 /* Initialization of state variables - see RFC 5880 6.8.1 */
294 s->loc_state = BFD_STATE_DOWN;
295 s->rem_state = BFD_STATE_DOWN;
296 s->des_min_tx_int = s->des_min_tx_new = opts->min_tx_int; // XXX opts->idle_tx_int;
297 s->req_min_rx_int = s->req_min_rx_new = opts->min_rx_int;
298 s->rem_min_rx_int = 1;
299 s->detect_mult = opts->multiplier;
300 s->passive = opts->passive;
301
302 s->tx_timer = tm2_new_init(p->tpool, bfd_tx_timer_hook, s, 0, 0);
303 s->hold_timer = tm2_new_init(p->tpool, bfd_hold_timer_hook, s, 0, 0);
304 bfd_session_update_tx_interval(s);
305
306 birdloop_leave(p->loop);
307
308 return s;
309}
310
311static void
312bfd_open_session(struct bfd_proto *p, struct bfd_session *s, ip_addr local, struct iface *ifa)
313{
314 birdloop_enter(p->loop);
315
316 s->bsock = bfd_get_socket(p, local, ifa);
317 // s->local = local;
318 // s->iface = ifa;
319 s->opened = 1;
320
321 bfd_session_control_tx_timer(s);
322
323 birdloop_leave(p->loop);
324}
325
326static void
327bfd_close_session(struct bfd_proto *p, struct bfd_session *s)
328{
329 birdloop_enter(p->loop);
330
331 bfd_free_socket(s->bsock);
332 s->bsock = NULL;
333 // s->local = IPA_NONE;
334 // s->iface = NULL;
335 s->opened = 0;
336
337 bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_PATH_DOWN);
338 bfd_session_control_tx_timer(s);
339
340 birdloop_leave(p->loop);
341}
342
343static void
344bfd_remove_session(struct bfd_proto *p, struct bfd_session *s)
345{
346 birdloop_enter(p->loop);
347
348 bfd_free_socket(s->bsock);
349
350 rfree(s->tx_timer);
351 rfree(s->hold_timer);
352
353 debug("XXX REM1 %d %d %u %I\n", p->session_hash_id.count, p->session_hash_ip.count, s->loc_id, s->addr);
354 HASH_REMOVE(p->session_hash_id, HASH_ID, s);
355 debug("XXX REM2 %d %d\n", p->session_hash_id.count, p->session_hash_ip.count);
356 HASH_REMOVE(p->session_hash_ip, HASH_IP, s);
357 debug("XXX REM3 %d %d\n", p->session_hash_id.count, p->session_hash_ip.count);
358
359 sl_free(p->session_slab, s);
360
361 birdloop_leave(p->loop);
362}
363
364static void
365bfd_configure_session(struct bfd_proto *p, struct bfd_session *s,
366 struct bfd_session_config *opts)
367{
368 birdloop_enter(p->loop);
369
370 // XXX opts->idle_tx_int;
371
372 bfd_session_set_min_tx(s, opts->min_tx_int);
373 bfd_session_set_min_rx(s, opts->min_rx_int);
374 s->detect_mult = opts->multiplier;
375 s->passive = opts->passive;
376
377 bfd_session_control_tx_timer(s);
378
379 birdloop_leave(p->loop);
380}
381
bf139664
OZ
382static void
383bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
384{
385 n->session = bfd_add_session(p, n->addr, n->opts);
386
387 if (n->opts->multihop)
388 {
389 bfd_open_session(p, n->session, n->local, NULL);
390 return;
391 }
392
393 struct neighbor *nb = neigh_find2(&p->p, &n->addr, n->iface, NEF_STICKY);
394 if (!nb)
395 {
396 log(L_ERR "%s: Invalid remote address %I%J", p->p.name, n->addr, n->iface);
397 return;
398 }
399
400 if (nb->data)
401 {
402 log(L_ERR "%s: Duplicate remote address %I", p->p.name, n->addr);
403 return;
404 }
405
406 nb->data = n->session;
407
408 if (nb->scope > 0)
409 bfd_open_session(p, n->session, nb->iface->addr->ip, nb->iface);
410 else
6a8d3f1c 411 TRACE(D_EVENTS, "Waiting for %I%J to become my neighbor", n->addr, n->iface);
bf139664
OZ
412}
413
414static void
415bfd_stop_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
416{
417 if (!n->opts->multihop)
418 {
419 struct neighbor *nb = neigh_find2(&p->p, &n->addr, n->iface, 0);
420 if (nb)
421 nb->data = NULL;
422 }
423
424 bfd_remove_session(p, n->session);
425}
426
bf139664
OZ
427static void
428bfd_neigh_notify(struct neighbor *nb)
429{
430 struct bfd_proto *p = (struct bfd_proto *) nb->proto;
431 struct bfd_session *s = nb->data;
432
433 if (!s)
434 return;
435
436 if ((nb->scope > 0) && !s->opened)
437 bfd_open_session(p, s, nb->iface->addr->ip, nb->iface);
438
439 if ((nb->scope <= 0) && s->opened)
440 bfd_close_session(p, s);
441}
442
443
6a8d3f1c
OZ
444/* This core notify code should be replaced after main loop transition to birdloop */
445
446int pipe(int pipefd[2]);
447void pipe_drain(int fd);
448void pipe_kick(int fd);
449
450static int
451bfd_notify_hook(sock *sk, int len)
452{
453 struct bfd_proto *p = sk->data;
454 struct bfd_session *s;
455 list tmp_list;
456
457 pipe_drain(sk->fd);
458
459 bfd_lock_sessions(p);
460 init_list(&tmp_list);
461 add_tail_list(&tmp_list, &p->notify_list);
462 init_list(&p->notify_list);
463 bfd_unlock_sessions(p);
464
465 WALK_LIST_FIRST(s, tmp_list)
466 {
467 bfd_lock_sessions(p);
468 rem2_node(&s->n);
469 bfd_unlock_sessions(p);
470
471 // XXX do something
472 TRACE(D_EVENTS, "Notify: session changed %I %d %d", s->addr, s->loc_state, s->loc_diag);
473 }
474
475 return 0;
476}
477
478static inline void
479bfd_notify_kick(struct bfd_proto *p)
480{
481 pipe_kick(p->notify_ws->fd);
482}
483
484static void
485bfd_noterr_hook(sock *sk, int err)
486{
487 struct bfd_proto *p = sk->data;
488 log(L_ERR "%s: Notify socket error: %m", p->p.name, err);
489}
490
491static void
492bfd_notify_init(struct bfd_proto *p)
493{
494 int pfds[2];
495 sock *sk;
496
497 int rv = pipe(pfds);
498 if (rv < 0)
499 die("pipe: %m");
500
501 sk = sk_new(p->p.pool);
502 sk->type = SK_MAGIC;
503 sk->rx_hook = bfd_notify_hook;
504 sk->err_hook = bfd_noterr_hook;
505 sk->fd = pfds[0];
506 sk->data = p;
507 if (sk_open(sk) < 0)
508 die("bfd: sk_open failed");
509 p->notify_rs = sk;
510
511 /* The write sock is not added to any event loop */
512 sk = sk_new(p->p.pool);
513 sk->type = SK_MAGIC;
514 sk->fd = pfds[1];
515 sk->data = p;
516 sk->flags = SKF_THREAD;
517 if (sk_open(sk) < 0)
518 die("bfd: sk_open failed");
519 p->notify_ws = sk;
520}
521
bf139664
OZ
522
523static struct proto *
524bfd_init(struct proto_config *c)
525{
526 struct proto *p = proto_new(c, sizeof(struct bfd_proto));
527
6a8d3f1c 528 p->neigh_notify = bfd_neigh_notify;
bf139664
OZ
529
530 return p;
531}
532
533static int
534bfd_start(struct proto *P)
535{
536 struct bfd_proto *p = (struct bfd_proto *) P;
537 struct bfd_config *cf = (struct bfd_config *) (P->cf);
538
6a8d3f1c
OZ
539 p->loop = birdloop_new(P->pool);
540 p->tpool = rp_new(NULL, "BFD thread root");
541 pthread_spin_init(&p->lock, PTHREAD_PROCESS_PRIVATE);
542
bf139664 543 p->session_slab = sl_new(P->pool, sizeof(struct bfd_session));
6a8d3f1c
OZ
544 HASH_INIT(p->session_hash_id, P->pool, 4);
545 HASH_INIT(p->session_hash_ip, P->pool, 4);
546
547 init_list(&p->sock_list);
548
549
550 birdloop_mask_wakeups(p->loop);
bf139664 551
6a8d3f1c
OZ
552 init_list(&p->notify_list);
553 bfd_notify_init(p);
554
555 birdloop_enter(p->loop);
556 p->rx_1 = bfd_open_rx_sk(p, 0);
557 p->rx_m = bfd_open_rx_sk(p, 1);
558 birdloop_leave(p->loop);
bf139664
OZ
559
560 struct bfd_neighbor *n;
6a8d3f1c 561 WALK_LIST(n, cf->neigh_list)
bf139664
OZ
562 bfd_start_neighbor(p, n);
563
6a8d3f1c
OZ
564 birdloop_unmask_wakeups(p->loop);
565
bf139664
OZ
566 return PS_UP;
567}
568
569
570static int
571bfd_shutdown(struct proto *P)
572{
573 struct bfd_proto *p = (struct bfd_proto *) P;
574
575 return PS_DOWN;
576}
577
578static inline int
579bfd_same_neighbor(struct bfd_neighbor *x, struct bfd_neighbor *y)
580{
581 return ipa_equal(x->addr, y->addr) && ipa_equal(x->local, y->local) &&
582 (x->iface == y->iface) && (x->opts->multihop == y->opts->multihop);
583}
584
585static void
586bfd_match_neighbor(struct bfd_proto *p, struct bfd_neighbor *on, struct bfd_config *new)
587{
588 struct bfd_neighbor *nn;
589
6a8d3f1c 590 WALK_LIST(nn, new->neigh_list)
bf139664
OZ
591 if (bfd_same_neighbor(nn, on))
592 {
593 nn->session = on->session;
6a8d3f1c 594 bfd_configure_session(p, nn->session, nn->opts);
bf139664
OZ
595 return;
596 }
597
598 bfd_stop_neighbor(p, on);
599}
600
601static int
602bfd_reconfigure(struct proto *P, struct proto_config *c)
603{
604 struct bfd_proto *p = (struct bfd_proto *) P;
605 struct bfd_config *old = (struct bfd_config *) (P->cf);
606 struct bfd_config *new = (struct bfd_config *) c;
607 struct bfd_neighbor *n;
608
6a8d3f1c
OZ
609 birdloop_mask_wakeups(p->loop);
610
611 WALK_LIST(n, old->neigh_list)
bf139664
OZ
612 bfd_match_neighbor(p, n, new);
613
6a8d3f1c 614 WALK_LIST(n, new->neigh_list)
bf139664
OZ
615 if (!n->session)
616 bfd_start_neighbor(p, n);
617
6a8d3f1c
OZ
618 birdloop_unmask_wakeups(p->loop);
619
bf139664
OZ
620 return 1;
621}
622
623static void
624bfd_copy_config(struct proto_config *dest, struct proto_config *src)
625{
626 struct bfd_config *d = (struct bfd_config *) dest;
6a8d3f1c 627 // struct bfd_config *s = (struct bfd_config *) src;
bf139664 628
6a8d3f1c
OZ
629 /* We clean up neigh_list, ifaces are non-sharable */
630 init_list(&d->neigh_list);
bf139664 631
bf139664
OZ
632}
633
6a8d3f1c
OZ
634void
635bfd_show_sessions(struct proto *P)
636{
637 struct bfd_proto *p = (struct bfd_proto *) P;
638 uint state, diag;
639 u32 tx_int, timeout;
640 const char *ifname;
641
642 if (p->p.proto_state != PS_UP)
643 {
644 cli_msg(-1013, "%s: is not up", p->p.name);
645 cli_msg(0, "");
646 return;
647 }
648
649 cli_msg(-1013, "%s:", p->p.name);
650 cli_msg(-1013, "%-12s\t%s\t%s\t%s\t%s", "Router IP", "Iface",
651 "State", "TX Int", "Timeout");
652
653 debug("XXX WALK %d %d\n", p->session_hash_id.count, p->session_hash_ip.count);
654
655 HASH_WALK(p->session_hash_id, next_id, s)
656 {
657 // FIXME this is unsafe
658 state = s->loc_state;
659 diag = s->loc_diag;
660 ifname = (s->bsock && s->bsock->sk->iface) ? s->bsock->sk->iface->name : "---";
661 tx_int = (MAX(s->des_min_tx_int, s->rem_min_rx_int) TO_MS);
662 timeout = (MAX(s->req_min_rx_int, s->rem_min_tx_int) TO_MS) * s->rem_detect_mult;
663
664 cli_msg(-1013, "%I\t%s\t%d %d\t%u\t%u",
665 s->addr, ifname, state, diag, tx_int, timeout);
666 }
667 HASH_WALK_END;
668
669 cli_msg(0, "");
670}
671
672
bf139664
OZ
673struct protocol proto_bfd = {
674 .name = "BFD",
675 .template = "bfd%d",
676 .init = bfd_init,
677 .start = bfd_start,
678 .shutdown = bfd_shutdown,
679 .reconfigure = bfd_reconfigure,
680 .copy_config = bfd_copy_config,
681};