diff options
Diffstat (limited to 'proto/bfd/bfd.c')
-rw-r--r-- | proto/bfd/bfd.c | 535 |
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", |