summaryrefslogtreecommitdiff
path: root/proto/bfd/bfd.c
diff options
context:
space:
mode:
Diffstat (limited to 'proto/bfd/bfd.c')
-rw-r--r--proto/bfd/bfd.c535
1 files changed, 360 insertions, 175 deletions
diff --git a/proto/bfd/bfd.c b/proto/bfd/bfd.c
index 4c7fe1f1..6e38102b 100644
--- a/proto/bfd/bfd.c
+++ b/proto/bfd/bfd.c
@@ -1,150 +1,92 @@
-#include "nest/bird.h"
-#include "nest/iface.h"
-#include "nest/protocol.h"
-#include "nest/route.h"
-#include "nest/cli.h"
-#include "conf/conf.h"
-#include "lib/socket.h"
-#include "lib/resource.h"
-#include "lib/string.h"
+/*
+ * BIRD -- Bidirectional Forwarding Detection (BFD)
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
#include "bfd.h"
-#define HASH_ID_KEY loc_id
-#define HASH_ID_NEXT next_id
-#define HASH_ID_EQ(a,b) ((a)==(b))
-#define HASH_ID_FN(a) (a)
+#define HASH_ID_KEY(n) n->loc_id
+#define HASH_ID_NEXT(n) n->next_id
+#define HASH_ID_EQ(a,b) (a == b)
+#define HASH_ID_FN(k) (k)
-#define HASH_IP_KEY addr
-#define HASH_IP_NEXT next_ip
-#define HASH_IP_EQ(a,b) ((a)==(b))
-#define HASH_IP_FN(a) (a == b)
+#define HASH_IP_KEY(n) n->addr
+#define HASH_IP_NEXT(n) n->next_ip
+#define HASH_IP_EQ(a,b) ipa_equal(a,b)
+#define HASH_IP_FN(k) ipa_hash(k)
-static u32
-bfd_get_free_id(struct bfd_proto *p)
-{
- u32 id;
- for (id = random_u32(); 1; id++)
- if (id && !bfd_find_session_by_id(p, id))
- break;
-
- return id;
-}
+static inline void bfd_notify_kick(struct bfd_proto *p);
-static void
-bfd_add_session(struct bfd_proto *p, ip_addr addr, struct bfd_session_config *opts)
+static void
+bfd_session_update_state(struct bfd_session *s, uint state, uint diag)
{
- birdloop_enter(p->loop);
-
- struct bfd_session *s = sl_alloc(p->session_slab);
- bzero(s, sizeof(struct bfd_session));
+ struct bfd_proto *p = s->bfd;
+ int notify;
- /* Initialization of state variables - see RFC 5880 3.8.1 */
- s->loc_state = BFD_STATE_DOWN;
- s->rem_state = BFD_STATE_DOWN;
- s->loc_id = bfd_get_free_id(p);
- s->des_min_tx_int = s->des_min_tx_new = s->opts->idle_tx_int;
- s->req_min_rx_int = s->req_min_rx_new = s->opts->min_rx_int;
- s->detect_mult = s->opts->multiplier;
- s->rem_min_rx_int = 1;
-
- HASH_INSERT(p->session_hash_id, HASH_ID, s);
- HASH_INSERT(p->session_hash_ip, HASH_IP, s);
-
- s->tx_timer = tm2_new_set(xxx, bfd_rx_timer_hook, s, 0, 0);
- s->hold_timer = tm2_new_set(xxx, bfd_hold_timer_hook, s, 0, 0);
- bfd_session_update_tx_interval(s);
-
- birdloop_leave(p->loop);
-}
-
-static void
-bfd_open_session(struct bfd_proto *p, struct bfd_session *s, ip_addr local, struct iface *ifa)
-{
- birdloop_enter(p->loop);
+ if (s->loc_state == state)
+ return;
- s->bsock = bfd_get_socket(p, local, ifa);
- s->local = local;
- s->iface = ifa;
- s->opened = 1;
+ //TRACE(D_EVENTS, "Session changed %I %d %d", s->addr, state, diag);
+ debug("STATE %I %d %d %d\n", s->addr, s->loc_state, state, diag);
+
+ bfd_lock_sessions(p);
+ s->loc_state = state;
+ s->loc_diag = diag;
- bfd_session_control_tx_timer(s);
+ notify = !NODE_VALID(&s->n);
+ if (notify)
+ add_tail(&p->notify_list, &s->n);
+ bfd_unlock_sessions(p);
- birdloop_leave(p->loop);
+ if (notify)
+ bfd_notify_kick(p);
}
-static void
-bfd_close_session(struct bfd_proto *p, struct bfd_session *s)
+static void
+bfd_session_timeout(struct bfd_session *s)
{
- birdloop_enter(p->loop);
-
- bfd_free_socket(s->bsock);
- s->bsock = NULL;
- s->local = IPA_NONE;
- s->iface = NULL;
- s->opened = 0;
-
- bfd_session_control_tx_timer(s);
+ s->rem_state = BFD_STATE_DOWN;
+ s->rem_id = 0;
+ s->rem_min_tx_int = 0;
+ s->rem_min_rx_int = 1;
+ s->rem_demand_mode = 0;
+ s->rem_detect_mult = 0;
- birdloop_leave(p->loop);
+ bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_TIMEOUT);
}
static void
-bfd_remove_session(struct bfd_proto *p, struct bfd_session *s)
+bfd_session_update_tx_interval(struct bfd_session *s)
{
- birdloop_enter(p->loop);
-
- bfd_free_socket(s->bsock);
-
- rfree(s->tx_timer);
- rfree(s->hold_timer);
-
- HASH_REMOVE(p->session_hash_id, HASH_ID, s);
- HASH_REMOVE(p->session_hash_ip, HASH_IP, s);
-
- sl_free(p->session_slab, s);
+ u32 tx_int = MAX(s->des_min_tx_int, s->rem_min_rx_int);
+ u32 tx_int_l = tx_int - (tx_int / 4); // 75 %
+ u32 tx_int_h = tx_int - (tx_int / 10); // 90 %
- birdloop_leave(p->loop);
-}
+ s->tx_timer->recurrent = tx_int_l;
+ s->tx_timer->randomize = tx_int_h - tx_int_l;
-struct bfd_session *
-bfd_find_session_by_id(struct bfd_proto *p, u32 id)
-{
- return HASH_FIND(p->session_hash_id, HASH_ID, id);
-}
+ /* Do not set timer if no previous event */
+ if (!s->last_tx)
+ return;
-struct bfd_session *
-bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr)
-{
- return HASH_FIND(p->session_hash_ip, HASH_IP, addr);
+ /* Set timer relative to last tx_timer event */
+ tm2_set(s->tx_timer, s->last_tx + tx_int_l);
}
static void
-bfd_rx_timer_hook(timer2 *t)
+bfd_session_update_detection_time(struct bfd_session *s, int kick)
{
- struct bfd_session *s = timer->data;
+ btime timeout = (btime) MAX(s->req_min_rx_int, s->rem_min_tx_int) * s->rem_detect_mult;
- s->last_tx = xxx_now;
- bfd_send_ctl(s->bfd, s, 0);
-}
-
-static void
-bfd_hold_timer_hook(timer2 *t)
-{
- bfd_session_timeout(timer->data);
-}
+ if (kick)
+ s->last_rx = current_time();
-static void
-bfd_session_timeout(struct bfd_session *s)
-{
- s->rem_state = BFD_STATE_DOWN;
- s->rem_id = 0;
- s->rem_min_tx_int = 0;
- s->rem_min_rx_int = 1;
- s->rem_demand_mode = 0;
+ if (!s->last_rx)
+ return;
- bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_TIMEOUT);
+ tm2_set(s->hold_timer, s->last_rx + timeout);
}
static void
@@ -178,38 +120,6 @@ bfd_session_control_tx_timer(struct bfd_session *s)
}
static void
-bfd_session_update_tx_interval(struct bfd_session *s)
-{
- u32 tx_int = MAX(s->des_min_tx_int, s->rem_min_rx_int);
- u32 tx_int_l = tx_int - (tx_int / 4); // 75 %
- u32 tx_int_h = tx_int - (tx_int / 10); // 90 %
-
- s->tx_timer->recurrent = tx_int_l;
- s->tx_timer->randomize = tx_int_h - tx_int_l;
-
- /* Do not set timer if no previous event */
- if (!s->last_tx)
- return;
-
- /* Set timer relative to last tx_timer event */
- tm2_set(s->tx_timer, s->last_tx + tx_int_l);
-}
-
-static void
-bfd_session_update_detection_time(struct bfd_session *s, int kick)
-{
- xxx_time timeout = (xxx_time) MAX(s->req_min_rx_int, s->rem_min_tx_int) * s->rem_detect_mult;
-
- if (kick)
- s->last_rx = xxx_now;
-
- if (!s->last_rx)
- return;
-
- tm2_set(s->hold_timer, s->last_rx + timeout);
-}
-
-void
bfd_session_request_poll(struct bfd_session *s, u8 request)
{
s->poll_scheduled |= request;
@@ -222,7 +132,7 @@ bfd_session_request_poll(struct bfd_session *s, u8 request)
bfd_send_ctl(s->bfd, s, 0);
}
-void
+static void
bfd_session_terminate_poll(struct bfd_session *s)
{
u8 poll_done = s->poll_active & ~s->poll_scheduled;
@@ -237,16 +147,16 @@ bfd_session_terminate_poll(struct bfd_session *s)
/* Timers are updated by caller - bfd_session_process_ctl() */
- xxx_restart_poll();
+ // xxx_restart_poll();
}
void
-bfd_session_process_ctl(struct bfd_session *s, u8 flags, u32 old_rx_int, u32 old_tx_int)
+bfd_session_process_ctl(struct bfd_session *s, u8 flags, u32 old_tx_int, u32 old_rx_int)
{
if (s->poll_active && (flags & BFD_FLAG_FINAL))
bfd_session_terminate_poll(s);
- if ((s->des_min_tx_int != old_rx_int) || (s->rem_min_rx_int != old_tx_int))
+ if ((s->des_min_tx_int != old_tx_int) || (s->rem_min_rx_int != old_rx_int))
bfd_session_update_tx_interval(s);
bfd_session_update_detection_time(s, 1);
@@ -281,10 +191,9 @@ bfd_session_process_ctl(struct bfd_session *s, u8 flags, u32 old_rx_int, u32 old
bfd_session_control_tx_timer(s);
if (flags & BFD_FLAG_POLL)
- bfd_send_ctl(p, s, 1);
+ bfd_send_ctl(s->bfd, s, 1);
}
-
static void
bfd_session_set_min_tx(struct bfd_session *s, u32 val)
{
@@ -325,6 +234,151 @@ bfd_session_set_min_rx(struct bfd_session *s, u32 val)
bfd_session_request_poll(s, BFD_POLL_RX);
}
+struct bfd_session *
+bfd_find_session_by_id(struct bfd_proto *p, u32 id)
+{
+ return HASH_FIND(p->session_hash_id, HASH_ID, id);
+}
+
+struct bfd_session *
+bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr)
+{
+ return HASH_FIND(p->session_hash_ip, HASH_IP, addr);
+}
+
+static void
+bfd_tx_timer_hook(timer2 *t)
+{
+ struct bfd_session *s = t->data;
+
+ s->last_tx = current_time();
+ // debug("TX %d\n", (s32) (s->last_tx TO_MS));
+ bfd_send_ctl(s->bfd, s, 0);
+}
+
+static void
+bfd_hold_timer_hook(timer2 *t)
+{
+ bfd_session_timeout(t->data);
+}
+
+static u32
+bfd_get_free_id(struct bfd_proto *p)
+{
+ u32 id;
+ for (id = random_u32(); 1; id++)
+ if (id && !bfd_find_session_by_id(p, id))
+ break;
+
+ return id;
+}
+
+static struct bfd_session *
+bfd_add_session(struct bfd_proto *p, ip_addr addr, struct bfd_session_config *opts)
+{
+ birdloop_enter(p->loop);
+
+ struct bfd_session *s = sl_alloc(p->session_slab);
+ bzero(s, sizeof(struct bfd_session));
+
+ s->addr = addr;
+ s->loc_id = bfd_get_free_id(p);
+ debug("XXX INS1 %d %d %u %I\n", p->session_hash_id.count, p->session_hash_ip.count, s->loc_id, s->addr);
+ HASH_INSERT(p->session_hash_id, HASH_ID, s);
+ debug("XXX INS2 %d %d\n", p->session_hash_id.count, p->session_hash_ip.count);
+ HASH_INSERT(p->session_hash_ip, HASH_IP, s);
+ debug("XXX INS3 %d %d\n", p->session_hash_id.count, p->session_hash_ip.count);
+ s->bfd = p;
+
+ /* Initialization of state variables - see RFC 5880 6.8.1 */
+ s->loc_state = BFD_STATE_DOWN;
+ s->rem_state = BFD_STATE_DOWN;
+ s->des_min_tx_int = s->des_min_tx_new = opts->min_tx_int; // XXX opts->idle_tx_int;
+ s->req_min_rx_int = s->req_min_rx_new = opts->min_rx_int;
+ s->rem_min_rx_int = 1;
+ s->detect_mult = opts->multiplier;
+ s->passive = opts->passive;
+
+ s->tx_timer = tm2_new_init(p->tpool, bfd_tx_timer_hook, s, 0, 0);
+ s->hold_timer = tm2_new_init(p->tpool, bfd_hold_timer_hook, s, 0, 0);
+ bfd_session_update_tx_interval(s);
+
+ birdloop_leave(p->loop);
+
+ return s;
+}
+
+static void
+bfd_open_session(struct bfd_proto *p, struct bfd_session *s, ip_addr local, struct iface *ifa)
+{
+ birdloop_enter(p->loop);
+
+ s->bsock = bfd_get_socket(p, local, ifa);
+ // s->local = local;
+ // s->iface = ifa;
+ s->opened = 1;
+
+ bfd_session_control_tx_timer(s);
+
+ birdloop_leave(p->loop);
+}
+
+static void
+bfd_close_session(struct bfd_proto *p, struct bfd_session *s)
+{
+ birdloop_enter(p->loop);
+
+ bfd_free_socket(s->bsock);
+ s->bsock = NULL;
+ // s->local = IPA_NONE;
+ // s->iface = NULL;
+ s->opened = 0;
+
+ bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_PATH_DOWN);
+ bfd_session_control_tx_timer(s);
+
+ birdloop_leave(p->loop);
+}
+
+static void
+bfd_remove_session(struct bfd_proto *p, struct bfd_session *s)
+{
+ birdloop_enter(p->loop);
+
+ bfd_free_socket(s->bsock);
+
+ rfree(s->tx_timer);
+ rfree(s->hold_timer);
+
+ debug("XXX REM1 %d %d %u %I\n", p->session_hash_id.count, p->session_hash_ip.count, s->loc_id, s->addr);
+ HASH_REMOVE(p->session_hash_id, HASH_ID, s);
+ debug("XXX REM2 %d %d\n", p->session_hash_id.count, p->session_hash_ip.count);
+ HASH_REMOVE(p->session_hash_ip, HASH_IP, s);
+ debug("XXX REM3 %d %d\n", p->session_hash_id.count, p->session_hash_ip.count);
+
+ sl_free(p->session_slab, s);
+
+ birdloop_leave(p->loop);
+}
+
+static void
+bfd_configure_session(struct bfd_proto *p, struct bfd_session *s,
+ struct bfd_session_config *opts)
+{
+ birdloop_enter(p->loop);
+
+ // XXX opts->idle_tx_int;
+
+ bfd_session_set_min_tx(s, opts->min_tx_int);
+ bfd_session_set_min_rx(s, opts->min_rx_int);
+ s->detect_mult = opts->multiplier;
+ s->passive = opts->passive;
+
+ bfd_session_control_tx_timer(s);
+
+ birdloop_leave(p->loop);
+}
+
static void
bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
{
@@ -354,7 +408,7 @@ bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
if (nb->scope > 0)
bfd_open_session(p, n->session, nb->iface->addr->ip, nb->iface);
else
- TRACE(D_EVENTS, "Waiting for %I%J to become my neighbor", n->addr, cf->iface);
+ TRACE(D_EVENTS, "Waiting for %I%J to become my neighbor", n->addr, n->iface);
}
static void
@@ -370,7 +424,6 @@ bfd_stop_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
bfd_remove_session(p, n->session);
}
-
static void
bfd_neigh_notify(struct neighbor *nb)
{
@@ -388,14 +441,91 @@ bfd_neigh_notify(struct neighbor *nb)
}
+/* This core notify code should be replaced after main loop transition to birdloop */
+
+int pipe(int pipefd[2]);
+void pipe_drain(int fd);
+void pipe_kick(int fd);
+
+static int
+bfd_notify_hook(sock *sk, int len)
+{
+ struct bfd_proto *p = sk->data;
+ struct bfd_session *s;
+ list tmp_list;
+
+ pipe_drain(sk->fd);
+
+ bfd_lock_sessions(p);
+ init_list(&tmp_list);
+ add_tail_list(&tmp_list, &p->notify_list);
+ init_list(&p->notify_list);
+ bfd_unlock_sessions(p);
+
+ WALK_LIST_FIRST(s, tmp_list)
+ {
+ bfd_lock_sessions(p);
+ rem2_node(&s->n);
+ bfd_unlock_sessions(p);
+
+ // XXX do something
+ TRACE(D_EVENTS, "Notify: session changed %I %d %d", s->addr, s->loc_state, s->loc_diag);
+ }
+
+ return 0;
+}
+
+static inline void
+bfd_notify_kick(struct bfd_proto *p)
+{
+ pipe_kick(p->notify_ws->fd);
+}
+
+static void
+bfd_noterr_hook(sock *sk, int err)
+{
+ struct bfd_proto *p = sk->data;
+ log(L_ERR "%s: Notify socket error: %m", p->p.name, err);
+}
+
+static void
+bfd_notify_init(struct bfd_proto *p)
+{
+ int pfds[2];
+ sock *sk;
+
+ int rv = pipe(pfds);
+ if (rv < 0)
+ die("pipe: %m");
+
+ sk = sk_new(p->p.pool);
+ sk->type = SK_MAGIC;
+ sk->rx_hook = bfd_notify_hook;
+ sk->err_hook = bfd_noterr_hook;
+ sk->fd = pfds[0];
+ sk->data = p;
+ if (sk_open(sk) < 0)
+ die("bfd: sk_open failed");
+ p->notify_rs = sk;
+
+ /* The write sock is not added to any event loop */
+ sk = sk_new(p->p.pool);
+ sk->type = SK_MAGIC;
+ sk->fd = pfds[1];
+ sk->data = p;
+ sk->flags = SKF_THREAD;
+ if (sk_open(sk) < 0)
+ die("bfd: sk_open failed");
+ p->notify_ws = sk;
+}
+
static struct proto *
bfd_init(struct proto_config *c)
{
struct proto *p = proto_new(c, sizeof(struct bfd_proto));
- p->if_notify = bfd_if_notify;
- p->ifa_notify = bfd_ifa_notify;
+ p->neigh_notify = bfd_neigh_notify;
return p;
}
@@ -406,16 +536,33 @@ bfd_start(struct proto *P)
struct bfd_proto *p = (struct bfd_proto *) P;
struct bfd_config *cf = (struct bfd_config *) (P->cf);
+ p->loop = birdloop_new(P->pool);
+ p->tpool = rp_new(NULL, "BFD thread root");
+ pthread_spin_init(&p->lock, PTHREAD_PROCESS_PRIVATE);
+
p->session_slab = sl_new(P->pool, sizeof(struct bfd_session));
- init_list(&p->sockets);
+ HASH_INIT(p->session_hash_id, P->pool, 4);
+ HASH_INIT(p->session_hash_ip, P->pool, 4);
+
+ init_list(&p->sock_list);
+
+
+ birdloop_mask_wakeups(p->loop);
- HASH_INIT(p->session_hash_id, P->pool, 16);
- HASH_INIT(p->session_hash_ip, P->pool, 16);
+ init_list(&p->notify_list);
+ bfd_notify_init(p);
+
+ birdloop_enter(p->loop);
+ p->rx_1 = bfd_open_rx_sk(p, 0);
+ p->rx_m = bfd_open_rx_sk(p, 1);
+ birdloop_leave(p->loop);
struct bfd_neighbor *n;
- WALK_LIST(n, cf->neighbors)
+ WALK_LIST(n, cf->neigh_list)
bfd_start_neighbor(p, n);
+ birdloop_unmask_wakeups(p->loop);
+
return PS_UP;
}
@@ -440,14 +587,11 @@ bfd_match_neighbor(struct bfd_proto *p, struct bfd_neighbor *on, struct bfd_conf
{
struct bfd_neighbor *nn;
- if (r->neigh)
- r->neigh->data = NULL;
-
- WALK_LIST(nn, new->neighbors)
+ WALK_LIST(nn, new->neigh_list)
if (bfd_same_neighbor(nn, on))
{
nn->session = on->session;
- // XXX reconfiguration of session?
+ bfd_configure_session(p, nn->session, nn->opts);
return;
}
@@ -462,13 +606,17 @@ bfd_reconfigure(struct proto *P, struct proto_config *c)
struct bfd_config *new = (struct bfd_config *) c;
struct bfd_neighbor *n;
- WALK_LIST(n, old->neighbors)
+ birdloop_mask_wakeups(p->loop);
+
+ WALK_LIST(n, old->neigh_list)
bfd_match_neighbor(p, n, new);
- WALK_LIST(n, new->neighbors)
+ WALK_LIST(n, new->neigh_list)
if (!n->session)
bfd_start_neighbor(p, n);
+ birdloop_unmask_wakeups(p->loop);
+
return 1;
}
@@ -476,15 +624,52 @@ static void
bfd_copy_config(struct proto_config *dest, struct proto_config *src)
{
struct bfd_config *d = (struct bfd_config *) dest;
- struct bfd_config *s = (struct bfd_config *) src;
+ // struct bfd_config *s = (struct bfd_config *) src;
- /* We clean up patt_list, ifaces are non-sharable */
- init_list(&d->patt_list);
+ /* We clean up neigh_list, ifaces are non-sharable */
+ init_list(&d->neigh_list);
- /* We copy pref_list, shallow copy suffices */
- cfg_copy_list(&d->pref_list, &s->pref_list, sizeof(struct bfd_prefix_config));
}
+void
+bfd_show_sessions(struct proto *P)
+{
+ struct bfd_proto *p = (struct bfd_proto *) P;
+ uint state, diag;
+ u32 tx_int, timeout;
+ const char *ifname;
+
+ if (p->p.proto_state != PS_UP)
+ {
+ cli_msg(-1013, "%s: is not up", p->p.name);
+ cli_msg(0, "");
+ return;
+ }
+
+ cli_msg(-1013, "%s:", p->p.name);
+ cli_msg(-1013, "%-12s\t%s\t%s\t%s\t%s", "Router IP", "Iface",
+ "State", "TX Int", "Timeout");
+
+ debug("XXX WALK %d %d\n", p->session_hash_id.count, p->session_hash_ip.count);
+
+ HASH_WALK(p->session_hash_id, next_id, s)
+ {
+ // FIXME this is unsafe
+ state = s->loc_state;
+ diag = s->loc_diag;
+ ifname = (s->bsock && s->bsock->sk->iface) ? s->bsock->sk->iface->name : "---";
+ tx_int = (MAX(s->des_min_tx_int, s->rem_min_rx_int) TO_MS);
+ timeout = (MAX(s->req_min_rx_int, s->rem_min_tx_int) TO_MS) * s->rem_detect_mult;
+
+ cli_msg(-1013, "%I\t%s\t%d %d\t%u\t%u",
+ s->addr, ifname, state, diag, tx_int, timeout);
+ }
+ HASH_WALK_END;
+
+ cli_msg(0, "");
+}
+
+
struct protocol proto_bfd = {
.name = "BFD",
.template = "bfd%d",