[PATCH][RFC] BFD: Labeling a manually configured neighbor for tracking

Miao Wang shankerwangmiao at gmail.com
Thu Mar 30 00:28:15 CEST 2023


For BFD controlled Static routes, the BFD sessions are created
to the route next hop. However, in some scenarios, e.g. when the
next hop itself does not support BFD, or when it might be more
meaningful to create the session to a remote monitoring station,
we need more flexible relationship between a static route nexthop
and its controlling BFD session.

In this patch, a numeric label value can be specified with manually
configured BFD neighbors, and a static route nexthop can be
configured to track a certain BFD session using that label. The
detailed behavior is updated in the user's guide.

This feature may also be added to other protocols, but it seems not
so meaningful.
---
 doc/bird.sgml         | 23 ++++++++++++++---
 nest/bfd.h            |  5 ++--
 nest/config.Y         | 11 +++++++-
 proto/bfd/bfd.c       | 59 ++++++++++++++++++++++++++++++++++++++-----
 proto/bfd/bfd.h       |  1 +
 proto/bfd/config.Y    | 22 +++++++++++++---
 proto/bgp/bgp.c       |  2 +-
 proto/ospf/neighbor.c |  2 +-
 proto/rip/rip.c       |  2 +-
 proto/static/config.Y |  8 ++++--
 proto/static/static.c |  6 +++--
 proto/static/static.h |  1 +
 12 files changed, 120 insertions(+), 22 deletions(-)

diff --git a/doc/bird.sgml b/doc/bird.sgml
index 5ac32e9e..80aaa24f 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -2198,7 +2198,7 @@ protocol bfd [<name>] {
 		multiplier <num>;
 		passive <switch>;
 	};
-	neighbor <ip> [dev "<interface>"] [local <ip>] [multihop <switch>];
+	neighbor <ip> [dev "<interface>"] [local <ip>] [multihop <switch>] [label <num>];
 }
 </code>
 
@@ -2233,7 +2233,7 @@ protocol bfd [<name>] {
 	connected sessions. Currently only one such definition (for all multihop
 	sessions) could be used.
 
-	<tag><label id="bfd-neighbor">neighbor <m/ip/ [dev "<m/interface/"] [local <m/ip/] [multihop <m/switch/]</tag>
+	<tag><label id="bfd-neighbor">neighbor <m/ip/ [dev "<m/interface/"] [local <m/ip/] [multihop <m/switch/] [label <m/num/]</tag>
 	BFD sessions are usually created on demand as requested by other
 	protocols (like OSPF or BGP). This option allows to explicitly add
 	a BFD session to the specified neighbor regardless of such requests.
@@ -2242,7 +2242,11 @@ protocol bfd [<name>] {
 	optional specification of used interface and local IP. By default
 	the neighbor must be directly connected, unless the session is
 	configured as multihop. Note that local IP must be specified for
-	multihop sessions.
+	multihop sessions. If the optional <cf/label/ is specified, it is
+	used to identify the session in the BFD protocol instance and can
+	be later tracked explicitly by other protocols. The choice of the
+	label is up to the user other than zero, but it should be unique
+	within all BFD protocol instances.
 </descrip>
 
 <p>Session specific options (part of <cf/interface/ and <cf/multihop/ definitions):
@@ -2338,6 +2342,7 @@ protocol bfd {
 	neighbor 192.168.1.10;
 	neighbor 192.168.2.2 dev "eth2";
 	neighbor 192.168.10.1 local 192.168.1.1 multihop;
+	neighbor 192.168.10.2 local 192.168.1.1 multihop label 10;
 }
 </code>
 
@@ -5314,6 +5319,17 @@ options (<cf/bfd/ and <cf/weight 1/), the second nexthop has just <cf/weight 2/.
 	that BFD protocol also has to be configured, see <ref id="bfd" name="BFD">
 	section for details. Default value is no.
 
+	<tag><label id="static-route-bfd-track">bfd track <m/num/</tag>
+	Like <cf/bfd/ option, but the BFD session is not created by the Static
+	protocol, but it is expected to be already created by one of the neighbors
+	labeled by the given number in the <cf/neighbor/ statement of the
+	<ref id="bfd" name="BFD"> protocol. If such a BFD session is not configured,
+	the route is not installed until a matching session is created later by a
+	config update and established. It is useful when the next hop liveness
+	is confirmed with another BFD (maybe multihop) peer other than the next hop
+	itself. It is not possible to use both <cf/bfd/ and <cf/bfd track/ options
+	for the same nexthop. Default value is not to track any BFD session.
+
 	<tag><label id="static-route-mpls">mpls <m/num/[/<m/num/[/<m/num/[...]]]</tag>
 	MPLS labels that should be pushed to packets forwarded by the route.
 	The option could be used for both IP routes (on MPLS ingress routers)
@@ -5516,6 +5532,7 @@ protocol static {
 	route 2001:db8:40::/48 via "eth2";		# Direct route to eth2
 	route 2001:db8::/32 unreachable;		# Unreachable route
 	route ::/0 via 2001:db8:1::1 bfd;		# BFD-controlled default route
+	route 2001:db8:50::/48 via fe80::1%'eth1.70' bfd track 10;		# BFD-controlled default route, track bfd session labeled 10
 }
 </code>
 
diff --git a/nest/bfd.h b/nest/bfd.h
index 37561266..f5059c17 100644
--- a/nest/bfd.h
+++ b/nest/bfd.h
@@ -42,6 +42,7 @@ struct bfd_request {
   u8 diag;
   u8 old_state;
   u8 down;
+  u32 label;
 };
 
 #define BGP_BFD_GRACEFUL	2	/* BFD down triggers graceful restart */
@@ -57,14 +58,14 @@ static inline struct bfd_options * bfd_new_options(void)
 
 #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, const struct bfd_options *opts);
+struct bfd_request * bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface, struct iface *vrf, u32 label, 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, const struct bfd_options *opts 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, u32 label, 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 e78350ca..dea898d1 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -143,7 +143,7 @@ CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6)
 %type <s> optproto
 %type <ra> r_args
 %type <sd> sym_args
-%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_mode limit_action net_type tos password_algorithm
+%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_mode limit_action net_type tos password_algorithm bfd_neigh_label
 %type <ps> proto_patt proto_patt2
 %type <cc> channel_start proto_channel
 %type <cl> limit_spec
@@ -605,6 +605,15 @@ bfd_opts:
    '{' bfd_items '}'
  ;
 
+bfd_neigh_label:
+ expr {
+  if ($1 == 0){
+    cf_error("BFD label cannot be zero");
+  }
+  $$ = $1;
+ }
+ ;
+
 /* Core commands */
 CF_CLI_HELP(SHOW, ..., [[Show status information]])
 
diff --git a/proto/bfd/bfd.c b/proto/bfd/bfd.c
index 873e2ed5..bec5f1da 100644
--- a/proto/bfd/bfd.c
+++ b/proto/bfd/bfd.c
@@ -659,6 +659,23 @@ bfd_add_request(struct bfd_proto *p, struct bfd_request *req)
 {
   struct bfd_config *cf = (struct bfd_config *) (p->p.cf);
 
+  struct bfd_request *ref_req = NULL;
+  if (req->label != 0 && ipa_zero(req->addr)){
+    struct bfd_neighbor *nei;
+    WALK_LIST(nei, cf->neigh_list){
+      if (nei->label == req->label){
+        ref_req = nei->req;
+        break;
+      }
+    }
+    if (ref_req == NULL){
+      return 0;
+    }
+  }
+  struct bfd_request *actual_req = req;
+  if (ref_req != NULL)
+    req = ref_req;
+
   if (p->p.vrf_set && (p->p.vrf != req->vrf))
     return 0;
 
@@ -675,6 +692,8 @@ bfd_add_request(struct bfd_proto *p, struct bfd_request *req)
   if (!s)
     s = bfd_add_session(p, req->addr, req->local, req->iface, &req->opts);
 
+  req = actual_req;
+
   rem_node(&req->n);
   add_tail(&s->request_list, &req->n);
   req->session = s;
@@ -731,7 +750,7 @@ 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,
+		    struct iface *iface, struct iface *vrf, u32 label,
 		    void (*hook)(struct bfd_request *), void *data,
 		    const struct bfd_options *opts)
 {
@@ -744,6 +763,7 @@ bfd_request_session(pool *p, ip_addr addr, ip_addr local,
   req->local = local;
   req->iface = iface;
   req->vrf = vrf;
+  req->label = label;
 
   if (opts)
     req->opts = *opts;
@@ -771,9 +791,8 @@ bfd_update_request(struct bfd_request *req, const struct bfd_options *opts)
 }
 
 static void
-bfd_request_free(resource *r)
+bfd_request_detach_from_session(struct bfd_request *req)
 {
-  struct bfd_request *req = (struct bfd_request *) r;
   struct bfd_session *s = req->session;
 
   rem_node(&req->n);
@@ -785,6 +804,13 @@ bfd_request_free(resource *r)
     bfd_remove_session(s->ifa->bfd, s);
 }
 
+static void
+bfd_request_free(resource *r)
+{
+  struct bfd_request *req = (struct bfd_request *) r;
+  return bfd_request_detach_from_session(req);
+}
+
 static void
 bfd_request_dump(resource *r)
 {
@@ -819,7 +845,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, NULL);
+    n->req = bfd_request_session(p->p.pool, n->addr, local, nb->iface, p->p.vrf, n->label, NULL, NULL, NULL);
   }
 
   if ((nb->scope <= 0) && n->req)
@@ -836,7 +862,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, NULL);
+    n->req = bfd_request_session(p->p.pool, n->addr, n->local, NULL, p->p.vrf, n->label, NULL, NULL, NULL);
     return;
   }
 
@@ -868,16 +894,37 @@ bfd_stop_neighbor(struct bfd_proto *p UNUSED, struct bfd_neighbor *n)
   if (n->neigh)
     n->neigh->data = NULL;
   n->neigh = NULL;
+  list tmp_list;
+  init_list(&tmp_list);
+  if (n->label != 0){
+    if (n->req && n->req->session){
+      struct bfd_session *s = n->req->session;
+      struct node *nd, *ndn;
+      WALK_LIST_DELSAFE(nd, ndn, s->request_list){
+        struct bfd_request *req = SKIP_BACK(struct bfd_request, n, nd);
+        if (req->label == n->label){
+          bfd_request_detach_from_session(req);
+          add_tail(&tmp_list, &req->n);
+        }
+      }
+    }
+  }
 
   rfree(n->req);
   n->req = NULL;
+
+  struct node *nd;
+  WALK_LIST_FIRST(nd, tmp_list){
+    struct bfd_request *req = SKIP_BACK(struct bfd_request, n, nd);
+    bfd_submit_request(req);
+  }
 }
 
 static inline int
 bfd_same_neighbor(struct bfd_neighbor *x, struct bfd_neighbor *y)
 {
   return ipa_equal(x->addr, y->addr) && ipa_equal(x->local, y->local) &&
-    (x->iface == y->iface) && (x->multihop == y->multihop);
+    (x->iface == y->iface) && (x->multihop == y->multihop) && (x->label == y->label);
 }
 
 static void
diff --git a/proto/bfd/bfd.h b/proto/bfd/bfd.h
index 7caf9f66..405ca192 100644
--- a/proto/bfd/bfd.h
+++ b/proto/bfd/bfd.h
@@ -83,6 +83,7 @@ struct bfd_neighbor
 
   u8 multihop;
   u8 active;
+  u32 label;
 };
 
 struct bfd_proto
diff --git a/proto/bfd/config.Y b/proto/bfd/config.Y
index 70461872..e478fc3a 100644
--- a/proto/bfd/config.Y
+++ b/proto/bfd/config.Y
@@ -24,11 +24,11 @@ CF_DECLS
 CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE,
 	INTERFACE, MULTIHOP, NEIGHBOR, DEV, LOCAL, AUTHENTICATION,
 	NONE, SIMPLE, METICULOUS, KEYED, MD5, SHA1, IPV4, IPV6, DIRECT,
-	STRICT, BIND)
+	STRICT, BIND, LABEL)
 
 %type <iface> bfd_neigh_iface
 %type <a> bfd_neigh_local
-%type <i> bfd_neigh_multihop bfd_auth_type
+%type <i> bfd_neigh_multihop bfd_auth_type bfd_neigh_option_label
 
 CF_GRAMMAR
 
@@ -164,7 +164,14 @@ bfd_neigh_multihop:
  | MULTIHOP bool { $$ = $2; }
  ;
 
-bfd_neighbor: ipa bfd_neigh_iface bfd_neigh_local bfd_neigh_multihop
+bfd_neigh_option_label:
+   /* empty */ { $$ = 0; }
+ | LABEL bfd_neigh_label {
+  $$ = $2;
+ }
+ ;
+
+bfd_neighbor: ipa bfd_neigh_iface bfd_neigh_local bfd_neigh_multihop bfd_neigh_option_label
 {
   this_bfd_neighbor = cfg_allocz(sizeof(struct bfd_neighbor));
   add_tail(&BFD_CFG->neigh_list, NODE this_bfd_neighbor);
@@ -173,12 +180,21 @@ bfd_neighbor: ipa bfd_neigh_iface bfd_neigh_local bfd_neigh_multihop
   BFD_NEIGHBOR->local = $3;
   BFD_NEIGHBOR->iface = $2;
   BFD_NEIGHBOR->multihop = $4;
+  BFD_NEIGHBOR->label = $5;
 
   if ($4 && $2)
     cf_error("Neighbor cannot set both interface and multihop");
 
   if ($4 && ipa_zero($3))
     cf_error("Multihop neighbor requires specified local address");
+
+  if ($5){
+    struct bfd_neighbor *other_nei;
+    WALK_LIST(other_nei, BFD_CFG->neigh_list){
+      if (other_nei != this_bfd_neighbor && other_nei->label == $5)
+        cf_error("Neighbor label %d already used", $5);
+    }
+  }
 };
 
 
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index 9408715e..a3e6becf 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -1380,7 +1380,7 @@ bgp_update_bfd(struct bgp_proto *p, const struct bfd_options *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, bfd);
+				     p->p.vrf, 0, bgp_bfd_notify, p, bfd);
 
   if (!bfd && p->bfd_req)
   {
diff --git a/proto/ospf/neighbor.c b/proto/ospf/neighbor.c
index ca369819..a59cef32 100644
--- a/proto/ospf/neighbor.c
+++ b/proto/ospf/neighbor.c
@@ -776,7 +776,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,
+				     n->ifa->iface, p->p.vrf, 0,
 				     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 8c2d5aeb..6c559ce8 100644
--- a/proto/rip/rip.c
+++ b/proto/rip/rip.c
@@ -531,7 +531,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,
+				     n->nbr->iface, p->p.vrf, 0,
 				     rip_bfd_notify, n, NULL);
   }
 
diff --git a/proto/static/config.Y b/proto/static/config.Y
index 9d26ee82..6fa47338 100644
--- a/proto/static/config.Y
+++ b/proto/static/config.Y
@@ -46,8 +46,7 @@ static_route_finish(void)
 CF_DECLS
 
 CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK)
-CF_KEYWORDS(ONLINK, WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE, BFD, MPLS)
-
+CF_KEYWORDS(ONLINK, WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE, BFD, MPLS, TRACK)
 
 CF_GRAMMAR
 
@@ -98,6 +97,11 @@ stat_nexthop:
   }
   | stat_nexthop BFD bool {
     this_snh->use_bfd = $3; cf_check_bfd($3);
+    this_snh->bfd_label = 0;
+  }
+  | stat_nexthop BFD TRACK bfd_neigh_label {
+    this_snh->use_bfd = 1; cf_check_bfd(1);
+    this_snh->bfd_label = $4;
   }
 ;
 
diff --git a/proto/static/static.c b/proto/static/static.c
index f2bb98c7..47f7fa67 100644
--- a/proto/static/static.c
+++ b/proto/static/static.c
@@ -206,9 +206,10 @@ static_update_bfd(struct static_proto *p, struct static_route *r)
   if (bfd_up && !r->bfd_req)
   {
     // ip_addr local = ipa_nonzero(r->local) ? r->local : nb->ifa->ip;
-    r->bfd_req = bfd_request_session(p->p.pool, r->via,
+    r->bfd_req = bfd_request_session(p->p.pool,
+				     r->bfd_label != 0 ? IPA_NONE : r->via,
 				     nb->ifa ? nb->ifa->ip : IPA_NONE,
-				     nb->iface, p->p.vrf,
+				     nb->iface, p->p.vrf, r->bfd_label,
 				     static_bfd_notify, r, NULL);
   }
 
@@ -320,6 +321,7 @@ static_same_dest(struct static_route *x, struct static_route *y)
 	  (x->onlink != y->onlink) ||
 	  (x->weight != y->weight) ||
 	  (x->use_bfd != y->use_bfd) ||
+    (x->bfd_label != y->bfd_label) ||
 	  (!x->mls != !y->mls) ||
 	  ((x->mls) && (y->mls) && (x->mls->len != y->mls->len)))
 	return 0;
diff --git a/proto/static/static.h b/proto/static/static.h
index fc91f71c..22010d86 100644
--- a/proto/static/static.h
+++ b/proto/static/static.h
@@ -50,6 +50,7 @@ struct static_route {
   byte use_bfd;				/* Configured to use BFD */
   struct bfd_request *bfd_req;		/* BFD request, if BFD is used */
   mpls_label_stack *mls;		/* MPLS label stack; may be NULL */
+  u32 bfd_label;      /* Label for referred BFD session */
 };
 
 /*
-- 
2.39.0





More information about the Bird-users mailing list