BFD: Allow per-request session options

BFD session options are configured per interface in BFD protocol. This
patch allows to specify them also per-request in protocols requesting
sessions (currently limited to BGP).
This commit is contained in:
Ondrej Zajicek (work) 2020-11-08 15:33:22 +01:00
parent fc1e3211b1
commit 9d3fc3062b
11 changed files with 137 additions and 32 deletions

View file

@ -19,6 +19,7 @@ CF_HDR
#include "nest/protocol.h" #include "nest/protocol.h"
#include "nest/iface.h" #include "nest/iface.h"
#include "nest/route.h" #include "nest/route.h"
#include "nest/bfd.h"
#include "nest/cli.h" #include "nest/cli.h"
#include "filter/filter.h" #include "filter/filter.h"
@ -78,6 +79,7 @@ CF_DECLS
struct f_trie *trie; struct f_trie *trie;
struct f_val v; struct f_val v;
struct password_item *p; struct password_item *p;
struct bfd_options *bo;
struct rt_show_data *ra; struct rt_show_data *ra;
struct sym_show_data *sd; struct sym_show_data *sd;
struct lsadb_show_data *ld; struct lsadb_show_data *ld;

View file

@ -9,9 +9,20 @@
#include "lib/lists.h" #include "lib/lists.h"
#include "lib/resource.h" #include "lib/resource.h"
#include "conf/conf.h"
struct bfd_session; struct bfd_session;
struct bfd_options {
u32 min_rx_int;
u32 min_tx_int;
u32 idle_tx_int;
u8 multiplier;
u8 passive;
u8 passive_set;
u8 mode;
};
struct bfd_request { struct bfd_request {
resource r; resource r;
node n; node n;
@ -20,6 +31,7 @@ struct bfd_request {
ip_addr local; ip_addr local;
struct iface *iface; struct iface *iface;
struct iface *vrf; struct iface *vrf;
struct bfd_options opts;
void (*hook)(struct bfd_request *); void (*hook)(struct bfd_request *);
void *data; void *data;
@ -32,6 +44,7 @@ struct bfd_request {
u8 down; u8 down;
}; };
#define BGP_BFD_GRACEFUL 2 /* BFD down triggers graceful restart */
#define BFD_STATE_ADMIN_DOWN 0 #define BFD_STATE_ADMIN_DOWN 0
#define BFD_STATE_DOWN 1 #define BFD_STATE_DOWN 1
@ -39,15 +52,20 @@ struct bfd_request {
#define BFD_STATE_UP 3 #define BFD_STATE_UP 3
static inline struct bfd_options * bfd_new_options(void)
{ return cfg_allocz(sizeof(struct bfd_options)); }
#ifdef CONFIG_BFD #ifdef CONFIG_BFD
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 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, const struct bfd_options *opts);
void bfd_update_request(struct bfd_request *req, const struct bfd_options *opts);
static inline void cf_check_bfd(int use UNUSED) { } static inline void cf_check_bfd(int use UNUSED) { }
#else #else
static inline struct bfd_request * bfd_request_session(pool *p UNUSED, ip_addr addr UNUSED, ip_addr local UNUSED, struct iface *iface UNUSED, struct iface *vrf UNUSED, void (*hook)(struct bfd_request *) UNUSED, void *data UNUSED) { return NULL; } static inline struct bfd_request * bfd_request_session(pool *p UNUSED, ip_addr addr UNUSED, ip_addr local UNUSED, struct iface *iface UNUSED, struct iface *vrf UNUSED, void (*hook)(struct bfd_request *) UNUSED, void *data UNUSED, const struct bfd_options *opts UNUSED) { return NULL; }
static inline void bfd_update_request(struct bfd_request *req UNUSED, const struct bfd_options *opts UNUSED) { };
static inline void cf_check_bfd(int use) { if (use) cf_error("BFD not available"); } static inline void cf_check_bfd(int use) { if (use) cf_error("BFD not available"); }

View file

@ -25,6 +25,7 @@ static struct iface_patt_node *this_ipn;
static list *this_p_list; static list *this_p_list;
static struct password_item *this_p_item; static struct password_item *this_p_item;
static int password_id; static int password_id;
static struct bfd_options *this_bfd_opts;
static void static void
iface_patt_check(void) iface_patt_check(void)
@ -75,6 +76,7 @@ CF_KEYWORDS(BGP, PASSWORDS, DESCRIPTION, SORTED)
CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP) CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP)
CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, ROUTE, PROTOCOL, BASE, LOG, S, MS, US) CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, ROUTE, PROTOCOL, BASE, LOG, S, MS, US)
CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS) CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
CF_KEYWORDS(MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE)
CF_KEYWORDS(CHECK, LINK) CF_KEYWORDS(CHECK, LINK)
/* For r_args_channel */ /* For r_args_channel */
@ -97,6 +99,7 @@ CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6)
%type <ps> proto_patt proto_patt2 %type <ps> proto_patt proto_patt2
%type <cc> channel_start proto_channel %type <cc> channel_start proto_channel
%type <cl> limit_spec %type <cl> limit_spec
%type <bo> bfd_opts
%type <net> r_args_for_val %type <net> r_args_for_val
%type <net_ptr> r_args_for %type <net_ptr> r_args_for
%type <t> r_args_channel %type <t> r_args_channel
@ -497,6 +500,28 @@ password_algorithm:
| HMAC SHA512 { $$ = ALG_HMAC_SHA512; } | HMAC SHA512 { $$ = ALG_HMAC_SHA512; }
; ;
/* BFD options */
bfd_item:
INTERVAL expr_us { this_bfd_opts->min_rx_int = this_bfd_opts->min_tx_int = $2; }
| MIN RX INTERVAL expr_us { this_bfd_opts->min_rx_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; }
| MULTIPLIER expr { this_bfd_opts->multiplier = $2; }
| PASSIVE bool { this_bfd_opts->passive = $2; this_bfd_opts->passive_set = 1; }
| GRACEFUL { this_bfd_opts->mode = BGP_BFD_GRACEFUL; }
;
bfd_items:
/* empty */
| bfd_items bfd_item ';'
;
bfd_opts:
'{' { this_bfd_opts = bfd_new_options(); } bfd_items '}' { $$ = this_bfd_opts; this_bfd_opts = NULL; }
;
/* Core commands */ /* Core commands */
CF_CLI_HELP(SHOW, ..., [[Show status information]]) CF_CLI_HELP(SHOW, ..., [[Show status information]])

View file

@ -128,6 +128,18 @@ static inline void bfd_notify_kick(struct bfd_proto *p);
* BFD sessions * BFD sessions
*/ */
static inline struct bfd_session_config
bfd_merge_options(const struct bfd_iface_config *cf, const struct bfd_options *opts)
{
return (struct bfd_session_config) {
.min_rx_int = opts->min_rx_int ?: cf->min_rx_int,
.min_tx_int = opts->min_tx_int ?: cf->min_tx_int,
.idle_tx_int = opts->idle_tx_int ?: cf->idle_tx_int,
.multiplier = opts->multiplier ?: cf->multiplier,
.passive = opts->passive_set ? opts->passive : cf->passive
};
}
static void static void
bfd_session_update_state(struct bfd_session *s, uint state, uint diag) bfd_session_update_state(struct bfd_session *s, uint state, uint diag)
{ {
@ -152,10 +164,10 @@ bfd_session_update_state(struct bfd_session *s, uint state, uint diag)
bfd_unlock_sessions(p); bfd_unlock_sessions(p);
if (state == BFD_STATE_UP) if (state == BFD_STATE_UP)
bfd_session_set_min_tx(s, s->ifa->cf->min_tx_int); bfd_session_set_min_tx(s, s->cf.min_tx_int);
if (old_state == BFD_STATE_UP) if (old_state == BFD_STATE_UP)
bfd_session_set_min_tx(s, s->ifa->cf->idle_tx_int); bfd_session_set_min_tx(s, s->cf.idle_tx_int);
if (notify) if (notify)
bfd_notify_kick(p); bfd_notify_kick(p);
@ -405,7 +417,7 @@ bfd_get_free_id(struct bfd_proto *p)
} }
static struct bfd_session * static struct bfd_session *
bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface *iface) bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface *iface, struct bfd_options *opts)
{ {
birdloop_enter(p->loop); birdloop_enter(p->loop);
@ -421,15 +433,16 @@ 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);
/* 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;
s->rem_state = BFD_STATE_DOWN; s->rem_state = BFD_STATE_DOWN;
s->des_min_tx_int = s->des_min_tx_new = ifa->cf->idle_tx_int; 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 = ifa->cf->min_rx_int; s->req_min_rx_int = s->req_min_rx_new = s->cf.min_rx_int;
s->rem_min_rx_int = 1; s->rem_min_rx_int = 1;
s->detect_mult = ifa->cf->multiplier; s->detect_mult = s->cf.multiplier;
s->passive = ifa->cf->passive; s->passive = s->cf.passive;
s->tx_csn = random_u32(); s->tx_csn = random_u32();
s->tx_timer = tm_new_init(p->tpool, bfd_tx_timer_hook, s, 0, 0); s->tx_timer = tm_new_init(p->tpool, bfd_tx_timer_hook, s, 0, 0);
@ -506,15 +519,19 @@ bfd_remove_session(struct bfd_proto *p, struct bfd_session *s)
static void static void
bfd_reconfigure_session(struct bfd_proto *p, struct bfd_session *s) bfd_reconfigure_session(struct bfd_proto *p, struct bfd_session *s)
{ {
if (EMPTY_LIST(s->request_list))
return;
birdloop_enter(p->loop); birdloop_enter(p->loop);
struct bfd_iface_config *cf = s->ifa->cf; 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 = (s->loc_state == BFD_STATE_UP) ? cf->min_tx_int : 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_tx(s, tx);
bfd_session_set_min_rx(s, cf->min_rx_int); bfd_session_set_min_rx(s, s->cf.min_rx_int);
s->detect_mult = cf->multiplier; s->detect_mult = s->cf.multiplier;
s->passive = cf->passive; s->passive = s->cf.passive;
bfd_session_control_tx_timer(s, 0); bfd_session_control_tx_timer(s, 0);
@ -639,7 +656,7 @@ bfd_add_request(struct bfd_proto *p, struct bfd_request *req)
u8 state, diag; u8 state, diag;
if (!s) if (!s)
s = bfd_add_session(p, req->addr, req->local, req->iface); s = bfd_add_session(p, req->addr, req->local, req->iface, &req->opts);
rem_node(&req->n); rem_node(&req->n);
add_tail(&s->request_list, &req->n); add_tail(&s->request_list, &req->n);
@ -698,7 +715,8 @@ static struct resclass bfd_request_class;
struct bfd_request * struct bfd_request *
bfd_request_session(pool *p, ip_addr addr, ip_addr local, bfd_request_session(pool *p, ip_addr addr, ip_addr local,
struct iface *iface, struct iface *vrf, struct iface *iface, struct iface *vrf,
void (*hook)(struct bfd_request *), void *data) void (*hook)(struct bfd_request *), void *data,
const struct bfd_options *opts)
{ {
struct bfd_request *req = ralloc(p, &bfd_request_class); struct bfd_request *req = ralloc(p, &bfd_request_class);
@ -710,6 +728,9 @@ bfd_request_session(pool *p, ip_addr addr, ip_addr local,
req->iface = iface; req->iface = iface;
req->vrf = vrf; req->vrf = vrf;
if (opts)
req->opts = *opts;
bfd_submit_request(req); bfd_submit_request(req);
req->hook = hook; req->hook = hook;
@ -718,6 +739,20 @@ bfd_request_session(pool *p, ip_addr addr, ip_addr local,
return req; return req;
} }
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;
req->opts = *opts;
if (s)
bfd_reconfigure_session(s->ifa->bfd, s);
}
static void static void
bfd_request_free(resource *r) bfd_request_free(resource *r)
{ {
@ -767,7 +802,7 @@ bfd_neigh_notify(struct neighbor *nb)
if ((nb->scope > 0) && !n->req) if ((nb->scope > 0) && !n->req)
{ {
ip_addr local = ipa_nonzero(n->local) ? n->local : nb->ifa->ip; 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); n->req = bfd_request_session(p->p.pool, n->addr, local, nb->iface, p->p.vrf, NULL, NULL, NULL);
} }
if ((nb->scope <= 0) && n->req) if ((nb->scope <= 0) && n->req)
@ -784,7 +819,7 @@ bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
if (n->multihop) if (n->multihop)
{ {
n->req = bfd_request_session(p->p.pool, n->addr, n->local, NULL, p->p.vrf, NULL, NULL); n->req = bfd_request_session(p->p.pool, n->addr, n->local, NULL, p->p.vrf, NULL, NULL, NULL);
return; return;
} }

View file

@ -61,6 +61,15 @@ struct bfd_iface_config
list *passwords; /* Passwords for authentication */ 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;
};
struct bfd_neighbor struct bfd_neighbor
{ {
node n; node n;
@ -130,6 +139,9 @@ struct bfd_session
u8 rem_diag; u8 rem_diag;
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 */
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 */
u32 req_min_rx_int; /* Required min tx interval, local option */ u32 req_min_rx_int; /* Required min tx interval, local option */

View file

@ -134,7 +134,7 @@ static void bgp_active(struct bgp_proto *p);
static void bgp_setup_conn(struct bgp_proto *p, struct bgp_conn *conn); static void bgp_setup_conn(struct bgp_proto *p, struct bgp_conn *conn);
static void bgp_setup_sk(struct bgp_conn *conn, sock *s); static void bgp_setup_sk(struct bgp_conn *conn, sock *s);
static void bgp_send_open(struct bgp_conn *conn); static void bgp_send_open(struct bgp_conn *conn);
static void bgp_update_bfd(struct bgp_proto *p, int use_bfd); static void bgp_update_bfd(struct bgp_proto *p, const struct bfd_options *bfd);
static int bgp_incoming_connection(sock *sk, uint dummy UNUSED); static int bgp_incoming_connection(sock *sk, uint dummy UNUSED);
static void bgp_listen_sock_err(sock *sk UNUSED, int err); static void bgp_listen_sock_err(sock *sk UNUSED, int err);
@ -1357,7 +1357,7 @@ bgp_bfd_notify(struct bfd_request *req)
BGP_TRACE(D_EVENTS, "BFD session down"); BGP_TRACE(D_EVENTS, "BFD session down");
bgp_store_error(p, NULL, BE_MISC, BEM_BFD_DOWN); bgp_store_error(p, NULL, BE_MISC, BEM_BFD_DOWN);
if (p->cf->bfd == BGP_BFD_GRACEFUL) if (req->opts.mode == BGP_BFD_GRACEFUL)
{ {
/* Trigger graceful restart */ /* Trigger graceful restart */
if (p->conn && (p->conn->state == BS_ESTABLISHED) && p->gr_ready) if (p->conn && (p->conn->state == BS_ESTABLISHED) && p->gr_ready)
@ -1380,14 +1380,17 @@ bgp_bfd_notify(struct bfd_request *req)
} }
static void static void
bgp_update_bfd(struct bgp_proto *p, int use_bfd) bgp_update_bfd(struct bgp_proto *p, const struct bfd_options *bfd)
{ {
if (use_bfd && !p->bfd_req && !bgp_is_dynamic(p)) if (bfd && p->bfd_req)
bfd_update_request(p->bfd_req, bfd);
if (bfd && !p->bfd_req && !bgp_is_dynamic(p))
p->bfd_req = bfd_request_session(p->p.pool, p->remote_ip, p->local_ip, p->bfd_req = bfd_request_session(p->p.pool, p->remote_ip, p->local_ip,
p->cf->multihop ? NULL : p->neigh->iface, p->cf->multihop ? NULL : p->neigh->iface,
p->p.vrf, bgp_bfd_notify, p); p->p.vrf, bgp_bfd_notify, p, bfd);
if (!use_bfd && p->bfd_req) if (!bfd && p->bfd_req)
{ {
rfree(p->bfd_req); rfree(p->bfd_req);
p->bfd_req = NULL; p->bfd_req = NULL;
@ -2138,9 +2141,18 @@ bgp_channel_reconfigure(struct channel *C, struct channel_config *CC, int *impor
} }
static void static void
bgp_copy_config(struct proto_config *dest UNUSED, struct proto_config *src UNUSED) bgp_copy_config(struct proto_config *dest, struct proto_config *src)
{ {
/* Just a shallow copy */ struct bgp_config *d = (void *) dest;
struct bgp_config *s = (void *) src;
/* Copy BFD options */
if (s->bfd)
{
struct bfd_options *opts = cfg_alloc(sizeof(struct bfd_options));
memcpy(opts, s->bfd, sizeof(struct bfd_options));
d->bfd = opts;
}
} }

View file

@ -130,7 +130,7 @@ struct bgp_config {
const char *dynamic_name; /* Name pattern for dynamic BGP */ const char *dynamic_name; /* Name pattern for dynamic BGP */
int dynamic_name_digits; /* Minimum number of digits for dynamic names */ int dynamic_name_digits; /* Minimum number of digits for dynamic names */
int check_link; /* Use iface link state for liveness detection */ int check_link; /* Use iface link state for liveness detection */
int bfd; /* Use BFD for liveness detection */ const struct bfd_options *bfd; /* Use BFD for liveness detection */
}; };
struct bgp_channel_config { struct bgp_channel_config {

View file

@ -190,8 +190,9 @@ bgp_proto:
| bgp_proto LONG LIVED STALE TIME expr ';' { BGP_CFG->llgr_time = $6; } | bgp_proto LONG LIVED STALE TIME expr ';' { BGP_CFG->llgr_time = $6; }
| bgp_proto TTL SECURITY bool ';' { BGP_CFG->ttl_security = $4; } | bgp_proto TTL SECURITY bool ';' { BGP_CFG->ttl_security = $4; }
| bgp_proto CHECK LINK bool ';' { BGP_CFG->check_link = $4; } | bgp_proto CHECK LINK bool ';' { BGP_CFG->check_link = $4; }
| bgp_proto BFD bool ';' { BGP_CFG->bfd = $3; cf_check_bfd($3); } | bgp_proto BFD bool ';' { cf_check_bfd($3); BGP_CFG->bfd = $3 ? bfd_new_options() : NULL; }
| bgp_proto BFD GRACEFUL ';' { BGP_CFG->bfd = BGP_BFD_GRACEFUL; cf_check_bfd(1); } | bgp_proto BFD GRACEFUL ';' { cf_check_bfd(1); struct bfd_options *opts = bfd_new_options(); opts->mode = BGP_BFD_GRACEFUL; BGP_CFG->bfd = opts; }
| bgp_proto BFD bfd_opts ';' { BGP_CFG->bfd = $3; cf_check_bfd(1); }
| bgp_proto ENFORCE FIRST AS bool ';' { BGP_CFG->enforce_first_as = $5; } | bgp_proto ENFORCE FIRST AS bool ';' { BGP_CFG->enforce_first_as = $5; }
; ;

View file

@ -777,7 +777,7 @@ ospf_neigh_update_bfd(struct ospf_neighbor *n, int use_bfd)
if (use_bfd && !n->bfd_req) if (use_bfd && !n->bfd_req)
n->bfd_req = bfd_request_session(n->pool, n->ip, n->ifa->addr->ip, n->bfd_req = bfd_request_session(n->pool, n->ip, n->ifa->addr->ip,
n->ifa->iface, p->p.vrf, n->ifa->iface, p->p.vrf,
ospf_neigh_bfd_hook, n); ospf_neigh_bfd_hook, n, NULL);
if (!use_bfd && n->bfd_req) if (!use_bfd && n->bfd_req)
{ {

View file

@ -499,7 +499,7 @@ rip_update_bfd(struct rip_proto *p, struct rip_neighbor *n)
ip_addr saddr = rip_is_v2(p) ? n->ifa->sk->saddr : n->nbr->ifa->ip; ip_addr saddr = rip_is_v2(p) ? n->ifa->sk->saddr : n->nbr->ifa->ip;
n->bfd_req = bfd_request_session(p->p.pool, n->nbr->addr, saddr, n->bfd_req = bfd_request_session(p->p.pool, n->nbr->addr, saddr,
n->nbr->iface, p->p.vrf, n->nbr->iface, p->p.vrf,
rip_bfd_notify, n); rip_bfd_notify, n, NULL);
} }
if (!use_bfd && n->bfd_req) if (!use_bfd && n->bfd_req)

View file

@ -161,7 +161,7 @@ static_update_bfd(struct static_proto *p, struct static_route *r)
// ip_addr local = ipa_nonzero(r->local) ? r->local : nb->ifa->ip; // ip_addr local = ipa_nonzero(r->local) ? r->local : nb->ifa->ip;
r->bfd_req = bfd_request_session(p->p.pool, r->via, nb->ifa->ip, r->bfd_req = bfd_request_session(p->p.pool, r->via, nb->ifa->ip,
nb->iface, p->p.vrf, nb->iface, p->p.vrf,
static_bfd_notify, r); static_bfd_notify, r, NULL);
} }
if (!bfd_up && r->bfd_req) if (!bfd_up && r->bfd_req)