diff options
Diffstat (limited to 'proto/bfd/bfd.c')
-rw-r--r-- | proto/bfd/bfd.c | 484 |
1 files changed, 302 insertions, 182 deletions
diff --git a/proto/bfd/bfd.c b/proto/bfd/bfd.c index 479c1510..c88c1cb2 100644 --- a/proto/bfd/bfd.c +++ b/proto/bfd/bfd.c @@ -82,7 +82,7 @@ * BFD thread to the main thread. This is done in an asynchronous way, sesions * with pending notifications are linked (in the BFD thread) to @notify_list in * &bfd_proto, and then bfd_notify_hook() in the main thread is activated using - * bfd_notify_kick() and a pipe. The hook then processes scheduled sessions and + * a standard event sending code. The hook then processes scheduled sessions and * calls hooks from associated BFD requests. This @notify_list (and state fields * in structure &bfd_session) is protected by a spinlock in &bfd_proto and * functions bfd_lock_sessions() / bfd_unlock_sessions(). @@ -113,26 +113,22 @@ #define HASH_IP_EQ(a1,n1,a2,n2) ipa_equal(a1, a2) && n1 == n2 #define HASH_IP_FN(a,n) ipa_hash(a) ^ u32_hash(n) -DEFINE_DOMAIN(rtable); #define BFD_LOCK LOCK_DOMAIN(rtable, bfd_global.lock) #define BFD_UNLOCK UNLOCK_DOMAIN(rtable, bfd_global.lock) -#define BFD_ASSERT_LOCKED ASSERT_DIE(DOMAIN_IS_LOCKED(rtable, bfd_global.lock)) static struct { DOMAIN(rtable) lock; list wait_list; + list pickup_list; list proto_list; + uint pickup_reload; } bfd_global; -static struct bfd_session bfd_admin_down = { .loc = ATOMIC_VAR_INIT((struct bfd_session_state) { .state = BFD_STATE_ADMIN_DOWN }), }; - const char *bfd_state_names[] = { "AdminDown", "Down", "Init", "Up" }; static void bfd_session_set_min_tx(struct bfd_session *s, u32 val); static struct bfd_iface *bfd_get_iface(struct bfd_proto *p, ip_addr local, struct iface *iface); static void bfd_free_iface(struct bfd_iface *ifa); -static void bfd_remove_session(struct bfd_proto *p, struct bfd_session *s); -static void bfd_reconfigure_session_hook(void *vsession); /* @@ -151,57 +147,37 @@ bfd_merge_options(const struct bfd_iface_config *cf, const struct bfd_options *o }; } -static int +static void bfd_session_update_state(struct bfd_session *s, uint state, uint diag) { struct bfd_proto *p = s->ifa->bfd; - uint old_state = BFD_LOC_STATE(s).state; + uint old_state = s->loc_state; + int notify; if (state == old_state) - { - if (current_time() > s->last_reqlist_check + 5 S) - { - BFD_LOCK; - if (EMPTY_LIST(s->request_list)) - { - bfd_remove_session(p, s); - BFD_UNLOCK; - return 1; - } - - s->last_reqlist_check = current_time(); - BFD_UNLOCK; - } - return 0; - } + return; TRACE(D_EVENTS, "Session to %I changed state from %s to %s", s->addr, bfd_state_names[old_state], bfd_state_names[state]); - atomic_store_explicit(&s->loc, ((struct bfd_session_state) { .state = state, .diag = diag }), memory_order_release); + bfd_lock_sessions(p); + s->loc_state = state; + s->loc_diag = diag; s->last_state_change = current_time(); + notify = !NODE_VALID(&s->n); + if (notify) + add_tail(&p->notify_list, &s->n); + bfd_unlock_sessions(p); + if (state == BFD_STATE_UP) bfd_session_set_min_tx(s, s->cf.min_tx_int); if (old_state == BFD_STATE_UP) bfd_session_set_min_tx(s, s->cf.idle_tx_int); - BFD_LOCK; - if (EMPTY_LIST(s->request_list)) - { - bfd_remove_session(p, s); - BFD_UNLOCK; - return 1; - } - - struct bfd_request *req; - node *nn; - WALK_LIST2(req, nn, s->request_list, n) - ev_send_self(&req->event); - - BFD_UNLOCK; - return 0; + if (notify) + ev_send(&global_event_list, &p->notify_event); } static void @@ -246,8 +222,8 @@ bfd_session_control_tx_timer(struct bfd_session *s, int reset) if (s->rem_demand_mode && !s->poll_active && - (BFD_LOC_STATE(s).state == BFD_STATE_UP) && - (s->rem.state == BFD_STATE_UP)) + (s->loc_state == BFD_STATE_UP) && + (s->rem_state == BFD_STATE_UP)) goto stop; if (s->rem_min_rx_int == 0) @@ -317,29 +293,28 @@ bfd_session_process_ctl(struct bfd_session *s, u8 flags, u32 old_tx_int, u32 old int next_state = 0; int diag = BFD_DIAG_NOTHING; - switch (BFD_LOC_STATE(s).state) + 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; + 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; + 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; + if (s->rem_state <= BFD_STATE_DOWN) next_state = BFD_STATE_DOWN, diag = BFD_DIAG_NEIGHBOR_DOWN; break; } if (next_state) - if (bfd_session_update_state(s, next_state, diag)) - return; + bfd_session_update_state(s, next_state, diag); bfd_session_control_tx_timer(s, 0); @@ -354,7 +329,7 @@ bfd_session_timeout(struct bfd_session *s) TRACE(D_EVENTS, "Session to %I expired", s->addr); - s->rem.state = BFD_STATE_DOWN; + s->rem_state = BFD_STATE_DOWN; s->rem_id = 0; s->rem_min_tx_int = 0; s->rem_min_rx_int = 1; @@ -365,8 +340,7 @@ bfd_session_timeout(struct bfd_session *s) s->poll_active = 0; s->poll_scheduled = 0; - if (bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_TIMEOUT)) - return; + bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_TIMEOUT); bfd_session_control_tx_timer(s, 1); } @@ -382,7 +356,7 @@ bfd_session_set_min_tx(struct bfd_session *s, u32 val) s->des_min_tx_new = val; /* Postpone timer update if des_min_tx_int increases and the session is up */ - if ((BFD_LOC_STATE(s).state != BFD_STATE_UP) || (val < s->des_min_tx_int)) + 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); @@ -402,7 +376,7 @@ bfd_session_set_min_rx(struct bfd_session *s, u32 val) s->req_min_rx_new = val; /* Postpone timer update if req_min_rx_int decreases and the session is up */ - if ((BFD_LOC_STATE(s).state != BFD_STATE_UP) || (val > s->req_min_rx_int)) + 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); @@ -414,14 +388,12 @@ bfd_session_set_min_rx(struct bfd_session *s, u32 val) struct bfd_session * bfd_find_session_by_id(struct bfd_proto *p, u32 id) { - ASSERT_DIE(birdloop_inside(p->p.loop)); 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, uint ifindex) { - ASSERT_DIE(birdloop_inside(p->p.loop)); return HASH_FIND(p->session_hash_ip, HASH_IP, addr, ifindex); } @@ -455,7 +427,6 @@ static struct bfd_session * bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface *iface, struct bfd_options *opts) { ASSERT_DIE(birdloop_inside(p->p.loop)); - BFD_ASSERT_LOCKED; struct bfd_iface *ifa = bfd_get_iface(p, local, iface); @@ -469,15 +440,10 @@ bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface * HASH_INSERT(p->session_hash_ip, HASH_IP, s); s->cf = bfd_merge_options(ifa->cf, opts); - s->update_event = (event) { - .hook = bfd_reconfigure_session_hook, - .data = s, - .list = birdloop_event_list(p->p.loop), - }; /* Initialization of state variables - see RFC 5880 6.8.1 */ - atomic_store_explicit(&s->loc, ((struct bfd_session_state) { .state = BFD_STATE_DOWN }), memory_order_relaxed); - s->rem.state = BFD_STATE_DOWN; + s->loc_state = BFD_STATE_DOWN; + s->rem_state = BFD_STATE_DOWN; s->des_min_tx_int = s->des_min_tx_new = s->cf.idle_tx_int; s->req_min_rx_int = s->req_min_rx_new = s->cf.min_rx_int; s->rem_min_rx_int = 1; @@ -485,8 +451,8 @@ bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface * s->passive = s->cf.passive; s->tx_csn = random_u32(); - s->tx_timer = tm_new_init(p->p.pool, bfd_tx_timer_hook, s, 0, 0); - s->hold_timer = tm_new_init(p->p.pool, bfd_hold_timer_hook, s, 0, 0); + s->tx_timer = tm_new_init(p->tpool, bfd_tx_timer_hook, s, 0, 0); + s->hold_timer = tm_new_init(p->tpool, bfd_hold_timer_hook, s, 0, 0); bfd_session_update_tx_interval(s); bfd_session_control_tx_timer(s, 1); @@ -498,12 +464,42 @@ bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface * return s; } +/* static void -bfd_remove_session(struct bfd_proto *p, struct bfd_session *s) +bfd_open_session(struct bfd_proto *p, struct bfd_session *s, ip_addr local, struct iface *ifa) { - ASSERT_DIE(birdloop_inside(p->p.loop)); - BFD_ASSERT_LOCKED; - ASSERT_DIE(EMPTY_LIST(s->request_list)); + birdloop_enter(p->p.loop); + + s->opened = 1; + + bfd_session_control_tx_timer(s); + + birdloop_leave(p->p.loop); +} + +static void +bfd_close_session(struct bfd_proto *p, struct bfd_session *s) +{ + birdloop_enter(p->p.loop); + + s->opened = 0; + + bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_PATH_DOWN); + bfd_session_control_tx_timer(s); + + birdloop_leave(p->p.loop); +} +*/ + +static void +bfd_remove_session_locked(struct bfd_proto *p, struct bfd_session *s) +{ + /* Caller should ensure that request list is empty */ + + /* Remove session from notify list if scheduled for notification */ + /* No need for bfd_lock_sessions(), we are already protected by birdloop_enter() */ + if (NODE_VALID(&s->n)) + rem_node(&s->n); bfd_free_iface(s->ifa); @@ -515,25 +511,29 @@ bfd_remove_session(struct bfd_proto *p, struct bfd_session *s) TRACE(D_EVENTS, "Session to %I removed", s->addr); - sl_free(p->session_slab, s); + sl_free(s); +} + +static void +bfd_remove_session(struct bfd_proto *p, struct bfd_session *s) +{ + birdloop_enter(p->p.loop); + bfd_remove_session_locked(p, s); + birdloop_leave(p->p.loop); } static void bfd_reconfigure_session(struct bfd_proto *p, struct bfd_session *s) { - ASSERT_DIE(birdloop_inside(p->p.loop)); - BFD_LOCK; if (EMPTY_LIST(s->request_list)) - { - bfd_remove_session(p, s); - BFD_UNLOCK; return; - } + + birdloop_enter(p->p.loop); struct bfd_request *req = SKIP_BACK(struct bfd_request, n, HEAD(s->request_list)); s->cf = bfd_merge_options(s->ifa->cf, &req->opts); - u32 tx = (BFD_LOC_STATE(s).state == BFD_STATE_UP) ? s->cf.min_tx_int : s->cf.idle_tx_int; + u32 tx = (s->loc_state == BFD_STATE_UP) ? s->cf.min_tx_int : s->cf.idle_tx_int; bfd_session_set_min_tx(s, tx); bfd_session_set_min_rx(s, s->cf.min_rx_int); s->detect_mult = s->cf.multiplier; @@ -541,15 +541,9 @@ bfd_reconfigure_session(struct bfd_proto *p, struct bfd_session *s) bfd_session_control_tx_timer(s, 0); - TRACE(D_EVENTS, "Session to %I reconfigured", s->addr); - BFD_UNLOCK; -} + birdloop_leave(p->p.loop); -static void -bfd_reconfigure_session_hook(void *data) -{ - struct bfd_session *s = data; - return bfd_reconfigure_session(s->ifa->bfd, s); + TRACE(D_EVENTS, "Session to %I reconfigured", s->addr); } @@ -586,7 +580,7 @@ bfd_get_iface(struct bfd_proto *p, ip_addr local, struct iface *iface) struct bfd_config *cf = (struct bfd_config *) (p->p.cf); struct bfd_iface_config *ic = bfd_find_iface_config(cf, iface); - ifa = mb_allocz(p->p.pool, sizeof(struct bfd_iface)); + ifa = mb_allocz(p->tpool, sizeof(struct bfd_iface)); ifa->local = local; ifa->iface = iface; ifa->cf = ic; @@ -595,6 +589,9 @@ bfd_get_iface(struct bfd_proto *p, ip_addr local, struct iface *iface) ifa->sk = bfd_open_tx_sk(p, local, iface); ifa->uc = 1; + if (cf->strict_bind) + ifa->rx = bfd_open_rx_sk_bound(p, local, iface); + add_tail(&p->iface_list, &ifa->n); return ifa; @@ -607,17 +604,17 @@ bfd_free_iface(struct bfd_iface *ifa) return; if (ifa->sk) - { - sk_stop(ifa->sk); rfree(ifa->sk); - } + + if (ifa->rx) + rfree(ifa->rx); rem_node(&ifa->n); mb_free(ifa); } static void -bfd_reconfigure_iface(struct bfd_proto *p UNUSED, struct bfd_iface *ifa, struct bfd_config *nc) +bfd_reconfigure_iface(struct bfd_proto *p, struct bfd_iface *ifa, struct bfd_config *nc) { struct bfd_iface_config *new = bfd_find_iface_config(nc, ifa->iface); struct bfd_iface_config *old = ifa->cf; @@ -631,7 +628,9 @@ bfd_reconfigure_iface(struct bfd_proto *p UNUSED, struct bfd_iface *ifa, struct (new->passive != old->passive); /* This should be probably changed to not access ifa->cf from the BFD thread */ + birdloop_enter(p->p.loop); ifa->cf = new; + birdloop_leave(p->p.loop); } @@ -640,55 +639,77 @@ bfd_reconfigure_iface(struct bfd_proto *p UNUSED, struct bfd_iface *ifa, struct */ static void -bfd_request_notify(void *data) +bfd_request_notify(struct bfd_request *req, u8 state, u8 diag) { - struct bfd_request *req = data; - struct bfd_session_state old = req->old_state; - - BFD_LOCK; /* Needed to safely access req->session */ - struct bfd_session_state new = atomic_load_explicit(&req->session->loc, memory_order_acquire); - BFD_UNLOCK; + u8 old_state = req->state; - if (new.state == old.state) + if (state == old_state) return; - req->state = new.state; - req->diag = new.diag; - req->old_state = new; - req->down = (old.state == BFD_STATE_UP) && (new.state == BFD_STATE_DOWN); + req->state = state; + req->diag = diag; + req->old_state = old_state; + req->down = (old_state == BFD_STATE_UP) && (state == BFD_STATE_DOWN); if (req->hook) + { + struct birdloop *target = !birdloop_inside(req->target) ? req->target : NULL; + + if (target) + birdloop_enter(target); + req->hook(req); + + if (target) + birdloop_leave(target); + } } static int bfd_add_request(struct bfd_proto *p, struct bfd_request *req) { - BFD_ASSERT_LOCKED; - ASSERT_DIE(req->session == &bfd_admin_down); - struct bfd_config *cf = (struct bfd_config *) (p->p.cf); if (p->p.vrf && (p->p.vrf != req->vrf)) + { + TRACE(D_EVENTS, "Not accepting request to %I with different VRF", req->addr); return 0; + } if (ipa_is_ip4(req->addr) ? !cf->accept_ipv4 : !cf->accept_ipv6) + { + TRACE(D_EVENTS, "Not accepting request to %I (AF limit)", req->addr); return 0; + } if (req->iface ? !cf->accept_direct : !cf->accept_multihop) + { + TRACE(D_EVENTS, "Not accepting %s request to %I", req->iface ? "direct" : "multihop", req->addr); return 0; + } uint ifindex = req->iface ? req->iface->index : 0; struct bfd_session *s = bfd_find_session_by_addr(p, req->addr, ifindex); - if (!s) + if (s) + TRACE(D_EVENTS, "Session to %I reused", s->addr); + else s = bfd_add_session(p, req->addr, req->local, req->iface, &req->opts); rem_node(&req->n); add_tail(&s->request_list, &req->n); req->session = s; - ev_send_self(&req->event); + bfd_lock_sessions(p); + + int notify = !NODE_VALID(&s->n); + if (notify) + add_tail(&p->notify_list, &s->n); + + bfd_unlock_sessions(p); + + if (notify) + ev_send(&global_event_list, &p->notify_event); return 1; } @@ -696,35 +717,89 @@ bfd_add_request(struct bfd_proto *p, struct bfd_request *req) static void bfd_pickup_requests(void *_data UNUSED) { - struct bfd_proto *p; - node *nn; - WALK_LIST2(p, nn, bfd_global.proto_list, bfd_node) - { - birdloop_enter(p->p.loop); + /* NOTE TO MY FUTURE SELF + * + * Functions bfd_take_requests() and bfd_drop_requests() need to have + * consistent &bfd_global.wait_list and this is ensured only by having these + * functions called from bfd_start() and bfd_shutdown() which are both called + * in PROTO_LOCKED_FROM_MAIN context, i.e. always from &main_birdloop. + * + * This pickup event is also called in &main_birdloop, therefore we can + * freely do BFD_LOCK/BFD_UNLOCK while processing all the requests. All BFD + * protocols capable of bfd_add_request() are either started before this code + * happens or after that. + * + * If BFD protocols could start in parallel with this routine, they might + * miss some of the waiting requests, thus if anybody tries to start + * protocols or run this pickup event outside &main_birdloop in future, they + * shall ensure that this race condition is mitigated somehow. + * + * Thank you, my future self, for understanding. Have a nice day! + */ + + DBG("BFD pickup loop starting"); + + BFD_LOCK; + do { + bfd_global.pickup_reload = 0; + BFD_UNLOCK; + + node *n; + WALK_LIST(n, bfd_global.proto_list) + { + struct bfd_proto *p = SKIP_BACK(struct bfd_proto, bfd_node, n); + birdloop_enter(p->p.loop); + BFD_LOCK; + + TRACE(D_EVENTS, "Picking up new requests (%d available)", list_length(&bfd_global.pickup_list)); + + node *rn, *rnxt; + WALK_LIST_DELSAFE(rn, rnxt, bfd_global.pickup_list) + bfd_add_request(p, SKIP_BACK(struct bfd_request, n, rn)); + + BFD_UNLOCK; + + /* Remove sessions with no requests */ + HASH_WALK_DELSAFE(p->session_hash_id, next_id, s) + { + if (EMPTY_LIST(s->request_list)) + bfd_remove_session_locked(p, s); + } + HASH_WALK_END; + + birdloop_leave(p->p.loop); + } + BFD_LOCK; + } while (bfd_global.pickup_reload); - struct bfd_request *req; - node *rn, *rnxt; - WALK_LIST2_DELSAFE(req, rn, rnxt, bfd_global.wait_list, n) - bfd_add_request(p, req); + list tmp_list; + init_list(&tmp_list); + add_tail_list(&tmp_list, &bfd_global.pickup_list); - BFD_UNLOCK; - birdloop_ping(p->p.loop); - birdloop_leave(p->p.loop); - } + init_list(&bfd_global.pickup_list); + BFD_UNLOCK; + + log(L_TRACE "No protocol for %d BFD requests", list_length(&tmp_list)); + + node *n; + WALK_LIST(n, tmp_list) + bfd_request_notify(SKIP_BACK(struct bfd_request, n, n), BFD_STATE_ADMIN_DOWN, 0); + + BFD_LOCK; + add_tail_list(&bfd_global.wait_list, &tmp_list); + BFD_UNLOCK; } static event bfd_pickup_event = { .hook = bfd_pickup_requests }; -#define bfd_schedule_pickup() ev_send(&global_event_list, &bfd_pickup_event) static void bfd_take_requests(struct bfd_proto *p) { - struct bfd_request *req; node *n, *nn; BFD_LOCK; - WALK_LIST2_DELSAFE(req, n, nn, bfd_global.wait_list, n) - bfd_add_request(p, req); + WALK_LIST_DELSAFE(n, nn, bfd_global.wait_list) + bfd_add_request(p, SKIP_BACK(struct bfd_request, n, n)); BFD_UNLOCK; } @@ -739,13 +814,13 @@ bfd_drop_requests(struct bfd_proto *p) { struct bfd_request *req = SKIP_BACK(struct bfd_request, n, n); rem_node(&req->n); - add_tail(&bfd_global.wait_list, &req->n); - req->session = &bfd_admin_down; - ev_send_self(&req->event); + add_tail(&bfd_global.pickup_list, &req->n); + req->session = NULL; } - bfd_schedule_pickup(); - bfd_remove_session(p, s); + ev_send(&global_event_list, &bfd_pickup_event); + + bfd_remove_session_locked(p, s); } HASH_WALK_END; BFD_UNLOCK; @@ -757,7 +832,7 @@ struct bfd_request * bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface, struct iface *vrf, void (*hook)(struct bfd_request *), void *data, - struct event_list *list, + struct birdloop *target, const struct bfd_options *opts) { struct bfd_request *req = ralloc(p, &bfd_request_class); @@ -770,18 +845,18 @@ bfd_request_session(pool *p, ip_addr addr, ip_addr local, if (opts) req->opts = *opts; + ASSERT_DIE(target || !hook); req->hook = hook; req->data = data; - req->event = (event) { - .hook = bfd_request_notify, - .data = req, - .list = list, - }; + req->target = target; + + req->session = NULL; BFD_LOCK; - req->session = &bfd_admin_down; - add_tail(&bfd_global.wait_list, &req->n); - bfd_schedule_pickup(); + bfd_global.pickup_reload++; + add_tail(&bfd_global.pickup_list, &req->n); + ev_send(&global_event_list, &bfd_pickup_event); + DBG("New BFD request enlisted.\n"); BFD_UNLOCK; return req; @@ -790,17 +865,15 @@ bfd_request_session(pool *p, ip_addr addr, ip_addr local, void bfd_update_request(struct bfd_request *req, const struct bfd_options *opts) { + struct bfd_session *s = req->session; + if (!memcmp(opts, &req->opts, sizeof(const struct bfd_options))) return; - BFD_LOCK; req->opts = *opts; - struct bfd_session *s = req->session; - if (s != &bfd_admin_down) - ev_send_self(&s->update_event); - - BFD_UNLOCK; + if (s) + bfd_reconfigure_session(s->ifa->bfd, s); } static void @@ -812,11 +885,11 @@ bfd_request_free(resource *r) rem_node(&req->n); BFD_UNLOCK; - ev_postpone(&req->event); + ev_send(&global_event_list, &bfd_pickup_event); } static void -bfd_request_dump(resource *r) +bfd_request_dump(resource *r, unsigned indent UNUSED) { struct bfd_request *req = (struct bfd_request *) r; @@ -849,7 +922,7 @@ bfd_neigh_notify(struct neighbor *nb) if ((nb->scope > 0) && !n->req) { ip_addr local = ipa_nonzero(n->local) ? n->local : nb->ifa->ip; - n->req = bfd_request_session(p->p.pool, n->addr, local, nb->iface, p->p.vrf, NULL, NULL, birdloop_event_list(p->p.loop), NULL); + n->req = bfd_request_session(p->p.pool, n->addr, local, nb->iface, p->p.vrf, NULL, NULL, NULL, NULL); } if ((nb->scope <= 0) && n->req) @@ -866,7 +939,7 @@ bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n) if (n->multihop) { - n->req = bfd_request_session(p->p.pool, n->addr, n->local, NULL, p->p.vrf, NULL, NULL, birdloop_event_list(p->p.loop), NULL); + n->req = bfd_request_session(p->p.pool, n->addr, n->local, NULL, p->p.vrf, NULL, NULL, NULL, NULL); return; } @@ -941,23 +1014,53 @@ bfd_reconfigure_neighbors(struct bfd_proto *p, struct bfd_config *new) /* - * BFD protocol glue + * BFD notify socket */ -void -bfd_init_all(void) +/* This core notify code should be replaced after main loop transition to birdloop */ + +static void +bfd_notify_hook(void *data) { - bfd_global.lock = DOMAIN_NEW(rtable, "BFD Global"); - init_list(&bfd_global.wait_list); - init_list(&bfd_global.proto_list); + struct bfd_proto *p = data; + struct bfd_session *s; + list tmp_list; + u8 state, diag; + node *n, *nn; + + 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); + rem_node(&s->n); + state = s->loc_state; + diag = s->loc_diag; + bfd_unlock_sessions(p); + + WALK_LIST_DELSAFE(n, nn, s->request_list) + bfd_request_notify(SKIP_BACK(struct bfd_request, n, n), state, diag); + + /* Remove the session if all requests were removed in notify hooks */ + if (EMPTY_LIST(s->request_list)) + bfd_remove_session(p, s); + } } +/* + * BFD protocol glue + */ + static struct proto * bfd_init(struct proto_config *c) { struct proto *p = proto_new(c); - p->neigh_notify = bfd_neigh_notify; + p->iface_sub.neigh_notify = bfd_neigh_notify; return p; } @@ -968,25 +1071,38 @@ bfd_start(struct proto *P) struct bfd_proto *p = (struct bfd_proto *) P; struct bfd_config *cf = (struct bfd_config *) (P->cf); + pthread_spin_init(&p->lock, PTHREAD_PROCESS_PRIVATE); + + p->tpool = rp_new(P->pool, "BFD loop pool"); + p->session_slab = sl_new(P->pool, sizeof(struct bfd_session)); HASH_INIT(p->session_hash_id, P->pool, 8); HASH_INIT(p->session_hash_ip, P->pool, 8); init_list(&p->iface_list); + init_list(&p->notify_list); + p->notify_event = (event) { + .hook = bfd_notify_hook, + .data = p, + }; + add_tail(&bfd_global.proto_list, &p->bfd_node); - if (cf->accept_ipv4 && cf->accept_direct) - p->rx4_1 = bfd_open_rx_sk(p, 0, SK_IPV4); + if (!cf->strict_bind) + { + if (cf->accept_ipv4 && cf->accept_direct) + p->rx4_1 = bfd_open_rx_sk(p, 0, SK_IPV4); - if (cf->accept_ipv4 && cf->accept_multihop) - p->rx4_m = bfd_open_rx_sk(p, 1, SK_IPV4); + if (cf->accept_ipv4 && cf->accept_multihop) + p->rx4_m = bfd_open_rx_sk(p, 1, SK_IPV4); - if (cf->accept_ipv6 && cf->accept_direct) - p->rx6_1 = bfd_open_rx_sk(p, 0, SK_IPV6); + if (cf->accept_ipv6 && cf->accept_direct) + p->rx6_1 = bfd_open_rx_sk(p, 0, SK_IPV6); - if (cf->accept_ipv6 && cf->accept_multihop) - p->rx6_m = bfd_open_rx_sk(p, 1, SK_IPV6); + if (cf->accept_ipv6 && cf->accept_multihop) + p->rx6_m = bfd_open_rx_sk(p, 1, SK_IPV6); + } bfd_take_requests(p); @@ -1011,11 +1127,6 @@ bfd_shutdown(struct proto *P) bfd_drop_requests(p); - if (p->rx4_1) sk_stop(p->rx4_1); - if (p->rx4_m) sk_stop(p->rx4_m); - if (p->rx6_1) sk_stop(p->rx6_1); - if (p->rx6_m) sk_stop(p->rx6_m); - return PS_DOWN; } @@ -1027,13 +1138,12 @@ bfd_reconfigure(struct proto *P, struct proto_config *c) struct bfd_config *new = (struct bfd_config *) c; struct bfd_iface *ifa; - ASSERT_DIE(birdloop_inside(P->loop)); - /* TODO: Improve accept reconfiguration */ if ((new->accept_ipv4 != old->accept_ipv4) || (new->accept_ipv6 != old->accept_ipv6) || (new->accept_direct != old->accept_direct) || - (new->accept_multihop != old->accept_multihop)) + (new->accept_multihop != old->accept_multihop) || + (new->strict_bind != old->strict_bind)) return 0; birdloop_mask_wakeups(p->p.loop); @@ -1041,12 +1151,12 @@ bfd_reconfigure(struct proto *P, struct proto_config *c) WALK_LIST(ifa, p->iface_list) bfd_reconfigure_iface(p, ifa, new); - HASH_WALK_DELSAFE(p->session_hash_id, next_id, s) + HASH_WALK(p->session_hash_id, next_id, s) { if (s->ifa->changed) bfd_reconfigure_session(p, s); } - HASH_WALK_DELSAFE_END; + HASH_WALK_END; bfd_reconfigure_neighbors(p, new); @@ -1071,14 +1181,13 @@ bfd_show_sessions(struct proto *P) { byte tbuf[TM_DATETIME_BUFFER_SIZE]; struct bfd_proto *p = (struct bfd_proto *) P; + uint state, diag UNUSED; btime tx_int, timeout; const char *ifname; - birdloop_enter(P->loop); if (p->p.proto_state != PS_UP) { cli_msg(-1020, "%s: is not up", p->p.name); - birdloop_leave(P->loop); return; } @@ -1086,9 +1195,12 @@ bfd_show_sessions(struct proto *P) cli_msg(-1020, "%-25s %-10s %-10s %-12s %8s %8s", "IP address", "Interface", "State", "Since", "Interval", "Timeout"); + HASH_WALK(p->session_hash_id, next_id, s) { - uint state = BFD_LOC_STATE(s).state; + /* FIXME: this is thread-unsafe, but perhaps harmless */ + state = s->loc_state; + diag = s->loc_diag; ifname = (s->ifa && s->ifa->iface) ? s->ifa->iface->name : "---"; tx_int = s->last_tx ? MAX(s->des_min_tx_int, s->rem_min_rx_int) : 0; timeout = (btime) MAX(s->req_min_rx_int, s->rem_min_tx_int) * s->rem_detect_mult; @@ -1100,15 +1212,12 @@ bfd_show_sessions(struct proto *P) s->addr, ifname, bfd_state_names[state], tbuf, tx_int, timeout); } HASH_WALK_END; - - birdloop_leave(P->loop); } struct protocol proto_bfd = { .name = "BFD", .template = "bfd%d", - .class = PROTOCOL_BFD, .proto_size = sizeof(struct bfd_proto), .config_size = sizeof(struct bfd_config), .init = bfd_init, @@ -1117,3 +1226,14 @@ struct protocol proto_bfd = { .reconfigure = bfd_reconfigure, .copy_config = bfd_copy_config, }; + +void +bfd_build(void) +{ + proto_build(&proto_bfd); + + bfd_global.lock = DOMAIN_NEW(rtable, "BFD Global"); + init_list(&bfd_global.wait_list); + init_list(&bfd_global.pickup_list); + init_list(&bfd_global.proto_list); +} |