diff --git a/conf/confbase.Y b/conf/confbase.Y index 8b22f236..f76dcb3c 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -19,6 +19,7 @@ CF_HDR #include "nest/protocol.h" #include "nest/iface.h" #include "nest/route.h" +#include "nest/bfd.h" #include "nest/cli.h" #include "filter/filter.h" @@ -78,6 +79,7 @@ CF_DECLS struct f_trie *trie; struct f_val v; struct password_item *p; + struct bfd_options *bo; struct rt_show_data *ra; struct sym_show_data *sd; struct lsadb_show_data *ld; diff --git a/nest/bfd.h b/nest/bfd.h index 36add991..37561266 100644 --- a/nest/bfd.h +++ b/nest/bfd.h @@ -9,9 +9,20 @@ #include "lib/lists.h" #include "lib/resource.h" +#include "conf/conf.h" 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 { resource r; node n; @@ -20,6 +31,7 @@ struct bfd_request { ip_addr local; struct iface *iface; struct iface *vrf; + struct bfd_options opts; void (*hook)(struct bfd_request *); void *data; @@ -32,6 +44,7 @@ struct bfd_request { u8 down; }; +#define BGP_BFD_GRACEFUL 2 /* BFD down triggers graceful restart */ #define BFD_STATE_ADMIN_DOWN 0 #define BFD_STATE_DOWN 1 @@ -39,15 +52,20 @@ struct bfd_request { #define BFD_STATE_UP 3 +static inline struct bfd_options * bfd_new_options(void) +{ return cfg_allocz(sizeof(struct bfd_options)); } + #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) { } #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"); } diff --git a/nest/config.Y b/nest/config.Y index f2f1df34..73556f15 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -25,6 +25,7 @@ static struct iface_patt_node *this_ipn; static list *this_p_list; static struct password_item *this_p_item; static int password_id; +static struct bfd_options *this_bfd_opts; static 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(TIMEFORMAT, ISO, SHORT, LONG, ROUTE, PROTOCOL, BASE, LOG, S, MS, US) CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS) +CF_KEYWORDS(MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE) CF_KEYWORDS(CHECK, LINK) /* For r_args_channel */ @@ -97,6 +99,7 @@ CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6) %type proto_patt proto_patt2 %type channel_start proto_channel %type limit_spec +%type bfd_opts %type r_args_for_val %type r_args_for %type r_args_channel @@ -497,6 +500,28 @@ password_algorithm: | 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 */ CF_CLI_HELP(SHOW, ..., [[Show status information]]) diff --git a/proto/bfd/bfd.c b/proto/bfd/bfd.c index e303d7a0..417263ef 100644 --- a/proto/bfd/bfd.c +++ b/proto/bfd/bfd.c @@ -128,6 +128,18 @@ static inline void bfd_notify_kick(struct bfd_proto *p); * 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 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); 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) - 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) bfd_notify_kick(p); @@ -405,7 +417,7 @@ bfd_get_free_id(struct bfd_proto *p) } 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); @@ -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_ip, HASH_IP, s); + s->cf = bfd_merge_options(ifa->cf, opts); /* 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 = ifa->cf->idle_tx_int; - s->req_min_rx_int = s->req_min_rx_new = ifa->cf->min_rx_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 = s->cf.min_rx_int; s->rem_min_rx_int = 1; - s->detect_mult = ifa->cf->multiplier; - s->passive = ifa->cf->passive; + s->detect_mult = s->cf.multiplier; + s->passive = s->cf.passive; s->tx_csn = random_u32(); 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 bfd_reconfigure_session(struct bfd_proto *p, struct bfd_session *s) { + if (EMPTY_LIST(s->request_list)) + return; + 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_rx(s, cf->min_rx_int); - s->detect_mult = cf->multiplier; - s->passive = cf->passive; + 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); @@ -639,7 +656,7 @@ bfd_add_request(struct bfd_proto *p, struct bfd_request *req) u8 state, diag; 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); add_tail(&s->request_list, &req->n); @@ -698,7 +715,8 @@ static struct resclass bfd_request_class; 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) + void (*hook)(struct bfd_request *), void *data, + const struct bfd_options *opts) { 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->vrf = vrf; + if (opts) + req->opts = *opts; + bfd_submit_request(req); req->hook = hook; @@ -718,6 +739,20 @@ bfd_request_session(pool *p, ip_addr addr, ip_addr local, 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 bfd_request_free(resource *r) { @@ -767,7 +802,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); + 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) @@ -784,7 +819,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); + n->req = bfd_request_session(p->p.pool, n->addr, n->local, NULL, p->p.vrf, NULL, NULL, NULL); return; } diff --git a/proto/bfd/bfd.h b/proto/bfd/bfd.h index 5c2054cc..83e2a991 100644 --- a/proto/bfd/bfd.h +++ b/proto/bfd/bfd.h @@ -61,6 +61,15 @@ struct bfd_iface_config 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 { node n; @@ -130,6 +139,9 @@ struct bfd_session u8 rem_diag; u32 loc_id; /* Local session ID (local 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_new; /* Used for des_min_tx_int change */ u32 req_min_rx_int; /* Required min tx interval, local option */ diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index b9ed6c78..b34dc325 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -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_sk(struct bgp_conn *conn, sock *s); 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 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_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 */ if (p->conn && (p->conn->state == BS_ESTABLISHED) && p->gr_ready) @@ -1380,14 +1380,17 @@ bgp_bfd_notify(struct bfd_request *req) } 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->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); p->bfd_req = NULL; @@ -2138,9 +2141,18 @@ bgp_channel_reconfigure(struct channel *C, struct channel_config *CC, int *impor } 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; + } } diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 455f04f9..5f365fcd 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -130,7 +130,7 @@ struct bgp_config { const char *dynamic_name; /* Name pattern for dynamic BGP */ int dynamic_name_digits; /* Minimum number of digits for dynamic names */ 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 { diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 7279560b..dc295645 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -190,8 +190,9 @@ bgp_proto: | bgp_proto LONG LIVED STALE TIME expr ';' { BGP_CFG->llgr_time = $6; } | bgp_proto TTL SECURITY bool ';' { BGP_CFG->ttl_security = $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 GRACEFUL ';' { BGP_CFG->bfd = BGP_BFD_GRACEFUL; cf_check_bfd(1); } + | bgp_proto BFD bool ';' { cf_check_bfd($3); BGP_CFG->bfd = $3 ? bfd_new_options() : NULL; } + | 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; } ; diff --git a/proto/ospf/neighbor.c b/proto/ospf/neighbor.c index 18692d6e..ca369819 100644 --- a/proto/ospf/neighbor.c +++ b/proto/ospf/neighbor.c @@ -777,7 +777,7 @@ ospf_neigh_update_bfd(struct ospf_neighbor *n, int use_bfd) if (use_bfd && !n->bfd_req) n->bfd_req = bfd_request_session(n->pool, n->ip, n->ifa->addr->ip, n->ifa->iface, p->p.vrf, - ospf_neigh_bfd_hook, n); + ospf_neigh_bfd_hook, n, NULL); if (!use_bfd && n->bfd_req) { diff --git a/proto/rip/rip.c b/proto/rip/rip.c index 5c53ab1e..8b4719f7 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -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; n->bfd_req = bfd_request_session(p->p.pool, n->nbr->addr, saddr, n->nbr->iface, p->p.vrf, - rip_bfd_notify, n); + rip_bfd_notify, n, NULL); } if (!use_bfd && n->bfd_req) diff --git a/proto/static/static.c b/proto/static/static.c index 72b14991..16a981ed 100644 --- a/proto/static/static.c +++ b/proto/static/static.c @@ -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; r->bfd_req = bfd_request_session(p->p.pool, r->via, nb->ifa->ip, nb->iface, p->p.vrf, - static_bfd_notify, r); + static_bfd_notify, r, NULL); } if (!bfd_up && r->bfd_req)