mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 09:41:54 +00:00
BFD: Fix session reconfiguration locking order
The sessions have to be updated asynchronously to avoid cross-locking between protocols. Testsuite: cf-ibgp-bfd-switch, cf-ibgp-multi-bfd-auth Fixes: #139 Thanks to Daniel Suchy <danny@danysek.cz> for reporting: https://trubka.network.cz/pipermail/bird-users/2024-December/017984.html
This commit is contained in:
parent
6779e5da69
commit
8349536278
@ -18,8 +18,11 @@ struct bfd_options {
|
|||||||
u32 min_tx_int;
|
u32 min_tx_int;
|
||||||
u32 idle_tx_int;
|
u32 idle_tx_int;
|
||||||
u8 multiplier;
|
u8 multiplier;
|
||||||
u8 passive;
|
PACKED enum bfd_opt_passive {
|
||||||
u8 passive_set;
|
BFD_OPT_PASSIVE_UNKNOWN = 0,
|
||||||
|
BFD_OPT_PASSIVE,
|
||||||
|
BFD_OPT_NOT_PASSIVE,
|
||||||
|
} passive;
|
||||||
u8 mode;
|
u8 mode;
|
||||||
u8 auth_type; /* Authentication type (BFD_AUTH_*) */
|
u8 auth_type; /* Authentication type (BFD_AUTH_*) */
|
||||||
list *passwords; /* Passwords for authentication */
|
list *passwords; /* Passwords for authentication */
|
||||||
|
144
proto/bfd/bfd.c
144
proto/bfd/bfd.c
@ -172,17 +172,17 @@ static void bfd_free_iface(struct bfd_iface *ifa);
|
|||||||
* BFD sessions
|
* BFD sessions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline struct bfd_session_config
|
static inline struct bfd_options
|
||||||
bfd_merge_options(const struct bfd_iface_config *cf, const struct bfd_options *opts)
|
bfd_merge_options(const struct bfd_options *bottom, const struct bfd_options *top)
|
||||||
{
|
{
|
||||||
return (struct bfd_session_config) {
|
return (struct bfd_options) {
|
||||||
.min_rx_int = opts->min_rx_int ?: cf->min_rx_int,
|
.min_rx_int = top->min_rx_int ?: bottom->min_rx_int,
|
||||||
.min_tx_int = opts->min_tx_int ?: cf->min_tx_int,
|
.min_tx_int = top->min_tx_int ?: bottom->min_tx_int,
|
||||||
.idle_tx_int = opts->idle_tx_int ?: cf->idle_tx_int,
|
.idle_tx_int = top->idle_tx_int ?: bottom->idle_tx_int,
|
||||||
.multiplier = opts->multiplier ?: cf->multiplier,
|
.multiplier = top->multiplier ?: bottom->multiplier,
|
||||||
.passive = opts->passive_set ? opts->passive : cf->passive,
|
.passive = top->passive ?: bottom->passive,
|
||||||
.auth_type = opts->auth_type ?: cf->auth_type,
|
.auth_type = top->auth_type ?: bottom->auth_type,
|
||||||
.passwords = opts->passwords ?: cf->passwords,
|
.passwords = top->passwords ?: bottom->passwords,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -478,7 +478,7 @@ bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface *
|
|||||||
HASH_INSERT(p->session_hash_id, HASH_ID, s);
|
HASH_INSERT(p->session_hash_id, HASH_ID, s);
|
||||||
HASH_INSERT(p->session_hash_ip, HASH_IP, s);
|
HASH_INSERT(p->session_hash_ip, HASH_IP, s);
|
||||||
|
|
||||||
s->cf = bfd_merge_options(ifa->cf, opts);
|
s->cf = bfd_merge_options(&ifa->cf->opts, opts);
|
||||||
|
|
||||||
/* Initialization of state variables - see RFC 5880 6.8.1 */
|
/* Initialization of state variables - see RFC 5880 6.8.1 */
|
||||||
s->loc_state = BFD_STATE_DOWN;
|
s->loc_state = BFD_STATE_DOWN;
|
||||||
@ -561,26 +561,58 @@ bfd_remove_session(struct bfd_proto *p, struct bfd_session *s)
|
|||||||
birdloop_leave(p->p.loop);
|
birdloop_leave(p->p.loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct bfd_reconfigure_sessions_deferred_call {
|
||||||
|
struct deferred_call dc;
|
||||||
|
struct bfd_proto *p;
|
||||||
|
config_ref old_config;
|
||||||
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
bfd_reconfigure_session(struct bfd_proto *p, struct bfd_session *s)
|
bfd_reconfigure_sessions(struct deferred_call *dc)
|
||||||
{
|
{
|
||||||
if (EMPTY_LIST(s->request_list))
|
SKIP_BACK_DECLARE(struct bfd_reconfigure_sessions_deferred_call,
|
||||||
return;
|
brsdc, dc, dc);
|
||||||
|
|
||||||
ASSERT_DIE(birdloop_inside(p->p.loop));
|
struct bfd_proto *p = brsdc->p;
|
||||||
|
birdloop_enter(p->p.loop);
|
||||||
|
|
||||||
SKIP_BACK_DECLARE(struct bfd_request, req, n, HEAD(s->request_list));
|
HASH_WALK(p->session_hash_id, next_id, s)
|
||||||
s->cf = bfd_merge_options(s->ifa->cf, &req->opts);
|
{
|
||||||
|
if (!EMPTY_LIST(s->request_list))
|
||||||
|
{
|
||||||
|
SKIP_BACK_DECLARE(struct bfd_request, req, n, HEAD(s->request_list));
|
||||||
|
struct bfd_options opts = bfd_merge_options(&s->ifa->cf->opts, &req->opts);
|
||||||
|
|
||||||
u32 tx = (s->loc_state == BFD_STATE_UP) ? s->cf.min_tx_int : s->cf.idle_tx_int;
|
#define CHK(x) (opts.x != s->cf.x) ||
|
||||||
bfd_session_set_min_tx(s, tx);
|
bool reload = MACRO_FOREACH(CHK,
|
||||||
bfd_session_set_min_rx(s, s->cf.min_rx_int);
|
min_rx_int,
|
||||||
s->detect_mult = s->cf.multiplier;
|
min_tx_int,
|
||||||
s->passive = s->cf.passive;
|
idle_tx_int,
|
||||||
|
multiplier,
|
||||||
|
passive) false; /* terminating the || chain */
|
||||||
|
#undef CHK
|
||||||
|
|
||||||
bfd_session_control_tx_timer(s, 0);
|
s->cf = opts;
|
||||||
|
|
||||||
TRACE(D_EVENTS, "Session to %I reconfigured", s->addr);
|
if (reload)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
s->passive = s->cf.passive;
|
||||||
|
|
||||||
|
bfd_session_control_tx_timer(s, 0);
|
||||||
|
|
||||||
|
TRACE(D_EVENTS, "Session to %I reconfigured", s->addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HASH_WALK_END;
|
||||||
|
birdloop_leave(p->p.loop);
|
||||||
|
|
||||||
|
/* Now the config is clean */
|
||||||
|
OBSREF_CLEAR(brsdc->old_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -589,10 +621,12 @@ bfd_reconfigure_session(struct bfd_proto *p, struct bfd_session *s)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static struct bfd_iface_config bfd_default_iface = {
|
static struct bfd_iface_config bfd_default_iface = {
|
||||||
.min_rx_int = BFD_DEFAULT_MIN_RX_INT,
|
.opts = {
|
||||||
.min_tx_int = BFD_DEFAULT_MIN_TX_INT,
|
.min_rx_int = BFD_DEFAULT_MIN_RX_INT,
|
||||||
.idle_tx_int = BFD_DEFAULT_IDLE_TX_INT,
|
.min_tx_int = BFD_DEFAULT_MIN_TX_INT,
|
||||||
.multiplier = BFD_DEFAULT_MULTIPLIER,
|
.idle_tx_int = BFD_DEFAULT_IDLE_TX_INT,
|
||||||
|
.multiplier = BFD_DEFAULT_MULTIPLIER,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct bfd_iface_config *
|
static inline struct bfd_iface_config *
|
||||||
@ -650,24 +684,6 @@ bfd_free_iface(struct bfd_iface *ifa)
|
|||||||
mb_free(ifa);
|
mb_free(ifa);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
bfd_reconfigure_iface(struct bfd_proto *p UNUSED, 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;
|
|
||||||
|
|
||||||
/* Check options that are handled in bfd_reconfigure_session() */
|
|
||||||
ifa->changed =
|
|
||||||
(new->min_rx_int != old->min_rx_int) ||
|
|
||||||
(new->min_tx_int != old->min_tx_int) ||
|
|
||||||
(new->idle_tx_int != old->idle_tx_int) ||
|
|
||||||
(new->multiplier != old->multiplier) ||
|
|
||||||
(new->passive != old->passive);
|
|
||||||
|
|
||||||
/* This should be probably changed to not access ifa->cf from the BFD thread */
|
|
||||||
ifa->cf = new;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* BFD requests
|
* BFD requests
|
||||||
@ -900,20 +916,7 @@ bfd_request_session(pool *p, ip_addr addr, ip_addr local,
|
|||||||
void
|
void
|
||||||
bfd_update_request(struct bfd_request *req, const struct bfd_options *opts)
|
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;
|
|
||||||
|
|
||||||
req->opts = *opts;
|
req->opts = *opts;
|
||||||
|
|
||||||
if (s)
|
|
||||||
{
|
|
||||||
struct bfd_proto *p = s->ifa->bfd;
|
|
||||||
birdloop_enter(p->p.loop);
|
|
||||||
bfd_reconfigure_session(p, s);
|
|
||||||
birdloop_leave(p->p.loop);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1193,21 +1196,22 @@ bfd_reconfigure(struct proto *P, struct proto_config *c)
|
|||||||
(new->zero_udp6_checksum_rx != old->zero_udp6_checksum_rx))
|
(new->zero_udp6_checksum_rx != old->zero_udp6_checksum_rx))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
birdloop_mask_wakeups(p->p.loop);
|
|
||||||
|
|
||||||
WALK_LIST(ifa, p->iface_list)
|
WALK_LIST(ifa, p->iface_list)
|
||||||
bfd_reconfigure_iface(p, ifa, new);
|
ifa->cf = bfd_find_iface_config(new, ifa->iface);
|
||||||
|
|
||||||
HASH_WALK(p->session_hash_id, next_id, s)
|
|
||||||
{
|
|
||||||
if (s->ifa->changed)
|
|
||||||
bfd_reconfigure_session(p, s);
|
|
||||||
}
|
|
||||||
HASH_WALK_END;
|
|
||||||
|
|
||||||
bfd_reconfigure_neighbors(p, new);
|
bfd_reconfigure_neighbors(p, new);
|
||||||
|
|
||||||
birdloop_unmask_wakeups(p->p.loop);
|
/* Sessions get reconfigured after all the config is applied */
|
||||||
|
struct bfd_reconfigure_sessions_deferred_call brsdc = {
|
||||||
|
.dc.hook = bfd_reconfigure_sessions,
|
||||||
|
.p = p,
|
||||||
|
};
|
||||||
|
SKIP_BACK_DECLARE(struct bfd_reconfigure_sessions_deferred_call,
|
||||||
|
brsdcp, dc, defer_call(&brsdc.dc, sizeof brsdc));
|
||||||
|
|
||||||
|
/* We need to keep the old config alive until all the sessions get
|
||||||
|
* reconfigured */
|
||||||
|
OBSREF_SET(brsdcp->old_config, P->cf->global);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -54,24 +54,7 @@ struct bfd_config
|
|||||||
struct bfd_iface_config
|
struct bfd_iface_config
|
||||||
{
|
{
|
||||||
struct iface_patt i;
|
struct iface_patt i;
|
||||||
u32 min_rx_int;
|
struct bfd_options opts;
|
||||||
u32 min_tx_int;
|
|
||||||
u32 idle_tx_int;
|
|
||||||
u8 multiplier;
|
|
||||||
u8 passive;
|
|
||||||
u8 auth_type; /* Authentication type (BFD_AUTH_*) */
|
|
||||||
list *passwords; /* Passwords for authentication */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bfd_session_config
|
|
||||||
{
|
|
||||||
u32 min_rx_int;
|
|
||||||
u32 min_tx_int;
|
|
||||||
u32 idle_tx_int;
|
|
||||||
u8 multiplier;
|
|
||||||
u8 passive;
|
|
||||||
u8 auth_type; /* Authentication type (BFD_AUTH_*) */
|
|
||||||
list *passwords; /* Passwords for authentication */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bfd_neighbor
|
struct bfd_neighbor
|
||||||
@ -146,7 +129,7 @@ struct bfd_session
|
|||||||
u32 loc_id; /* Local session ID (local discriminator) */
|
u32 loc_id; /* Local session ID (local discriminator) */
|
||||||
u32 rem_id; /* Remote session ID (remote discriminator) */
|
u32 rem_id; /* Remote session ID (remote discriminator) */
|
||||||
|
|
||||||
struct bfd_session_config cf; /* Static configuration parameters */
|
struct bfd_options cf; /* Static configuration parameters */
|
||||||
|
|
||||||
u32 des_min_tx_int; /* Desired min rx interval, local option */
|
u32 des_min_tx_int; /* Desired min rx interval, local option */
|
||||||
u32 des_min_tx_new; /* Used for des_min_tx_int change */
|
u32 des_min_tx_new; /* Used for des_min_tx_int change */
|
||||||
|
@ -86,44 +86,37 @@ bfd_iface_start:
|
|||||||
add_tail(&BFD_CFG->patt_list, NODE this_ipatt);
|
add_tail(&BFD_CFG->patt_list, NODE this_ipatt);
|
||||||
init_list(&this_ipatt->ipn_list);
|
init_list(&this_ipatt->ipn_list);
|
||||||
|
|
||||||
BFD_IFACE->min_rx_int = BFD_DEFAULT_MIN_RX_INT;
|
this_bfd_opts = &BFD_IFACE->opts;
|
||||||
BFD_IFACE->min_tx_int = BFD_DEFAULT_MIN_TX_INT;
|
|
||||||
BFD_IFACE->idle_tx_int = BFD_DEFAULT_IDLE_TX_INT;
|
this_bfd_opts->min_rx_int = BFD_DEFAULT_MIN_RX_INT;
|
||||||
BFD_IFACE->multiplier = BFD_DEFAULT_MULTIPLIER;
|
this_bfd_opts->min_tx_int = BFD_DEFAULT_MIN_TX_INT;
|
||||||
|
this_bfd_opts->idle_tx_int = BFD_DEFAULT_IDLE_TX_INT;
|
||||||
|
this_bfd_opts->multiplier = BFD_DEFAULT_MULTIPLIER;
|
||||||
|
|
||||||
reset_passwords();
|
reset_passwords();
|
||||||
};
|
};
|
||||||
|
|
||||||
bfd_iface_finish:
|
bfd_iface_finish:
|
||||||
{
|
{
|
||||||
BFD_IFACE->passwords = get_passwords();
|
this_bfd_opts->passwords = get_passwords();
|
||||||
|
|
||||||
if (!BFD_IFACE->auth_type != !BFD_IFACE->passwords)
|
if (!this_bfd_opts->auth_type != !this_bfd_opts->passwords)
|
||||||
cf_warn("Authentication and password options should be used together");
|
cf_warn("Authentication and password options should be used together");
|
||||||
|
|
||||||
if (BFD_IFACE->passwords)
|
if (this_bfd_opts->passwords)
|
||||||
{
|
{
|
||||||
struct password_item *pass;
|
struct password_item *pass;
|
||||||
WALK_LIST(pass, *BFD_IFACE->passwords)
|
WALK_LIST(pass, *this_bfd_opts->passwords)
|
||||||
{
|
{
|
||||||
if (pass->alg)
|
if (pass->alg)
|
||||||
cf_error("Password algorithm option not available in BFD protocol");
|
cf_error("Password algorithm option not available in BFD protocol");
|
||||||
|
|
||||||
pass->alg = bfd_auth_type_to_hash_alg[BFD_IFACE->auth_type];
|
pass->alg = bfd_auth_type_to_hash_alg[this_bfd_opts->auth_type];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
bfd_iface_item:
|
this_bfd_opts = NULL;
|
||||||
INTERVAL expr_us { BFD_IFACE->min_rx_int = BFD_IFACE->min_tx_int = $2; }
|
};
|
||||||
| MIN RX INTERVAL expr_us { BFD_IFACE->min_rx_int = $4; }
|
|
||||||
| MIN TX INTERVAL expr_us { BFD_IFACE->min_tx_int = $4; }
|
|
||||||
| IDLE TX INTERVAL expr_us { BFD_IFACE->idle_tx_int = $4; }
|
|
||||||
| MULTIPLIER expr { BFD_IFACE->multiplier = $2; }
|
|
||||||
| PASSIVE bool { BFD_IFACE->passive = $2; }
|
|
||||||
| AUTHENTICATION bfd_auth_type { BFD_IFACE->auth_type = $2; }
|
|
||||||
| password_list {}
|
|
||||||
;
|
|
||||||
|
|
||||||
bfd_auth_type:
|
bfd_auth_type:
|
||||||
NONE { $$ = BFD_AUTH_NONE; }
|
NONE { $$ = BFD_AUTH_NONE; }
|
||||||
@ -134,14 +127,9 @@ bfd_auth_type:
|
|||||||
| METICULOUS KEYED SHA1 { $$ = BFD_AUTH_METICULOUS_KEYED_SHA1; }
|
| METICULOUS KEYED SHA1 { $$ = BFD_AUTH_METICULOUS_KEYED_SHA1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
bfd_iface_opts:
|
|
||||||
/* empty */
|
|
||||||
| bfd_iface_opts bfd_iface_item ';'
|
|
||||||
;
|
|
||||||
|
|
||||||
bfd_iface_opt_list:
|
bfd_iface_opt_list:
|
||||||
/* empty */
|
/* empty */
|
||||||
| '{' bfd_iface_opts '}'
|
| '{' bfd_items '}'
|
||||||
;
|
;
|
||||||
|
|
||||||
bfd_iface:
|
bfd_iface:
|
||||||
@ -194,7 +182,7 @@ bfd_item:
|
|||||||
| MIN TX INTERVAL expr_us { this_bfd_opts->min_tx_int = $4; }
|
| MIN TX INTERVAL expr_us { this_bfd_opts->min_tx_int = $4; }
|
||||||
| IDLE TX INTERVAL expr_us { this_bfd_opts->idle_tx_int = $4; }
|
| IDLE TX INTERVAL expr_us { this_bfd_opts->idle_tx_int = $4; }
|
||||||
| MULTIPLIER expr { this_bfd_opts->multiplier = $2; }
|
| MULTIPLIER expr { this_bfd_opts->multiplier = $2; }
|
||||||
| PASSIVE bool { this_bfd_opts->passive = $2; this_bfd_opts->passive_set = 1; }
|
| PASSIVE bool { this_bfd_opts->passive = $2 ? BFD_OPT_PASSIVE : BFD_OPT_NOT_PASSIVE; }
|
||||||
| GRACEFUL { this_bfd_opts->mode = BGP_BFD_GRACEFUL; }
|
| GRACEFUL { this_bfd_opts->mode = BGP_BFD_GRACEFUL; }
|
||||||
| AUTHENTICATION bfd_auth_type { this_bfd_opts->auth_type = $2; }
|
| AUTHENTICATION bfd_auth_type { this_bfd_opts->auth_type = $2; }
|
||||||
| password_list {}
|
| password_list {}
|
||||||
|
@ -109,7 +109,7 @@ const u8 bfd_auth_type_to_hash_alg[] = {
|
|||||||
static void
|
static void
|
||||||
bfd_fill_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_ctl_packet *pkt)
|
bfd_fill_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_ctl_packet *pkt)
|
||||||
{
|
{
|
||||||
struct bfd_session_config *cf = &s->cf;
|
struct bfd_options *cf = &s->cf;
|
||||||
struct password_item *pass = password_find(cf->passwords, 0);
|
struct password_item *pass = password_find(cf->passwords, 0);
|
||||||
uint meticulous = 0;
|
uint meticulous = 0;
|
||||||
|
|
||||||
@ -179,7 +179,7 @@ bfd_fill_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_c
|
|||||||
static int
|
static int
|
||||||
bfd_check_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_ctl_packet *pkt)
|
bfd_check_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_ctl_packet *pkt)
|
||||||
{
|
{
|
||||||
struct bfd_session_config *cf = &s->cf;
|
struct bfd_options *cf = &s->cf;
|
||||||
const char *err_dsc = NULL;
|
const char *err_dsc = NULL;
|
||||||
uint err_val = 0;
|
uint err_val = 0;
|
||||||
uint auth_type = 0;
|
uint auth_type = 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user