6a8d3f1c1f
Now it compiles and mostly works.
681 lines
15 KiB
C
681 lines
15 KiB
C
/*
|
|
* 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(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(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 inline void bfd_notify_kick(struct bfd_proto *p);
|
|
|
|
static void
|
|
bfd_session_update_state(struct bfd_session *s, uint state, uint diag)
|
|
{
|
|
struct bfd_proto *p = s->bfd;
|
|
int notify;
|
|
|
|
if (s->loc_state == state)
|
|
return;
|
|
|
|
//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;
|
|
|
|
notify = !NODE_VALID(&s->n);
|
|
if (notify)
|
|
add_tail(&p->notify_list, &s->n);
|
|
bfd_unlock_sessions(p);
|
|
|
|
if (notify)
|
|
bfd_notify_kick(p);
|
|
}
|
|
|
|
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;
|
|
s->rem_detect_mult = 0;
|
|
|
|
bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_TIMEOUT);
|
|
}
|
|
|
|
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)
|
|
{
|
|
btime timeout = (btime) MAX(s->req_min_rx_int, s->rem_min_tx_int) * s->rem_detect_mult;
|
|
|
|
if (kick)
|
|
s->last_rx = current_time();
|
|
|
|
if (!s->last_rx)
|
|
return;
|
|
|
|
tm2_set(s->hold_timer, s->last_rx + timeout);
|
|
}
|
|
|
|
static void
|
|
bfd_session_control_tx_timer(struct bfd_session *s)
|
|
{
|
|
if (!s->opened)
|
|
goto stop;
|
|
|
|
if (s->passive && (s->rem_id == 0))
|
|
goto stop;
|
|
|
|
if (s->rem_demand_mode &&
|
|
!s->poll_active &&
|
|
(s->loc_state == BFD_STATE_UP) &&
|
|
(s->rem_state == BFD_STATE_UP))
|
|
goto stop;
|
|
|
|
if (s->rem_min_rx_int == 0)
|
|
goto stop;
|
|
|
|
/* So TX timer should run */
|
|
if (tm2_active(s->tx_timer))
|
|
return;
|
|
|
|
tm2_start(s->tx_timer, 0);
|
|
return;
|
|
|
|
stop:
|
|
tm2_stop(s->tx_timer);
|
|
s->last_tx = 0;
|
|
}
|
|
|
|
static void
|
|
bfd_session_request_poll(struct bfd_session *s, u8 request)
|
|
{
|
|
s->poll_scheduled |= request;
|
|
|
|
if (s->poll_active)
|
|
return;
|
|
|
|
s->poll_active = s->poll_scheduled;
|
|
s->poll_scheduled = 0;
|
|
bfd_send_ctl(s->bfd, s, 0);
|
|
}
|
|
|
|
static void
|
|
bfd_session_terminate_poll(struct bfd_session *s)
|
|
{
|
|
u8 poll_done = s->poll_active & ~s->poll_scheduled;
|
|
|
|
if (poll_done & BFD_POLL_TX)
|
|
s->des_min_tx_int = s->des_min_tx_new;
|
|
|
|
if (poll_done & BFD_POLL_RX)
|
|
s->req_min_rx_int = s->req_min_rx_new;
|
|
|
|
s->poll_active = 0;
|
|
|
|
/* Timers are updated by caller - bfd_session_process_ctl() */
|
|
|
|
// xxx_restart_poll();
|
|
}
|
|
|
|
void
|
|
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_tx_int) || (s->rem_min_rx_int != old_rx_int))
|
|
bfd_session_update_tx_interval(s);
|
|
|
|
bfd_session_update_detection_time(s, 1);
|
|
|
|
/* Update session state */
|
|
int next_state = 0;
|
|
int diag = BFD_DIAG_NOTHING;
|
|
|
|
switch (s->loc_state)
|
|
{
|
|
case BFD_STATE_ADMIN_DOWN:
|
|
return;
|
|
|
|
case BFD_STATE_DOWN:
|
|
if (s->rem_state == BFD_STATE_DOWN) next_state = BFD_STATE_INIT;
|
|
else if (s->rem_state == BFD_STATE_INIT) next_state = BFD_STATE_UP;
|
|
break;
|
|
|
|
case BFD_STATE_INIT:
|
|
if (s->rem_state == BFD_STATE_ADMIN_DOWN) next_state = BFD_STATE_DOWN, diag = BFD_DIAG_NEIGHBOR_DOWN;
|
|
else if (s->rem_state >= BFD_STATE_INIT) next_state = BFD_STATE_UP;
|
|
break;
|
|
|
|
case BFD_STATE_UP:
|
|
if (s->rem_state <= BFD_STATE_DOWN) next_state = BFD_STATE_DOWN, diag = BFD_DIAG_NEIGHBOR_DOWN;
|
|
break;
|
|
}
|
|
|
|
if (next_state)
|
|
bfd_session_update_state(s, next_state, diag);
|
|
|
|
bfd_session_control_tx_timer(s);
|
|
|
|
if (flags & BFD_FLAG_POLL)
|
|
bfd_send_ctl(s->bfd, s, 1);
|
|
}
|
|
|
|
static void
|
|
bfd_session_set_min_tx(struct bfd_session *s, u32 val)
|
|
{
|
|
/* Note that des_min_tx_int <= des_min_tx_new */
|
|
|
|
if (val == s->des_min_tx_new)
|
|
return;
|
|
|
|
s->des_min_tx_new = val;
|
|
|
|
/* Postpone timer update if des_min_tx_int increases and the session is up */
|
|
if ((s->loc_state != BFD_STATE_UP) || (val < s->des_min_tx_int))
|
|
{
|
|
s->des_min_tx_int = val;
|
|
bfd_session_update_tx_interval(s);
|
|
}
|
|
|
|
bfd_session_request_poll(s, BFD_POLL_TX);
|
|
}
|
|
|
|
static void
|
|
bfd_session_set_min_rx(struct bfd_session *s, u32 val)
|
|
{
|
|
/* Note that req_min_rx_int >= req_min_rx_new */
|
|
|
|
if (val == s->req_min_rx_new)
|
|
return;
|
|
|
|
s->req_min_rx_new = val;
|
|
|
|
/* Postpone timer update if req_min_rx_int decreases and the session is up */
|
|
if ((s->loc_state != BFD_STATE_UP) || (val > s->req_min_rx_int))
|
|
{
|
|
s->req_min_rx_int = val;
|
|
bfd_session_update_detection_time(s, 0);
|
|
}
|
|
|
|
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)
|
|
{
|
|
n->session = bfd_add_session(p, n->addr, n->opts);
|
|
|
|
if (n->opts->multihop)
|
|
{
|
|
bfd_open_session(p, n->session, n->local, NULL);
|
|
return;
|
|
}
|
|
|
|
struct neighbor *nb = neigh_find2(&p->p, &n->addr, n->iface, NEF_STICKY);
|
|
if (!nb)
|
|
{
|
|
log(L_ERR "%s: Invalid remote address %I%J", p->p.name, n->addr, n->iface);
|
|
return;
|
|
}
|
|
|
|
if (nb->data)
|
|
{
|
|
log(L_ERR "%s: Duplicate remote address %I", p->p.name, n->addr);
|
|
return;
|
|
}
|
|
|
|
nb->data = n->session;
|
|
|
|
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, n->iface);
|
|
}
|
|
|
|
static void
|
|
bfd_stop_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
|
|
{
|
|
if (!n->opts->multihop)
|
|
{
|
|
struct neighbor *nb = neigh_find2(&p->p, &n->addr, n->iface, 0);
|
|
if (nb)
|
|
nb->data = NULL;
|
|
}
|
|
|
|
bfd_remove_session(p, n->session);
|
|
}
|
|
|
|
static void
|
|
bfd_neigh_notify(struct neighbor *nb)
|
|
{
|
|
struct bfd_proto *p = (struct bfd_proto *) nb->proto;
|
|
struct bfd_session *s = nb->data;
|
|
|
|
if (!s)
|
|
return;
|
|
|
|
if ((nb->scope > 0) && !s->opened)
|
|
bfd_open_session(p, s, nb->iface->addr->ip, nb->iface);
|
|
|
|
if ((nb->scope <= 0) && s->opened)
|
|
bfd_close_session(p, s);
|
|
}
|
|
|
|
|
|
/* 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->neigh_notify = bfd_neigh_notify;
|
|
|
|
return p;
|
|
}
|
|
|
|
static int
|
|
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));
|
|
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);
|
|
|
|
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->neigh_list)
|
|
bfd_start_neighbor(p, n);
|
|
|
|
birdloop_unmask_wakeups(p->loop);
|
|
|
|
return PS_UP;
|
|
}
|
|
|
|
|
|
static int
|
|
bfd_shutdown(struct proto *P)
|
|
{
|
|
struct bfd_proto *p = (struct bfd_proto *) P;
|
|
|
|
return PS_DOWN;
|
|
}
|
|
|
|
static inline int
|
|
bfd_same_neighbor(struct bfd_neighbor *x, struct bfd_neighbor *y)
|
|
{
|
|
return ipa_equal(x->addr, y->addr) && ipa_equal(x->local, y->local) &&
|
|
(x->iface == y->iface) && (x->opts->multihop == y->opts->multihop);
|
|
}
|
|
|
|
static void
|
|
bfd_match_neighbor(struct bfd_proto *p, struct bfd_neighbor *on, struct bfd_config *new)
|
|
{
|
|
struct bfd_neighbor *nn;
|
|
|
|
WALK_LIST(nn, new->neigh_list)
|
|
if (bfd_same_neighbor(nn, on))
|
|
{
|
|
nn->session = on->session;
|
|
bfd_configure_session(p, nn->session, nn->opts);
|
|
return;
|
|
}
|
|
|
|
bfd_stop_neighbor(p, on);
|
|
}
|
|
|
|
static int
|
|
bfd_reconfigure(struct proto *P, struct proto_config *c)
|
|
{
|
|
struct bfd_proto *p = (struct bfd_proto *) P;
|
|
struct bfd_config *old = (struct bfd_config *) (P->cf);
|
|
struct bfd_config *new = (struct bfd_config *) c;
|
|
struct bfd_neighbor *n;
|
|
|
|
birdloop_mask_wakeups(p->loop);
|
|
|
|
WALK_LIST(n, old->neigh_list)
|
|
bfd_match_neighbor(p, n, new);
|
|
|
|
WALK_LIST(n, new->neigh_list)
|
|
if (!n->session)
|
|
bfd_start_neighbor(p, n);
|
|
|
|
birdloop_unmask_wakeups(p->loop);
|
|
|
|
return 1;
|
|
}
|
|
|
|
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;
|
|
|
|
/* We clean up neigh_list, ifaces are non-sharable */
|
|
init_list(&d->neigh_list);
|
|
|
|
}
|
|
|
|
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",
|
|
.init = bfd_init,
|
|
.start = bfd_start,
|
|
.shutdown = bfd_shutdown,
|
|
.reconfigure = bfd_reconfigure,
|
|
.copy_config = bfd_copy_config,
|
|
};
|