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.c484
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);
+}