]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
BFD: Allow per-request session options
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Sun, 8 Nov 2020 14:33:22 +0000 (15:33 +0100)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Sun, 8 Nov 2020 14:33:22 +0000 (15:33 +0100)
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).

conf/confbase.Y
nest/bfd.h
nest/config.Y
proto/bfd/bfd.c
proto/bfd/bfd.h
proto/bgp/bgp.c
proto/bgp/bgp.h
proto/bgp/config.Y
proto/ospf/neighbor.c
proto/rip/rip.c
proto/static/static.c

index 8b22f23673dc8a9d44ecce117d51575dafe92622..f76dcb3c1aefc01325b3e57f961d53af834d584c 100644 (file)
@@ -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;
index 36add991fd5a662478aa638922213968dcb8f20e..37561266e014f6dfb2f4508a0fc4e5ca612fd813 100644 (file)
@@ -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"); }
 
index f2f1df34cd15d11635445665379fe8f20d58e74f..73556f15335e68a08f2b2bc03057fa519597ec1d 100644 (file)
@@ -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 <ps> proto_patt proto_patt2
 %type <cc> channel_start proto_channel
 %type <cl> limit_spec
+%type <bo> bfd_opts
 %type <net> r_args_for_val
 %type <net_ptr> r_args_for
 %type <t> 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]])
 
index e303d7a0d50ebbca1bd1c4a784e601aef5e05136..417263ef6159bbc12407d1279675e0ecf77eb539 100644 (file)
@@ -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;
   }
 
index 5c2054cc39343ebdecbe0cca0bcc2c8d6b0a629d..83e2a9910ba4b0934d182206474ef8929f1e9a54 100644 (file)
@@ -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 */
index b9ed6c7894ca5ba627f024006b7dfdc4666b7027..b34dc325f3b04deafc0b8bab4ad3a20c84cc3b76 100644 (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_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;
+  }
 }
 
 
index 455f04f9ed9d763843ef819193d292a01516aa85..5f365fcd249b7f7aec1bbf77f344b33bb49b3a64 100644 (file)
@@ -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 {
index 7279560bf4f91753c71c0e9cb352570de892c47e..dc2956451e129de6f9047ef233dc8d14647c6b11 100644 (file)
@@ -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; }
  ;
 
index 18692d6eb24941348d280be7bd5dbc19a744097a..ca369819b7792a6f8c3e7dba52b11a701d08a0b5 100644 (file)
@@ -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)
   {
index 5c53ab1e0cd01b8d241109c98c170a4903e7cf2d..8b4719f73ff995849c74cf3a21207cca5d0943f7 100644 (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;
     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)
index 72b14991eee5936863b35c30321e19c18e328cc6..16a981ed56de7a610d6a819e8ebde7512223578a 100644 (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;
     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)